如何从无限字节 C# 中读取 UTF-8 字符



通常,要从字节流中读取字符,请使用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();
    }
}

如何修改此代码以使其适用于多字节字符?

不是Encoding.UTF8.GetChars旨在转换完整缓冲区的,而是获取Decoder的实例并重复调用其成员方法GetChars这将利用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();
    }
}

我不明白你为什么不使用流阅读器的 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();
    }
}

Mike,我发现您的解决方案也非常适合我的情况。但我注意到有时需要四次 GetChar(( 调用来确定要返回的字符。这意味着charCount是2,而我的下一个Char缓冲区大小是1。所以我收到错误"输出字符缓冲区太小,无法包含解码字符,编码 Unicode 回退系统.文本.解码器替换回退。

我将代码更改为:

    // ...
    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...
        }
    }

相关内容

  • 没有找到相关文章

最新更新