NodeJs / Bun 分析文件编码 并将 各种编码格式 转为 另一个编码格式 ( 比如: GB2312→UTF-8, UTF-8→GB2312)
版本号
"iconv-lite": "^0.6.3",
"chardet": "^2.0.0",
github.com/runk/node-chardet
可以识别文本是 哪种编码
( 大文件截取一部分进行分析,速度比较快 )
let bun_file_obj = Bun.file(full_file_path)
let file_bytes = await bun_file_obj.bytes()
// 截取
let slice_end_index = file_bytes.length > 300 ? 300 : file_bytes.length
let type_arr = chardet.analyse(file_bytes.slice(0, slice_end_index))
// 取第一个结果, 可信度高
let type = type_arr[0]
console.log(full_file_path + "___" + type.name + "___" + type?.lang);
github.com/ashtuchkin/iconv-lite
支持所有的编码类型的转换 !!!
iconv.decode(…)
将任意编码的文本转为 JavaScript 字符串,
参数 Buffer
, 返回值 string
如果编码写错, 会导致 乱码+乱码, 彻底废了.
解析之前可以用 chardet
识别一下编码格式
大文件建议截取一部分进行分析,速度比较快
提前知道编码,就不需要它了,
iconv.encode(…)
将 JavaScript 字符串转为另一个编码
参数 string
, 返回值 Buffer
比如 GB2312
let data = iconv.encode("哈哈哈", "GB2312");
fs.writeFileSync("./test.txt", data);
更多用法到 iconv-lite
README 里看
代码
NodeJs 的 fs.readFile
回调类型就是 Buffer
,可直接用,
Bun 有两个方法
bun_file_obj.bytes()
返回类型是 Uint8Array
( 可以用, 但 TypeScript 类型会报错, Buffer
继承自 Uint8Array
, 防止源码中用了 Buffer
特有的 api .建议转一下类型 )
bun_file_obj.arrayBuffer()
返回类型是 ArrayBuffer
两个都不可以直接用,
所以先用 Buffer.from(...)
将这两个类型转为 Buffer
,
完整代码
import fs from 'node:fs';
import iconv from 'iconv-lite';
import path from 'node:path';
import chardet from 'chardet';
let dir_path = "D:\\Desktop\\新建文件夹2\\新建文件夹"
// 读取文件夹中所有文件名
let file_name_list = fs.readdirSync(dir_path)
convert_dir_files()
// (
// 循环中,一定要用 let 定义变量,
// 如果用 var, 由于回调函数不是立即执行,
// 读取变量时,已经不是当时的值
// )
async function convert_dir_files() {
for (const item of file_name_list) {
// 拼接完整的路径
let full_file_path = path.resolve(dir_path, item)
// Bun 官方首推的 文件IO 方式, 用起来更舒服
let bun_file_obj = Bun.file(full_file_path)
// 读取文件 ( Uint8Array )
let file_bytes = await bun_file_obj.bytes()
// 分析编码 ( 截取一小段 提高性能 )
let slice_end_index = file_bytes.length > 300 ? 300 : file_bytes.length
let type_arr = chardet.analyse(file_bytes.slice(0, slice_end_index))
let type = type_arr[0]
// 只处理 GB2312
if (type.name == "GB18030" && type?.lang == "zh") {
let file_buffer = Buffer.from(file_bytes)
// 将 GB2312 解码 为 JavaScript 字符串类型 (js默认编码是 utf16)
let de_file_data = iconv.decode(file_buffer, 'GB2312').toString();
// 写入文件 ( bun_file_obj.type 默认就是 utf8 编码, 比 utf16 更省空间)
await bun_file_obj.write(de_file_data)
// 如果要继续操作此文件, write 一定要 await,
// 因为在写入文件时,文件禁止被其他操作,比如下面的 rename 就会报错
} else if (type.name == "UTF-8") {
// 不做任何处理
} else {
console.log("未知编码:" + "___" + item + "___" + type.name + "___" + type?.lang);
}
// 处理一下文件名
let new_file_path = path
.resolve(
dir_path,
item
.replace(/[\t\r\f\n\s]*/g, "")
)
// 如果 2 次文件名不同,就重命名
if (full_file_path != new_file_path) {
// 重命名
fs.rename(full_file_path, new_file_path, (err) => {
if (err) throw err;
})
}
}
}
检查编码
用 Bun
执行
byte 进行截取后, 100 个 10M 的文件, 可以在瞬间分析完毕
import fs from 'node:fs';
import path from 'node:path';
import chardet from 'chardet';
let dir_path = "D:\\Desktop\\新建文件夹2\\新建文件夹"
let dir1_path = "D:\\Desktop\\新建文件夹2\\utf8"
let dir2_path = "D:\\Desktop\\新建文件夹2\\gb2312"
if (!fs.existsSync(dir1_path)) {
fs.mkdirSync(dir1_path)
}
if (!fs.existsSync(dir2_path)) {
fs.mkdirSync(dir2_path)
}
// 读取文件夹中所有文件名
let file_name_list = fs.readdirSync(dir_path)
for (const item of file_name_list) {
let full_file_path = path.resolve(dir_path, item)
let bun_file_obj = Bun.file(full_file_path)
// 读取文件
let file_bytes = await bun_file_obj.bytes()
// 截取一定长度的数据, 进行分析, 文件太大影响分析速度
let slice_end_index = file_bytes.length > 300 ? 300 : file_bytes.length
let type_arr = chardet.analyse(file_bytes.slice(0, slice_end_index))
// 取第一个结果, 可信度高
let type = type_arr[0]
console.log(item + "___" + type.name + "___" + type?.lang);
// 移动文件
if (type.name == "UTF-8") {
fs.renameSync(full_file_path, path.resolve(dir1_path, item))
} else if (type.name == "GB18030" && type?.lang == "zh") {
fs.renameSync(full_file_path, path.resolve(dir2_path, item))
} else {
// 其他类型不移动
}
}