Мне нужно переписать текстовый файл в байтах. Я абсолютно ничего не знаю о perl, но я нашел отлично работающий фрагмент кода в perl, называемый moz-byteshift.pl (документация). Это делает именно то, что я хочу сделать, но мне нужно сделать это на С#.
Здесь исходный код файла 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 script, это было бы здорово. Пример кода эквивалента в С# будет лучше. =)
Спасибо за помощь.
Что делает этот код: Прочитайте каждый байт со стандартного ввода один за другим (после его переключения в необработанный режим, чтобы перевод не выполнялся). Распаковка получает значение байта только что прочитанного символа, так что чтение "0" превращается в 0x30. Кодировка latin1 выбирается так, чтобы это преобразование было согласованным (например, см. http://www.cs.tut.fi/~jkorpela/latin9.html).
Затем значение, указанное в командной строке с опцией -s, добавляется к этому байту вместе с 512 для имитации операции модуля. Таким образом, -s 0, -s 256 и т.д. Эквивалентны. Я не уверен, почему это необходимо, потому что я предполагал, что следующий пакет позаботился об этом, но я думаю, что у них, должно быть, были веские причины, чтобы поместить его туда.
Затем напишите исходный байт на стандартный ввод.
Вот что происходит, когда вы запускаете его в файле, содержащем символы 012345 (я помещаю данные в раздел DATA):
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;
эквивалентен одному шагу, используемому в коде.
Много позже: ОК, я не знаю С#, но вот что я смог собрать вместе, используя онлайн-документацию. Кто-то, кто знает С#, должен исправить это:
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....
args.Length < 1 ||
в начало вашего условия if, чтобы исключить исключение «индекс вне границ», когда ничего не введено.
Там нечего сказать. Он считывает файл по одному байту за раз, настраивает значение каждого байта на произвольное значение (задается с помощью флага -s) и записывает отрегулированные байты. Это двоичный эквивалент шифрования ROT-13 текстового файла.
Остальная часть деталей зависит от того, как Perl делает эти вещи. getopts() - это функция (из модуля Getopt:: Std), которая обрабатывает ключи командной строки. binmode() помещает дескрипторы файлов в режим raw, чтобы обойти любую магию, которую Perl обычно делает во время ввода-вывода. Функции sysread() и syswrite() используются для доступа к потоку низкого уровня. Функции pack() и unpack() используются для чтения и записи двоичных данных; Perl не использует собственные типы.
Это было бы тривиально повторить в 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();
}