Помогите со сдвигом байтов.
-
22-08-2019 - |
Вопрос
Мне нужно сдвинуть текстовый файл.Я абсолютно ничего не знаю о Perl, но нашел в Perl прекрасно работающий фрагмент кода под названием moz-byteshift.pl (документация).Это именно то, что я хочу, но мне нужно сделать это на C#.
Вот исходный код файла perl:
#!/usr/bin/perl
# To perform a byteshift of 7
# To decode: moz-byteshift.pl -s -7 <infile >outfile
# To encode: moz-byteshift.pl -s 7 <infile >outfile
# To perform a byteshift of 13
# To decode: moz-byteshift.pl -s -13 <infile >outfile
# To encode: moz-byteshift.pl -s 13 <infile >outfile
use encoding 'latin1';
use strict;
use Getopt::Std;
use vars qw/$opt_s/;
getopts("s:");
if(!defined $opt_s) {
die "Missing shift\n";
}
my $buffer;
while(1) {
binmode(STDIN, ":raw");
my $n=sysread STDIN, $buffer, 1;
if($n == 0) {
last;
}
my $byte = unpack("c", $buffer);
$byte += 512 + $opt_s;
$buffer = pack("c", $byte);
binmode(STDOUT, ":raw");
syswrite STDOUT, $buffer, 1;
}
Если бы кто-нибудь мог хотя бы объяснить, как работает Perl-скрипт, было бы здорово.Пример кода эквивалента на C# был бы лучше."="
Спасибо за помощь.
Решение
Код делает следующее:Считайте каждый байт из стандартного ввода один за другим (после переключения его в необработанный режим, чтобы не происходило перевода).При распаковке получается значение байта только что прочитанного символа, так что чтение «0» превращается в 0x30.Кодировка Latin1 выбирается таким образом, чтобы это преобразование было последовательным (например,видеть http://www.cs.tut.fi/~jkorpela/latin9.html).
Затем к этому байту добавляется значение, указанное в командной строке с опцией -s, вместе с 512 для имитации операции вычисления модуля.Таким образом, -s 0, -s 256 и т. д. эквивалентны.Я не уверен, зачем это нужно, потому что я предполагал, что следующий пакет позаботится об этом, но я думаю, что у них, должно быть, были веские причины поместить его туда.
Затем запишите необработанный байт на стандартный ввод.
Вот что происходит, когда вы запускаете его с файлом, содержащим символы 012345 (я помещаю данные в ДАННЫЕ раздел):
E:\Test> byteshift.pl -s 1 | xxd
0000000: 3132 3334 3536 0b 123456.
Каждое значение байта увеличивается на единицу.
E:\Test> byteshift.pl -s 257 | xxd
0000000: 3132 3334 3536 0b 123456.
Помните: 257 % 256 = 1.То есть:
$byte += $opt_s;
$byte %= 256;
эквивалентно одному шагу, используемому в коде.
Гораздо позже:Хорошо, я не знаю C#, но вот что мне удалось собрать воедино с помощью онлайн-документации.Кто-то, кто знает C#, должен это исправить:
using System;
using System.IO;
class BinaryRW {
static void Main(string[] args) {
BinaryWriter binWriter = new BinaryWriter(
Console.OpenStandardOutput()
);
BinaryReader binReader = new BinaryReader(
Console.OpenStandardInput()
);
int delta;
if ( args.Length < 1
|| ! int.TryParse( args[0], out delta ) )
{
Console.WriteLine(
"Provide a non-negative delta on the command line"
);
}
else {
try {
while ( true ) {
int bin = binReader.ReadByte();
byte bout = (byte) ( ( bin + delta ) % 256 );
binWriter.Write( bout );
}
}
catch(EndOfStreamException) { }
catch(ObjectDisposedException) { }
catch(IOException e) {
Console.WriteLine( e );
}
finally {
binWriter.Close();
binReader.Close();
}
}
}
}
E:\Test> xxd bin
0000000: 3031 3233 3435 0d0a 0d0a 012345....
E:\Test> b 0 < bin | xxd
0000000: 3031 3233 3435 0d0a 0d0a 012345....
E:\Test> b 32 < bin | xxd
0000000: 5051 5253 5455 2d2a 2d2a PQRSTU-*-*
E:\Test> b 257 < bin | xxd
0000000: 3132 3334 3536 0e0b 0e0b 123456....
Другие советы
Рассказывать особо нечего.Он считывает файл по одному байту за раз, корректирует значение каждого байта на произвольное значение (указанное с помощью флага -s) и записывает скорректированные байты.Это двоичный эквивалент шифрования текстового файла ROT-13.
Остальные детали относятся к тому, как Perl делает эти вещи.getopts() — это функция (из модуля Getopt::Std), которая обрабатывает переключатели командной строки.binmode() переводит дескрипторы файлов в необработанный режим, чтобы обойти любую магию, которую обычно выполняет Perl во время ввода-вывода.Функции sysread() и syswrite() используются для доступа к потоку низкого уровня.Функции package() и unpack() используются для чтения и записи двоичных данных;Perl не поддерживает собственные типы.
Это было бы тривиально для повторной реализации в C.Я бы рекомендовал сделать это (и привязать к нему C#, если это необходимо), а не напрямую портировать на C#.
Судя по другим ответам, эквивалент на С# будет выглядеть примерно так:
using(Stream sIn = new FileStream(inPath))
{
using(Stream sOut = new FileStream(outPath))
{
int b = sIn.ReadByte();
while(b >= 0)
{
b = (byte)b+1; // or some other value
sOut.WriteByte((byte)b);
b = sIn.ReadByte();
}
sOut.Close();
}
sIn.Close();
}