C# - .NET 4.7 - 流编码问题 - unicode 和 ascii 字符读取不正确
C# - .NET 4.7 - Stream Encoding problem - incorrect reads of unicode and ascii characters
我编写了一个小函数来将程序数据写入流中。大概这个流可以去任何地方:一个文件(当前用例),一个套接字,内存,任何地方。所以很自然地我只是使用默认编码写入数据。经过一些测试后,它抛出了一个编码异常。所以我不得不将字符串编码成字节数组并写入字节。
但是有一个问题:写入的字节在读取时不会解码回相同的字符串。如果我们使用可在键盘上键入的 ascii 字符,这不是问题,但当我们开始使用 unicode 字符和显然是 27 个 ascii 字符时,它确实会成为问题。
这是测试用例。我鼓励你 运行 它:
using System.IO;
using System.Text;
using System;
public class TestCase
{
public static void Main(string[] args)
{
readwrite_tests();
}
public static void readwrite_tests()
{
string temps, result;
ulong count = 0;
byte[] buffer = new byte[sizeof(char) * 4];
using(MemoryStream mem = new MemoryStream(buffer))
using (BinaryReader reader = new BinaryReader(mem, Encoding.Default))
using (BinaryWriter writer = new BinaryWriter(mem, Encoding.Default))
{
for(char c = char.MinValue; c <= 0xfff; ++c)
{
temps = c.ToString();
if(mem.Position != 0) mem.Seek(0, SeekOrigin.Begin);
result = read_write(temps, writer, reader, mem);
if(!result.Equals(temps))
{
//Console.Write("char: " + c.ToString() + " int: " + ((int)c).ToString() +
// "\tread: " + result + " int: [");
//foreach (char d in result) Console.Write(((int)d).ToString() + " ");
//Console.WriteLine("]");
++count;
}
}
}
Console.WriteLine("Incorrect reads is " + count.ToString() +
" out of " + int.Parse("fff", System.Globalization.NumberStyles.HexNumber));
Console.WriteLine("Correct Reads: " + ((ulong)int.Parse("fff", System.Globalization.NumberStyles.HexNumber) - count));
}
public static string read_write(string s, BinaryWriter writer, BinaryReader reader, Stream stream)
{
string read_string = "";
byte[] bytes = Encoding.Default.GetBytes(s);
writer.Write(bytes.Length);
writer.Write(bytes);
stream.Seek(0, SeekOrigin.Begin);
try
{
read_string = Encoding.Default.GetString(reader.ReadBytes(reader.ReadInt32()));
}
catch(EndOfStreamException)
{
}
return read_string;
}
}
请运行在https://dotnetfiddle.net/上查看结果。
如您所见,我们只有 238 次正确读取。我不明白为什么会这样。让我知道是否可以提供更多信息,但我已经尝试了很多,包括改用 JsonSerializer(结果相同)。
选择一种显式编码并坚持使用。最好是 UTF-8。在 .NET 4.7.2 中,默认编码(至少在 .NET Fiddle 上)是西欧 (Windows)。在 .NET 5 中是 Unicode (UTF-8)。
如果您不相信我,请将此行添加到您的 read_write 例程中:
Console.WriteLine(Encoding.Default.EncodingName);
我编写了一个小函数来将程序数据写入流中。大概这个流可以去任何地方:一个文件(当前用例),一个套接字,内存,任何地方。所以很自然地我只是使用默认编码写入数据。经过一些测试后,它抛出了一个编码异常。所以我不得不将字符串编码成字节数组并写入字节。
但是有一个问题:写入的字节在读取时不会解码回相同的字符串。如果我们使用可在键盘上键入的 ascii 字符,这不是问题,但当我们开始使用 unicode 字符和显然是 27 个 ascii 字符时,它确实会成为问题。
这是测试用例。我鼓励你 运行 它:
using System.IO;
using System.Text;
using System;
public class TestCase
{
public static void Main(string[] args)
{
readwrite_tests();
}
public static void readwrite_tests()
{
string temps, result;
ulong count = 0;
byte[] buffer = new byte[sizeof(char) * 4];
using(MemoryStream mem = new MemoryStream(buffer))
using (BinaryReader reader = new BinaryReader(mem, Encoding.Default))
using (BinaryWriter writer = new BinaryWriter(mem, Encoding.Default))
{
for(char c = char.MinValue; c <= 0xfff; ++c)
{
temps = c.ToString();
if(mem.Position != 0) mem.Seek(0, SeekOrigin.Begin);
result = read_write(temps, writer, reader, mem);
if(!result.Equals(temps))
{
//Console.Write("char: " + c.ToString() + " int: " + ((int)c).ToString() +
// "\tread: " + result + " int: [");
//foreach (char d in result) Console.Write(((int)d).ToString() + " ");
//Console.WriteLine("]");
++count;
}
}
}
Console.WriteLine("Incorrect reads is " + count.ToString() +
" out of " + int.Parse("fff", System.Globalization.NumberStyles.HexNumber));
Console.WriteLine("Correct Reads: " + ((ulong)int.Parse("fff", System.Globalization.NumberStyles.HexNumber) - count));
}
public static string read_write(string s, BinaryWriter writer, BinaryReader reader, Stream stream)
{
string read_string = "";
byte[] bytes = Encoding.Default.GetBytes(s);
writer.Write(bytes.Length);
writer.Write(bytes);
stream.Seek(0, SeekOrigin.Begin);
try
{
read_string = Encoding.Default.GetString(reader.ReadBytes(reader.ReadInt32()));
}
catch(EndOfStreamException)
{
}
return read_string;
}
}
请运行在https://dotnetfiddle.net/上查看结果。
如您所见,我们只有 238 次正确读取。我不明白为什么会这样。让我知道是否可以提供更多信息,但我已经尝试了很多,包括改用 JsonSerializer(结果相同)。
选择一种显式编码并坚持使用。最好是 UTF-8。在 .NET 4.7.2 中,默认编码(至少在 .NET Fiddle 上)是西欧 (Windows)。在 .NET 5 中是 Unicode (UTF-8)。
如果您不相信我,请将此行添加到您的 read_write 例程中:
Console.WriteLine(Encoding.Default.EncodingName);