js实现不同编码格式内容转码

3,247 阅读8分钟

js实现不同编码格式内容转码

  • 支持buffer转化为各类编码格式的字符串
  • 支持utf8字符串编码为其他多种类型的buffer
  • 支持buffer内容传输与解析

客服端

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Test</title>
</head>
<body>
    <button id="button">请求数据</button>
    <p>请求内容:</p>
    <div id="content"></div>
</body>
<script src=""></script>
<script>
    const btn = document.getElementById('button')
    btn.addEventListener('click', queryBuffer)
​
    function queryBuffer() {
        const reqContent = {name: 'buffer', type: 'gbk', data: []}
        const bufferContent = new TextEncoder().encode('正如我');
        reqContent.data = bufferContent
        // 发送post请求接口数据
        var httpRequest = new XMLHttpRequest(); // 第一步:创建需要的对象
        httpRequest.open('POST', '/test', true); // 第二步:打开连接
        httpRequest.setRequestHeader("Content-type", "application/json"); //设置请求头 注:post方式必须设置请求头(在建立连接后设置请求头)
        httpRequest.send();//发送请求 将请求头写在send中
​
        // 获取数据后的处理程序
        httpRequest.onreadystatechange = function () { // 请求后的回调接口,可将请求成功后要执行的程序写在其中
            if (httpRequest.readyState == 4 && httpRequest.status == 200) { // 验证请求是否发送成功
                const response = JSON.parse(httpRequest.response)
                const bufferData = response.data
                const arr = new Uint8Array(bufferData)
                const string = new TextDecoder('gb2312').decode(arr);
                document.getElementById('content').innerHTML = string
            }
        };
    }
</script>
</html>

服务端

var express = require('express')
var app = express() 
app.use(express.static('src'))
const fs = require("fs")
const path = require("path")
const iconv = require('iconv-lite');
app.use(express.json());
  
app.get('/', (req, res) => {})
​
app.post('/test', async function (req, res) {
    const reqData = typeof req.body.data
    console.log(reqData)
    const response = await getGbkData()
    const data = JSON.stringify(response) // 转为json字符串
    res.send(data)
})
​
app.listen(8888, () => {
    console.log("Successfully connected")
})
​
const getGbkData = () => {
    return new Promise((resolve, reject) => {
        fs.readFile(path.join(__dirname, "./gb2312.txt"), function (err, data) {
            if (err) {
                console.log('err')
            }
            const utf8String = iconv.decode(data, 'gbk')
            const gbkBuffer = iconv.encode(utf8String, 'gbk');
            resolve(data)
        })
    })
}

js实现转码的一些方式

1. Encoding API

Encoding API 提供了一种机制来处理各种字符编码文本,包括传统的非 UTF-8 编码。

API 提供了四个接口:TextDecoderTextEncoderTextDecoderStreamTextEncoderStream

TextDecoder

TextDecoder 接口表示一个文本解码器,一个解码器只支持一种特定文本编码,例如 UTF-8ISO-8859-2KOI8-RGBK,等等。解码器将字节流作为输入,并提供码位流作为输出。

构造方法:

TextDecoder() 构造函数使用参数中指定的编码返回一个新创建的 TextDecoder 对象。

  • 语法

    new TextDecoder()
    new TextDecoder(utfLabel)
    new TextDecoder(utfLabel, options)
    

    参数:

    utfLabel(可选): 一个字符串,默认是 "utf-8"。可以是任意有效的编码

    options (可选):一个具有属性的对象:

    • fatal:一个布尔值,表示在解码无效数据时,TextDecoder.decode() 方法是否必须抛出 TypeError。默认是 false,这意味着解码器将用替换字符替换错误的数据。
    // 示例
    const textDecoder1 = new TextDecoder("iso-8859-2");
    const textDecoder2 = new TextDecoder();
    const textDecoder3 = new TextDecoder("csiso2022kr", { fatal: true }); // Allows TypeError 
    
  • 实例属性

    encoding:只读属性 TextDecoder.encoding 返回一个字符串,其中包含了指定的解码器的解码算法的名称。编码由构造函数label 参数设置,并且默认是 utf-8

    fatal:只读属性 fatal 是一个布尔值,表示错误模式是否是致命的。

    ignoreBOM:只读属性 ignoreBOM 是一个布尔值,表示是否忽略字节顺序标记

  • 实例方法

    decode() :返回一个字符串,其包含作为参数传递的缓冲区解码后的文本。解码方法在当前的 TextDecoder 对象中定义。这包含了数据的预期编码,以及如何处理解码时发生的错误。

    语法: decode(buffer, options)

    参数:

    buffer 可选:一个 ArrayBufferTypedArray 或包含要解码的编码文本的 DataView 对象。

    options 可选:具有以下属性的对象:

    • stream:一个布尔标志,表示在后续调用 decode() 将跟随附加数据。

    返回值: 一个字符串。

    示例:

    const array = new Uint8Array([226, 130, 172]) 
    const decoder = new TextDecoder();
    const str = decoder.decode(array); // String "€"
    

TextEncoder

TextEncoder 接受码位流作为输入,并提供 UTF-8 字节流作为输出。

构造函数

TextEncoder() 返回一个新构造的 TextEncoder,它默认使用 UTF-8 编码将码位流转换成字节流。

  • 语法

    new TextEncoder()
    
  • 实例属性

    encoding:只读属性返回一个字符串,其中包含特定编码器使用的编码算法的名称。值只能为:utf-8

  • 实例方法

    encode() :接受一个字符串作为输入,返回一个对参数中给定的文本的编码后的 Uint8Array,编码的方法通过 TextEncoder 对象指定。

    语法: encode(string)

    参数:

    string可选:一个包含了将要编码的文本。

    返回值: 一个 Uint8Array (en-US) 对象。

    示例:

    const string = 'hello'
    const textEncoder = new TextEncoder();
    let encoded = textEncoder.encode(string);
    

2. icon-lite

iconv-lite是一个用于字符编码转换的JavaScript库。它的主要功能是将一种字符编码转换为另一种字符编码

iconv-lite支持多种字符编码,包括UTF-8、ISO-8859-1、GBK、GB2312、Big5等。它还支持流式转换和批量转换,可以快速处理大量数据。

iconv-lite安装:

npm install iconv-lite

安装完成后,在JavaScript代码中引入iconv-lite库:

const iconv = require('iconv-lite');

Basic API

  • iconv.encode(str, encoding, options):将字符串编码为指定编码格式。其中,str为要编码的字符串,encoding为目标编码格式,options为可选参数,用于指定是否添加BOM、是否忽略无效字符等。
  • iconv.decode(buffer, encoding, options):将Buffer对象解码为字符串。其中,buffer为要解码的Buffer对象,encoding为源编码格式,options为可选参数,用于指定是否忽略无效字符等。
// 将Buffer转换为js字符串
str = iconv.decode(Buffer.from([0x68, 0x65, 0x6c, 0x6c, 0x6f]), 'win1251');
​
// 从js字符串转换为Buffer
buf = iconv.encode("Sample input string", 'gbk');
​
// 检查指定编码格式是否被支持
iconv.encodingExists("us-ascii")

Streaming API

  • iconv.encodeStream(encoding, options):创建一个可写流,用于将数据流转换为指定编码格式。其中,encoding为目标编码格式,options为可选参数,用于指定是否添加BOM、是否忽略无效字符等。
  • iconv.decodeStream(encoding, options):创建一个可读流,用于将数据流解码为字符串。其中,encoding为源编码格式,options为可选参数,用于指定是否忽略无效字符等。
// 解码流(从二进制数据流到js字符串)
http.createServer(function(req, res) {
    var converterStream = iconv.decodeStream('win1251');
    req.pipe(converterStream);
​
    converterStream.on('data', function(str) {
        console.log(str); // TODO: 对解码后的字符串逐个执行操作。
    });
});
​
// 转换编码流示例
fs.createReadStream('file-in-win1251.txt')
    .pipe(iconv.decodeStream('win1251'))
    .pipe(iconv.encodeStream('ucs2'))
    .pipe(fs.createWriteStream('file-in-ucs2.txt'));
​
// Sugar:所有编码/解码流都有.collect(cb)方法来累积数据。
http.createServer(function(req, res) {
    req.pipe(iconv.decodeStream('win1251')).collect(function(err, body) {
        assert(typeof body == 'string');
        console.log(body); // full request body string
    });
});

支持的编码类型

所有node.js本机编码:utf8、ucs2/utf16le、ascii、binary、base64、hex。

其他unicode编码:utf16、utf16-be、utf-7、utf-7-imap、utf32、utf32-le和utf32-be。

所有广泛使用的单字节编码:Windows 125x系列、ISO-8859系列、IBM/DOS代码页、Macintosh系列、KOI8系列,以及iconv库支持的所有其他代码。别名,如“latin1”、“us-ascii”也受支持。

所有广泛使用的多字节编码:CP932、CP936、CP949、CP950、GB2312、GBK、GB18030、Big5、Shift_JIS、EUC-JP。

3. 扩展内容

3.1.Buffer对象与ArrayBuffer和Uint8Array等对象对比

相同点:

  • 它们都可以用于存储二进制数据,并且可以通过索引访问数据。

不同点:

  • 定义:Buffer对象是Node.js中特有的用于处理二进制数据的类。它是一个全局变量,可以通过require('buffer')获取。ArrayBuffer和Uint8Array等对象是HTML5标准中定义的。
  • 设计目标:Buffer对象的设计目标是为了处理网络通信、文件I/O等场景下的二进制数据。ArrayBuffer和Uint8Array等对象则是为了处理在Web环境下的二进制数据。
  • 底层实现方式:Buffer对象是在C++层面实现的,而ArrayBuffer和Uint8Array等对象则是在JavaScript层面实现的。在处理大量二进制数据时,Buffer对象的性能可能更好一些。
  • API:Buffer对象提供了toString()方法用于将二进制数据转换为字符串,ArrayBuffer和Uint8Array等对象没有类似方法。Buffer对象还提供了一些其他的API,例如copy()、slice()、fill()等方法,用于复制、分割、填充等操作。

互转:

在处理二进制数据时,它们也可以相互转换。可以使用Buffer.from()方法将ArrayBuffer对象转换为Buffer对 象,也可以使用Buffer的buffer属性将Buffer对象转换为ArrayBuffer对象。

Buffer.from方法

Node.js中用于创建Buffer对象的方法之一。它可以将字符串、数组或其他数据类型转换为Buffer对象。

Buffer.from()方法的语法如下:

Buffer.from(string[, encoding]) // encoding为可选参数,指定string的编码格式,默认为'utf8';
Buffer.from(array)
Buffer.from(buffer)
Buffer.from(arrayBuffer[, byteOffset[, length]]) // byteOffset为可选参数,指定从arrayBuffer的哪个字节开始转换,默认为0,length为可选参数,指定转换的字节数,默认为arrayBuffer的长度减去byteOffset。

例如,将一个字符串转换为Buffer对象:

const buf = Buffer.from('hello world', 'utf8');
console.log(buf); // Buffer(11) [104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]

Buffer.from()方法还可以将整数数组、另一个Buffer对象或ArrayBuffer对象转换为Buffer对象。例如,将一个整数数组转换为Buffer对象:

const buf = Buffer.from([0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64]);
console.log(buf.toString('utf8')); // hello world

整数数组成功转换为Buffer对象,并且可以将其转换回字符串。

3.2 ArrayBuffer与Uint8Array等对象对比

都是用于处理二进制数据的工具

ArrayBuffer: JavaScript中的一种数据类型,用于表示一段二进制数据。它本身并不能直接进行读写操作,需要使用TypedArray视图或DataView视图来读写数据。

TypedArray: 是一系列与ArrayBuffer关联的视图类型,例如Uint8Array、Int16Array、Float32Array等,用于读写特定类型的二进制数据。 Uint8Array是一种TypedArray类型,用于表示一个包含8位无符号整数的数组。它本质上是ArrayBuffer的一个视图,可以使用下标访问其中的数据,并且支持一些常见的数组操作,例如slice()、concat()等。

区别
  • 使用方式和适用场景: ArrayBuffer适用于存储任意类型的二进制数据,而TypedArray则适用于存储特定类型的二进制数据。例如,如果需要存储一个包含8位无符号整数的数组,可以使用Uint8Array,它提供了方便的数组操作和类型转换功能。如果需要存储一个复杂的二进制数据结构,可以使用ArrayBuffer,并使用DataView视图进行读写操作。

  • 性能: 在处理大量二进制数据时,使用TypedArray视图可能比使用DataView视图和Buffer对象更高效一些,因为它们可以将数据存储在连续的内存块中,避免了内存碎片问题。

互转

ArrayBuffer -> TypedArray

通过将ArrayBuffer实例传入TypedArray的构造函数,可以得到TypedArray实例

const arrayBuffer = new ArrayBuffer(32)
const unit8Array = new Unit8Array(arrayBuffer)

TypedArray -> ArrayBuffer

TypedArray可以通过实例的buffer属性得到ArrayBuffer实例

const unit8Array = new Unit8Array(arrayBuffer)
const ab = unit8Array.buffer