我希望从Stream或BinaryReader中读取下一个UTF8字符。不起作用的事情:
BinaryReader :: ReadChar - 这会抛出一个3或4个字节的字符。由于它返回一个两字节结构,因此没有选择。
BinaryReader :: ReadChars - 如果你要求它读取1个字符并且它遇到3或4个字节的字符,这将抛出。如果您要求多个字符读取超过1个字符,则会读取多个字符。
StreamReader :: Read - 这需要知道要读取多少字节,但UTF8字符中的字节数是可变的。
我的代码似乎有效:
private char[] ReadUTF8Char(Stream s)
{
byte[] bytes = new byte[4];
var enc = new UTF8Encoding(false, true);
if (1 != s.Read(bytes, 0, 1))
return null;
if (bytes[0] <= 0x7F) //Single byte character
{
return enc.GetChars(bytes, 0, 1);
}
else
{
var remainingBytes =
((bytes[0] & 240) == 240) ? 3 : (
((bytes[0] & 224) == 224) ? 2 : (
((bytes[0] & 192) == 192) ? 1 : -1
));
if (remainingBytes == -1)
return null;
s.Read(bytes, 1, remainingBytes);
return enc.GetChars(bytes, 0, remainingBytes + 1);
}
}
显然,这有点乱,有点特定于UTF8。是否有更优雅,更少自定义,更易于阅读的解决方案?
答案 0 :(得分:0)
我知道这个问题有点旧,但这是另一种解决方案。它的性能不如OPs解决方案(我也更喜欢),但它只使用builtin-utf8功能,而不知道utf8编码内部。
private static char ReadUTF8Char(Stream s)
{
if (s.Position >= s.Length)
throw new Exception("Error: Read beyond EOF");
using (BinaryReader reader = new BinaryReader(s, Encoding.Unicode, true))
{
int numRead = Math.Min(4, (int)(s.Length - s.Position));
byte[] bytes = reader.ReadBytes(numRead);
char[] chars = Encoding.UTF8.GetChars(bytes);
if (chars.Length == 0)
throw new Exception("Error: Invalid UTF8 char");
int charLen = Encoding.UTF8.GetByteCount(new char[] { chars[0] });
s.Position += (charLen - numRead);
return chars[0];
}
}
传递给BinaryReader
的构造函数的编码无关紧要。我不得不使用这个版本的构造函数来打开流。如果您已经有二进制阅读器,可以使用它:
private static char ReadUTF8Char(BinaryReader reader)
{
var s = reader.BaseStream;
if (s.Position >= s.Length)
throw new Exception("Error: Read beyond EOF");
int numRead = Math.Min(4, (int)(s.Length - s.Position));
byte[] bytes = reader.ReadBytes(numRead);
char[] chars = Encoding.UTF8.GetChars(bytes);
if (chars.Length == 0)
throw new Exception("Error: Invalid UTF8 char");
int charLen = Encoding.UTF8.GetByteCount(new char[] { chars[0] });
s.Position += (charLen - numRead);
return chars[0];
}