【问题标题】:Reading a single UTF8 character from stream in C#从 C# 中的流中读取单个 UTF8 字符
【发布时间】:2017-04-01 08:28:34
【问题描述】:

我希望从 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。对于这个问题,是否有更优雅、更少定制、更易于阅读的解决方案?

【问题讨论】:

  • 问题可能重复,但该答案不起作用。具体来说,它不处理代理对。我尝试修改它以使用 2 元素 char 缓冲区,但这只是导致了不同的问题。但是,除了代理对之外,它工作正常。
  • 酷,我不能 100% 确定这是否是同一件事,但我认为其中可能包含一些有用的信息。
  • 这是一个很好的发现——我的搜索没有找到它。可悲的是,“有什么可行的方法,但没有我目前的解决方案那么难看”这个问题就成立了。

标签: c# utf-8 stream


【解决方案1】:

我知道这个问题有点老了,但这是另一种解决方案。它的性能不如 OPs 解决方案(我也更喜欢),但它只使用内置 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];
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-01
    • 2013-01-03
    • 2010-10-10
    • 2015-11-01
    • 2012-04-29
    • 2012-02-28
    相关资源
    最近更新 更多