RAD Studio 10.1中TFileStream写入字节结果异常问题
问题诱因
RAD Studio 10.1 Berlin默认的string类型是UnicodeString,采用UTF-16编码,每个Char类型字符占2个字节,而非Delphi旧版本中单字节的AnsiString。
你的代码存在两个直接导致结果不符合预期的问题:
- 写入长度计算错误:
Length(sPalette)返回的是字符串的字符个数,不是实际占用的字节数。你定义的字符串有6个字符,实际占12字节,但代码只向流中写入了6字节,刚好只写入了前3个字符的完整双字节内容:每个字符的低字节是你定义的#1/#2/#3,高字节是0x00,最终就得到了01 00 02 00 03 00的输出,后面#4/#5/#6对应的字节完全没有被写入。 - 存储类型选型错误:要写入原始二进制字节序列时,不应该用双字节的Unicode字符串作为存储载体,极易因为编码隐式转换导致字节内容错乱。
另外原代码没有加异常保护,如果写入过程中抛出错误,创建的文件流会发生资源泄漏。
修复方法
优先推荐用字节数组存储原始二进制数据,从根源上规避字符串编码带来的问题,参考写法:
var PaletteBytes: array[0..5] of Byte; mystream: TFileStream; begin mystream := TFileStream.Create('C:\Data\test.bmp', fmCreate); try PaletteBytes := [1, 2, 3, 4, 5, 6]; mystream.WriteBuffer(PaletteBytes, SizeOf(PaletteBytes)); finally mystream.Free; end; end;
如果确实需要用字符串存储单字节序列,可以将字符串类型显式声明为AnsiString,同时注意长度按实际字节计算:
var sPalette : AnsiString; mystream: TFileStream; begin mystream := TFileStream.Create('C:\Data\test.bmp', fmCreate); try sPalette := #1#2#3#4#5#6; mystream.WriteBuffer(Pointer(sPalette)^, Length(sPalette) * SizeOf(AnsiChar)); finally mystream.Free; end; end;
不推荐继续使用默认UnicodeString存二进制字节,如果一定要这么写,必须在计算写入长度时乘以单个Char的字节数,且要保证所有字符编码值都在0-255区间,否则会出现字节截断问题:
var sPalette : string; mystream: TFileStream; begin mystream := TFileStream.Create('C:\Data\test.bmp', fmCreate); try sPalette := #1#2#3#4#5#6; mystream.WriteBuffer(Pointer(sPalette)^, Length(sPalette) * SizeOf(Char)); finally mystream.Free; end; end;
内容的提问来源于stack exchange,提问作者Ankush
相关产品推荐
相关产品推荐

