资讯专栏INFORMATION COLUMN

C# 从 UTF-8 流中读取字符串的正确方法

lanffy / 3014人阅读

摘要:我们下面的代码是从一个流中读取编码的字符串。例如,笑脸符号有时会被解码为个未知字符编码字符串解码字符串我们知道可以使用到个字节来表示一个字符,有关字符串编码的知识可以参考字符编码一文。参考资料字符编码教程

我们下面的代码是从一个流 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#教程​
 

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/124125.html

相关文章

  • C# UTF-8 流中读取符串正确方法

    摘要:我们下面的代码微软雅黑是从一个流中读取编码的字符串。微软雅黑问题出在某些情况下返回的字符串与与原始编码的字符串并不同。第一种方法只有当你得到全部数据时,才将字节数组转换为字符串。 我们下面的代码是从一个流 stream 中读取 UTF-8 编码的字符串。我们可以先考虑一下其中存在的潜在问题。​string ReadStri...

    xiaochao 评论0 收藏0
  • 2018年第一周-JavaIO系统

    摘要:过滤器流,如等,是类库,是为了提供一些类让你能够处理一些极为常见的数据格式。读写器,由于流和过滤器流还是仅次于处理字节,也就是二进制。过滤器流缓冲流和类将写入的数据存储到缓冲区中一个名为的保护字节数组字段,直到缓冲区满或刷新输出流。 A little older, a little wiser, but happy to see you. ——Interstellar 2018年了,再...

    kgbook 评论0 收藏0
  • Java I/O简介

    摘要:如果不指定字符集,则使用系统默认字符编码,系统的默认字符编码一般是。所以更准确的说,是将一个字节输入流按照给定的字符编码来解码,从而得到一个字符输入流。当然,缺点就是不能选择使用的字符编码。 相对于Python和 C来说,Java的I/O操作API比较复杂,因此本文打算做个简单的介绍。 1. I/O分类 总的来说Java的I/O按照处理数据的粒度和方向来划分,一共可以分为4类: 基...

    darkbug 评论0 收藏0
  • 重拾Java Network Programming(一)IO流

    摘要:不同类型的流入,往往对应于不同类型的流数据。所以通常会将字节缓存到一定数量后再发送。如果是,则将两个标记都抛弃并且将之前的内容作为一行返回。因此二者陷入死锁。因此推出了和类。 前言 最近在重拾Java网络编程,想要了解一些JAVA语言基本的实现,这里记录一下学习的过程。 阅读之前,你需要知道 网络节点(node):位于网络上的互相连通的设备,通常为计算机,也可以是打印机,网桥,路由器等...

    Lycheeee 评论0 收藏0
  • 第十一章-IO流#yyds干货盘点#

    摘要:是一个系统支持的所有字符的集合,包括各国家文字标点符号图形符号数字等字符集简体中文码表。支持中国国内少数民族的文字,同时支持繁体汉字以及日韩汉字等字符集为表达任意语言的任意字符而设计,是业界的一种标准,也称为统一码标准万国码。 1 File1.1 File类的概述和构造方法File: 它是文件和目录路径名的抽象...

    不知名网友 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<