Я пытаюсь записывать в файлы строго двоичные данные (без кодировки). Проблема в том, что при шестнадцатеричном дампе файлов я замечаю довольно странное поведение. Использование любого из следующих методов для создания файла приводит к тому же поведению. Я даже использовал System :: Text :: Encoding :: Default для тестирования потоков.

StreamWriter^ binWriter = gcnew StreamWriter(gcnew FileStream("test.bin",FileMode::Create));

(Also used this method)
FileStream^ tempBin = gcnew FileStream("test.bin",FileMode::Create);
BinaryWriter^ binWriter = gcnew BinaryWriter(tempBin);


binWriter->Write(0x80);
binWriter->Write(0x81);
.
.
binWriter->Write(0x8F);
binWriter->Write(0x90);
binWriter->Write(0x91);
.
.
binWriter->Write(0x9F);

Записывая эту последовательность байтов, я заметил, что единственными байтами, которые не преобразованы в 0x3F в шестнадцатеричном дампе, были 0x81,0x8D, 0x90,0x9D ... и я не знаю почему.

Я также пробовал создавать символьные массивы, и происходит аналогичная ситуация. т.е.

array<wchar_t,1>^ OT_Random_Delta_Limits = {0x00,0x00,0x03,0x79,0x00,0x00,0x04,0x88};
binWriter->Write(OT_Random_Delta_Limits);

0x88 будет записан как 0x3F.

Любые идеи?

0
B L 5 Ноя 2009 в 00:23
Кроме того, я не учел тот факт, что влияет ТОЛЬКО последовательность байтов 0x8 и 0x9. Например, 0xF9 или 0xC3 пишут так, как должны. 0x3F - это ASCII для? если кому было интересно.
 – 
B L
5 Ноя 2009 в 00:27

3 ответа

Лучший ответ

Если вы хотите использовать двоичные файлы, не используйте StreamWriter . Просто используйте FileStream и Write / WriteByte. StreamWriters (и TextWriters в целом) специально разработаны для текста . Независимо от того, хотите вы кодировку или нет, она будет применяться, потому что, когда вы вызываете StreamWriter.Write, она записывает char, а не byte.

Не создавайте массивы значений wchar_t - опять же, они предназначены для символов , то есть текста.

BinaryWriter.Write должен был сработать для вас, если только он не продвигал значения в char, и в этом случае у вас возникла бы точно такая же проблема.

Между прочим, без указания любой кодировки я ожидаю, что вы получите значения, отличные от 0x3F, а вместо этого получите байты, представляющие значения в кодировке UTF-8 для этих символов.

Если вы укажете Encoding.Default, вы увидите 0x3F для любых значений Unicode, не входящих в эту кодировку.

В любом случае, основной урок - придерживаться Stream, когда вы хотите иметь дело с двоичными данными, а не с текстом.

РЕДАКТИРОВАТЬ: Хорошо, это будет что-то вроде:

public static void ConvertHex(TextReader input, Stream output)
{
    while (true)
    {
        int firstNybble = input.Read();
        if (firstNybble == -1)
        {
            return;
        }
        int secondNybble = input.Read();
        if (secondNybble == -1)
        {
            throw new IOException("Reader finished half way through a byte");
        }
        int value = (ParseNybble(firstNybble) << 4) + ParseNybble(secondNybble);
        output.WriteByte((byte) value);
    }
}

// value would actually be a char, but as we've got an int in the above code,
// it just makes things a bit easier
private static int ParseNybble(int value)
{
    if (value >= '0' && value <= '9') return value - '0';
    if (value >= 'A' && value <= 'F') return value - 'A' + 10;
    if (value >= 'a' && value <= 'f') return value - 'a' + 10;
    throw new ArgumentException("Invalid nybble: " + (char) value);
}

Это очень неэффективно с точки зрения буферизации и т. Д., Но должно помочь вам начать.

3
Jon Skeet 9 Ноя 2009 в 23:28
Однако отчасти проблема в том, что я читаю большой текстовый файл и извлекаю байты по мере необходимости. Использование StreamReader :: ReadToEnd () действительно очень удобно.
 – 
B L
5 Ноя 2009 в 00:30
Если вы читаете большой текстовый файл, то вы не имеете дело с байтами, вы имеете дело с текстом . Вы должны четко разделить их в своей голове.
 – 
Jon Skeet
5 Ноя 2009 в 00:34
Да ... Проблема в том, что у меня проблемы с компилятором, который понимает, что я пытаюсь сделать. Что я действительно хочу сделать, так это проанализировать текст ASCII и объединить 2 последовательных символа, чтобы сформировать «байт», а затем записать его в двоичной форме, а не в эквиваленте ASCII. Например, я объединю строки «1» и «2», но когда я конвертирую и записываю как байт, он будет писать 0x0C вместо 0x12. Методам Convert :: ToByte и WriteByte () это не нравится, но я не вижу другого способа сделать это. Я не могу заставить компилятор играть по моим правилам.
 – 
B L
5 Ноя 2009 в 00:55
Извините, вы пытаетесь написать 0x12 или 0x0C? Вы в основном пытаетесь преобразовать шестнадцатеричный код в двоичный? Если да, то я могу написать для этого код на C # и позволить вам понять, как его перенести на C ++ :)
 – 
Jon Skeet
5 Ноя 2009 в 01:05
Я пытаюсь написать 0x12 не 0x0C. Я полагаю, вы могли бы сказать, что я пытаюсь преобразовать шестнадцатеричный формат в двоичный. Короче говоря, я разбираю шестнадцатеричные строки из файла, объединяю последовательные символы и преобразую их в байт. Поэтому, когда я читаю «12», я не хочу записывать «0x31 0x32» или «0x0C», мне нужно объединенное байтовое представление «12» ... 0x12. Извините, если мое объяснение дерьмово.
 – 
B L
5 Ноя 2009 в 17:57

Класс BinaryWriter(), инициализированный потоком, будет использовать кодировку по умолчанию UTF8 для любых записываемых символов или строк. Я предполагаю, что

binWriter->Write(0x80);
binWriter->Write(0x81);
.
.
binWriter->Write(0x8F);
binWriter->Write(0x90);
binWriter->Write(0x91);

Вызовы привязаны к перегрузке Write( char), поэтому они проходят через кодировщик символов. Я не очень знаком с C ++ / CLI, но мне кажется, что эти вызовы должны быть привязаны к Write(Int32), у которого не должно быть этой проблемы (возможно, ваш код действительно вызывает Write() с char, для которой заданы значения в вашем примере. Этим объясняется такое поведение).

0
Michael Burr 5 Ноя 2009 в 00:37

0x3F широко известен как символ ASCII '?'; сопоставляемые ему символы являются управляющими символами без представления для печати. Как указывает Джон, для необработанных двоичных данных используйте двоичный поток, а не текстовый механизм вывода.

РЕДАКТИРОВАТЬ - на самом деле ваши результаты выглядят не так, как я ожидал. В кодовой странице 1252 по умолчанию непечатаемые символы (т. Е. Те, которые могут отображаться на '?') в этом диапазоне: 0x81, 0x8D, 0x8F, 0x90 и 0x9D

0
Steve Gilham 5 Ноя 2009 в 00:38