zoukankan      html  css  js  c++  java
  • C# 从 UTF8 流中读取字符串的正确方法

    我们下面的代码是从一个流 stream 中读取 UTF-8 编码的字符串。我们可以先考虑一下其中存在的潜在问题。

    string ReadString(Stream stream)
    {
        var sb = new StringBuilder();
        var buffer = new byte[4096];
        int readCount;
        while ((readCount = stream.Read(buffer)) > 0)
        {
            var s = Encoding.UTF8.GetString(buffer, 0, readCount);
            sb.Append(s);
        }
    
        return sb.ToString();
    }
    

    问题出在:某些情况下返回的字符串与与原始编码的字符串并不同。
    例如,笑脸符号 有时会被解码为 4 个未知字符:

    编码字符串: 
    解码字符串: ????
    

    我们知道:UTF-8 可以使用 1 到 4 个字节来表示一个 Unicode 字符,有关字符串编码的知识可以参考 ​​字符编码​​​ 一文。

    ​​Stream.Read​​​ 方法可以把从 1 到​​ messageBuffer.Length​​​ 字节返回,这意味着缓冲区可能包含不完整的 UTF-8 字符。

    一旦缓冲区中的最后一个字符的 UTF-8 编码不完整,那么 ​​Encoding.UTF8.GetString​​ 就是转换一个无效的 UTF-8 字符串。在这种情况下,该方法返回一个无效字符串,因为它无法猜测丢失的字节。

    我们使用以下代码演示以上行为:

    var bytes = Encoding.UTF8.GetBytes("?");
    // bytes = new byte[4] { 240, 159, 152, 138 }
    
    var sb = new StringBuilder();
    // 模拟逐个字节地读取数据流
    for (var i = 0; i < bytes.Length; i++)
    {
        sb.Append(Encoding.UTF8.GetString(bytes, i, 1));
    }
    
    Console.WriteLine(sb.ToString());
    // "????" 代替了 ""
    
    Encoding.UTF8.GetBytes(sb.ToString());
    // new byte[12] { 239, 191, 189, 239, 191, 189, 239, 191, 189, 239, 191, 189 }
     
    

    如何修复代码

    有多种方法可以修复代码。
    第一种方法:只有当你得到全部数据时,才将字节数组转换为字符串。

    string ReadString(Stream stream)
    {
        using var ms = new MemoryStream();
        var buffer = new byte[4096];
        int readCount;
        while ((readCount = stream.Read(buffer)) > 0)
        {
            ms.Write(buffer, 0, readCount);
        }
    
        return Encoding.UTF8.GetString(ms.ToArray());
    }
    

    第二种方法:可以把流包进一个具有正确编码的 StreamReader 对象中。

    string ReadString(Stream stream)
    {
        using var sr = new StreamReader(stream, Encoding.UTF8);
        return sr.ReadToEnd();
    }
    

    另外,还可以使用System.Text.Decoder类来正确解码缓冲区内的字符。在需要性能的情况下,可以使用PipeReader、Rune类来以内存优化的方式读取数据。

    参考资料:

    1. ​​字符编码​ ​​
    2. C#教程​
  • 相关阅读:
    FileItem类的常用方法
    spring mvc(注解)上传文件的简单例子
    Linux下安装Tomcat服务器和部署Web应用
    防止表单重复提交的几种策略
    Rancher 2.0 学习目录
    Prometheus 学习目录
    k8s学习目录
    python之路——目录
    Mac OS X生成RSA公钥和私钥
    Django设置 DEBUG=False后静态文件无法加载解决
  • 原文地址:https://www.cnblogs.com/wanghao72214/p/15586846.html
Copyright © 2011-2022 走看看