当前位置: 首页 > 科技观察

深度探索Node-(5)《缓冲区与乱码的故事》一共有十道题

时间:2023-03-21 21:55:18 科技观察

本文转载自微信公众号《前端阳光》,作者张拉拉,事业有成。转载本文请联系前方阳光公众号。1、为什么会有Buffer对象?2.能说说你知道的Buffer对象吗?模块结构Buffer对象结构3.哇,原来Buffer对象这么有意思,还可以当Array用。如果分配给元素的值是小数而不是整数怎么办?4、我觉得Buffer对象很像一个字符串。两者可以转换吗?StringtoBufferBuffertostring5.输入流中应该常用Buffer能告诉我怎么用吗?6、有时我这样读取数据,然后打印出来,有时会出现乱码。是什么原因?7、为什么“月”、“是”、“希望”、“低”这4个字符没有正常输出,被3个乱码代替?很麻烦,请问有什么简单的方法吗?9.哇,真刺激,Node是怎么实现这个输出的?10.但是设置解码器后,即使转码也无法改变宽字节字符串被??截断的问题?1、为什么会有Buffer对象?在Node中,应用程序需要处理网络协议、操作数据库、处理图片、接收和上传文件等,在网络流和文件的操作中,还需要处理大量的Binary数据,JavaScript自带的字符串是远远不能满足这些需求,于是Buffer对象应运而生。Buffer广泛应用于文件I/O和网络I/O,尤其是在网络传输中,其性能非常重要。在应用中,我们通常对字符串进行操作,但是一旦在网络上传输,就需要将其转换为Buffer,进行二进制数据传输。在Web应用程序中,字符串一直被转换为缓冲区。提高字符串到缓冲区的转换效率可以大大提高网络吞吐量。2.能说说你知道的Buffer对象吗?嗯,是的。Buffer是一个类Array对象,但主要用于操作字节。所以我会从模块结构和对象结构的层面去理解。模块结构Buffer是一个典型的JavaScript和C++结合的模块。它用C++实现了与性能相关的部分,用JavaScript实现了与性能无关的部分,如图所示。在【深入探索Node】(四)《内存控制》中,我们提到Buffer占用的内存不是V8分配的,属于堆外内存。由于V8垃圾收集性能的影响,使用更高效和专有的内存分配和回收策略来管理公共操作对象是一个好主意。因为Buffer太普通了,所以Node在进程启动的时候就加载了它,放到了全局对象(global)上。所以在使用Buffer的时候,可以直接使用,不需要()。Buffer对象结构Buffer对象类似于一个数组,它的元素是两位十六进制数,即0到255之间的值。示例代码如下:从上面的例子可以看出,不同编码的字符串占用的元素个数不同。上面代码中的汉字在UTF-8编码下占3个元素,字母和半角标点符号占1个元素。Buffer受Array类型的影响很大。可以访问length属性获取长度,也可以通过下标访问元素。构造对象时也很相似。代码如下:上述代码分配了一个长度为100字节的Buffer对象。可以通过下标访问新初始化的Buffer的元素,代码如下:这里你会得到一个比较奇怪的结果,它的元素值是0到255之间的随机值。同样,我们也可以赋值通过下标给它:3.哇,Buffer对象很有趣,它也可以用作Array。我突然想到,如果赋给元素的值是小数而不是整数怎么办??如果分配给元素的值小于0,则将256逐一添加到该值,直到得到0到25??5之间的整数。如果得到的值大于255,则依次减去256,直到得到0~255范围内的值。如果是小数,舍去小数部分,只保留整数部分。4、我觉得Buffer对象很像一个字符串。两者可以转换吗?是的。String到Buffer字符串到Buffer对象的转换主要是通过构造函数完成的:通过构造函数转换的Buffer对象只能存储一种编码。当不传encoding参数时,默认转码为UTF-8存储。通过将Buffer转成字符串,将Buffer转成字符串也很简单。Buffer对象的toString()可以将Buffer对象转换成字符串,结束这三个参数来实现整体或局部的转换。如果Buffer对象是用多种编码写的,需要在本地指定不同的编码转换回正常的编码。5.Buffer应该在输入流中通用。能说说怎么用吗?Buffer在使用场景中通常是逐段传输的。下面是一个常用的从输入流读取内容的示例代码:上面的代码在国外比较常用,用来演示流读取。data事件中获取到的chunk对象就是Buffer对象。对于初学者来说,很容易将Buffer理解为一个字符串,所以在接受上面的例子时并没有异常。6、有时我这样读取数据,然后打印出来,有时会出现乱码,请问是什么原因?一旦输入流中出现宽字节编码,问题就会暴露出来。如果您在使用Node开发的网站上看到[illustration]乱码,这很可能就是问题的根源。用多个字节表示的字符称为宽字符,Unicode只是宽字符编码的一种实现,宽字符不一定是Unicode。这里隐藏的问题在于下面的代码:toString()操作隐藏在图片的代码中,相当于下面的代码:值得注意的是,老外的上下文通常指的是英文环境,在他们的sceneNext,这个toString()不会出问题。但是对于宽字节的中文,就会出问题。为了重现这个问题,我们模拟了下面一个大概的场景,将每次读取文件可读流的Buffer长度限制为11。代码如下:这段代码的测试数据是李白的?。执行该程序会出现如下输出:7、为什么“月”、“是”、“王”、“低”这四个字符没有正常输出,而是被3个乱码代替?出现这个输出的原因是文件可读流在读取的时候会一个一个的读取Buffer。这首诗的原始Buffer应该存储为:由于我们将Buffer对象的长度限制为11,所以只读流需要读取7次才能完成完整的读取。结果是依次输出了以下Buffer对象:buf.toString()方法默认使用UTF-8编码,汉字在UTF-8下占3个字节。因此,当输出第一个Buffer对象时,只能显示3个字符,Buffer中剩余的2个字节(e69c)会以乱码的形式显示。第二个Buffer对象的第一个字节不能形成文本,只能显示乱码。导致部分文字无法正常显示。在这个例子中,我们构造了一个11的限制,但是对于任意长度的Buffer,宽字节字符串都有可能被截断,但是Buffer的长度越大,出现的概率越低,但是这个问题还是不能忽略.8.所以因果报应!既然如此,那我把Buffer对象的长度限制为12,就没有问题了!但是每次都要数很麻烦,请问有什么简单的方法吗?是的,让我们忘记了除了可读流之外,还有一个方法setEncoding()来设置编码。示例如下:该方法的作用是让数据事件传递的不再是Buffer对象,而是经过编码的字符串。为此,我们继续完善上一首诗的程序,添加setEncoding()的步骤如下:重新执行程序,得到输出:9.哇,真刺激,Node是怎么实现的输出?实际上,当调用setEncoding()时,可读流对象内部设置了一个解码器对象。每个数据事件通过解码器对象从Buffer解码为字符串,然后传递给调用者。因此,设置编码后,data不再接收原来的Buffer对象。10、但是设置解码器后,即使转码了,还是改不了宽字节字符串被??截断的问题?解码器对象来自string_decoder模块StringDecoder的实例对象。你可以看看下面的代码:我把上面提到的前两个Buffer对象写入到解码器中。奇怪的是,“月”的转码并没有像往常一样分两部分输出。StringDecoder得到编码后,知道宽字节字符串在UTF-8编码下是3个字节存储的,所以第一次写()时,只输出前9个字节转码形成的字符,前两个单词“month”的字节保存在StringDecoder实例中。在第二次write()中,将剩下的2个字节和后面的11个字节合并,再次使用3个字节的整数倍进行转码。所以乱码问题就是通过这个中间形式解决的。