2009-11-04 25 views
0

我試圖嚴格寫入二進制數據到文件(無編碼)。問題是,當我十六進制轉儲文件,我注意到相當奇怪的行爲。使用下列方法之一來構造文件會導致相同的行爲。我甚至使用System :: Text :: Encoding :: Default來測試這些流。Visual Studio C++ 2008操縱字節?

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

此外,我忽略了只有0x8和0x9字節序列似乎受到影響的事實。例如,0xF9或0xC3,就像他們應該寫的一樣。 0x3F是ASCII碼的?如果有人有興趣。 – 2009-11-04 21:27:36

回答

3

如果你想堅持二進制文件,那麼不要使用StreamWriter。只需使用FileStreamWrite/WriteByte。 StreamWriters(和通常的TextWriters)明確爲文本設計。無論你是否需要編碼,都會應用 - 因爲當你打電話給StreamWriter.Write時,這是寫一個char,而不是一個byte

不要創建wchar_t值的數組 - 也是用於字符即文本。

BinaryWriter.Write本應該爲你工作,除非它將值提升到char在這種情況下,你會遇到完全相同的問題。

順便說一句,沒有指定任何編碼,我希望你得到非0x3F值,而是代表這些字符的UTF-8編碼值的字節。

當您指定Encoding.Default時,對於任何不在該編碼中的Unicode值,您都會看到0x3F。

無論如何,基本的教訓是堅持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); 
} 

這是緩衝等方面非常低效的,但應該讓你開始。

+0

雖然問題的一部分是我正在閱讀大量文本文件並根據需要提取字節。使用StreamReader :: ReadToEnd()確實非常方便。 – 2009-11-04 21:30:16

+0

如果你正在閱讀一個大文本文件,那麼你*不*處理字節,你正在處理*文本*。你需要非常清楚地分開你的腦袋。 – 2009-11-04 21:34:12

+0

是的..這樣做的問題是,我在編譯器中遇到問題,無法理解我想要做什麼。我真正想要做的是解析ASCII文本並連續連續2個字符以形成一個「字節」,然後將其寫入二進制形式,而不是ASCII碼等效。例如,我會連接字符串「1」和「2」,但是當我將其轉換並寫入爲一個字節時,它將寫入0x0C而不是0x12。 Convert :: ToByte和WriteByte()方法不喜歡那樣,但是我看不出有其他方法可以做到這一點。我似乎無法強迫編譯器按照我的規則玩遊戲。 – 2009-11-04 21:55:03

0

0x3F通常被稱爲ASCII字符'?';映射到它的字符是沒有可打印表示的控制字符。正如Jon指出的那樣,對原始二進制數據使用二進制流而不是面向文本的輸出機制。

編輯 - 實際上你的結果看起來像我期望的結果。在默認code page 1252中,不可打印字符(即可能映射到'?')在這個範圍內是0x81,0x8D,0x8F,0x90和0x9D

0

A 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變量設置爲值你的例子,這將解釋這種行爲)。