通常,要从字节流中读取字符,请使用StreamReader。在这个例子中,我正在从无限流中读取由'\ r'分隔的记录。
using(var reader = new StreamReader(stream, Encoding.UTF8))
{
var messageBuilder = new StringBuilder();
var nextChar = 'x';
while (reader.Peek() >= 0)
{
nextChar = (char)reader.Read()
messageBuilder.Append(nextChar);
if (nextChar == '\r')
{
ProcessBuffer(messageBuilder.ToString());
messageBuilder.Clear();
}
}
}
问题是StreamReader有一个小的内部缓冲区,所以如果代码等待'记录结束'分隔符(在这种情况下为'\ r'),它必须等到StreamReader的内部缓冲区被刷新(通常因为有更多的字节到了。)
此替代实现适用于单字节UTF-8字符,但在多字节字符上会失败。
int byteAsInt = 0;
var messageBuilder = new StringBuilder();
while ((byteAsInt = stream.ReadByte()) != -1)
{
var nextChar = Encoding.UTF8.GetChars(new[]{(byte) byteAsInt});
Console.Write(nextChar[0]);
messageBuilder.Append(nextChar);
if (nextChar[0] == '\r')
{
ProcessBuffer(messageBuilder.ToString());
messageBuilder.Clear();
}
}
如何修改此代码以使其适用于多字节字符?
答案 0 :(得分:10)
而不是设计用于转换完整缓冲区的Encoding.UTF8.GetChars
,获取Decoder
的实例并重复调用其成员方法GetChars
,这将使用Decoder
' s内部缓冲区,用于处理从一次调用结束到下一次调用的部分多字节序列。
答案 1 :(得分:5)
感谢理查德,我现在有了一个无限流读者。正如他解释的那样,诀窍是使用Decoder实例并调用其GetChars方法。我用多字节日文文本对它进行了测试,效果很好。
int byteAsInt = 0;
var messageBuilder = new StringBuilder();
var decoder = Encoding.UTF8.GetDecoder();
var nextChar = new char[1];
while ((byteAsInt = stream.ReadByte()) != -1)
{
var charCount = decoder.GetChars(new[] {(byte) byteAsInt}, 0, 1, nextChar, 0);
if(charCount == 0) continue;
Console.Write(nextChar[0]);
messageBuilder.Append(nextChar);
if (nextChar[0] == '\r')
{
ProcessBuffer(messageBuilder.ToString());
messageBuilder.Clear();
}
}
答案 2 :(得分:1)
我不明白你为什么不使用流阅读器的ReadLine方法。但是,如果没有充分的理由,那么在我看来,在解码器上反复调用GetChars效率很低。为什么不利用'\ r'的字节表示不能成为多字节序列的一部分呢? (多字节序列中的字节必须大于127;也就是说,它们的位设置最高。)
var messageBuilder = new List<byte>();
int byteAsInt;
while ((byteAsInt = stream.ReadByte()) != -1)
{
messageBuilder.Add((byte)byteAsInt);
if (byteAsInt == '\r')
{
var messageString = Encoding.UTF8.GetString(messageBuilder.ToArray());
Console.Write(messageString);
ProcessBuffer(messageString);
messageBuilder.Clear();
}
}
答案 3 :(得分:0)
迈克, 我也发现您的解决方案也很适合我的情况。但是我注意到,有时需要四个GetChar()调用来确定要返回的字符。这意味着charCount为2,而我的nextChar缓冲区大小为1。所以我收到错误消息“输出字符缓冲区太小,无法包含已解码的字符,对Unicode后备System.Text.DecoderReplacementFallback进行编码。”
我将代码更改为:
// ...
var nextChar = new char[4]; // 2 might suffice
for (var i = startPos; i < bytesRead; i++)
{
int charCount;
//...
charCount = decoder.GetChars(buffer, i, 1, nextChar, 0);
if (charCount == 0)
{
bytesSkipped++;
continue;
}
for (int ic = 0; ic < charCount; ic++)
{
char c = nextChar[ic];
charPos++;
// Process character here...
}
}