postgresql win11 下面导出的 sql 文件乱码问题 utf16 to gbk

55 天前
 sbmzhcn

当时不太会用 postgresql 在 windows 下面的 powershell 直接导出为 sql 文件,没看里面的内容,最近需要恢复数据发现内容中中文全是乱码,英文是正常的。

尝试了很多方法后,发现文件是 utf16 编码的Little-endian UTF-16 Unicode text, with very long lines, with CRLF line terminators

使用下面的命令可以导出为 gbk 编码,部分中文可以还原,但还是有相当多的字符无法还原

iconv -f UTF-16 -t GBK < export.sql > out.sql

涉及到相关问题的,只有这个文章有所说明 https://www.cnblogs.com/xyb930826/p/4657462.html

尝试了其中 C 代码,仍然是有相同的问题,有些编码无法转换成功

使用 python 重写了代码,实现后,仍然是部分中文无法解码成功

def read_map():
    map_value = {}
    try:
        with open("UnicodeToGBK.txt", "r") as f:
            for line in f:
                utf_str, gbk_str = line.strip().split()
                utf_id = int(utf_str, 16)
                gbk_id = int(gbk_str, 16)
                map_value[utf_id] = gbk_id
    except IOError:
        print("Error reading mapping file!")
        return None
    return map_value

def convert_utf16_to_gbk1(input_file, output_file):
    map_value = read_map()
    if map_value is None:
        print("Convert Failed!")
        return

    try:
        with open(input_file, "rb") as f_in, open(output_file, "wb") as f_out:
            # 跳过 BOM
            bom = f_in.read(2)
            if bom != b'\xff\xfe':
                f_in.seek(0)

            while True:
                ch = f_in.read(1)
                cl = f_in.read(1)
                if not ch or not cl:
                    break

                ch = ord(ch)
                cl = ord(cl)

                if ch > 0x7f and cl == 0x00:
                    ch2 = ord(f_in.read(1))
                    cl2 = ord(f_in.read(1))
                    f_out.write(bytes([ch, ch2]))
                elif ch <= 0x7f and cl == 0x00:
                    f_out.write(bytes([ch]))
                else:
                    utf = cl * 256 + ch
                    gbk = map_value.get(utf, 0)
                    f_out.write(bytes([gbk // 256, gbk % 256]))

    except IOError:
        print("Error processing files!")
        return

    print("Conversion completed successfully!")

def convert_utf16_to_gbk(input_file, output_file):
    map_value = read_map()
    if map_value is None:
        print("Convert Failed!")
        return

    try:
        with open(input_file, "rb") as f_in, open(output_file, "wb") as f_out:
            # 跳过 BOM
            bom = f_in.read(2)
            if bom != b'\xff\xfe':
                f_in.seek(0)

            while True:
                low_byte = f_in.read(1)
                high_byte = f_in.read(1)
                if not low_byte or not high_byte:
                    break

                low_byte = ord(low_byte)
                high_byte = ord(high_byte)

                # 正确处理小端序 UTF-16
                utf = (high_byte << 8) | low_byte

                if utf in map_value:
                    gbk = map_value[utf]
                    f_out.write(bytes([gbk // 256, gbk % 256]))
                elif utf <= 0x7f:
                    # ASCII 字符
                    f_out.write(bytes([utf]))
                else:
                    # 处理未映射的字符
                    print(f"Unable to convert UTF-16 character: U+{utf:04X}", chr(utf))
                    # hex_str = f"\\u{utf:04X}"
                    # f_out.write(hex_str.encode('ascii'))
                    # f_out.write(bytes.fromhex("E046E160"))  # 或者选择其他替代字符

    except IOError:
        print("Error processing files!")
        return

    print("Conversion completed successfully!")

if __name__ == "__main__":
    convert_utf16_to_gbk("export.sql", "out.sql")

比如 UnicodeToGBK.txt 的对应关系如下

90C5	DBA4
90C6	E042
90C7	DBA8
90C8	E043
90C9	E044
90CA	BDBC
90CB	E045
90CC	E046
90CD	E047
90CE	C0C9
90CF	DBA3
90D0	DBA6
90D1	D6A3

错误的字节

Unable to convert UTF-16 character: U+E046 
Unable to convert UTF-16 character: U+E160 
Unable to convert UTF-16 character: U+E1E2 
Unable to convert UTF-16 character: U+20AC €
Unable to convert UTF-16 character: U+E0A5 
Unable to convert UTF-16 character: U+E195 
Unable to convert UTF-16 character: U+E218 
Unable to convert UTF-16 character: U+20AC €
Unable to convert UTF-16 character: U+E1EC 
Unable to convert UTF-16 character: U+20AC €
Unable to convert UTF-16 character: U+20AC €
Unable to convert UTF-16 character: U+E1BC 
Unable to convert UTF-16 character: U+20AC €
Unable to convert UTF-16 character: U+20AC €
Unable to convert UTF-16 character: U+E11E 
Unable to convert UTF-16 character: U+E1C0 
Unable to convert UTF-16 character: U+E57D 
Unable to convert UTF-16 character: U+20AC €
Unable to convert UTF-16 character: U+E21A 
Unable to convert UTF-16 character: U+E6E7 
Unable to convert UTF-16 character: U+E11C 
Unable to convert UTF-16 character: U+20AC €
Unable to convert UTF-16 character: U+20AC €
Unable to convert UTF-16 character: U+E11C 
Unable to convert UTF-16 character: U+20AC €
Unable to convert UTF-16 character: U+E6E7 

论坛上技术人员较多,有没有遇到过相关问题,不吝赐教

1611 次点击
所在节点    程序员
14 条回复
ntedshen
55 天前
ps 里用输出重定向导出 sql 。。。
乱码才是正常情况,因为是 ps 转换的 utf16le 而不是 pg 转换的。。。

话说默认不该是 utf8 么。。。
中文字符集怎么会有 20ac 欧元符号,然后 e000-f8ff 是私有段,可以在一些 iconfont 里看到。。。
sleepm
55 天前
那就换访问方式导出
用 dbeaver
或者 从另一个 linux 主机远程访问数据库,导出
顺便推荐个工具,虽然不是我写的
https://github.com/xo/usql
sleepm
55 天前
yinmin
55 天前
你应该用 cmd 命令提示符,而不是 powershell 导出 sql
hez2010
55 天前
PowerShell 在 7.3 版本之前会把输出编码为 UTF16-LE 文本后输出,可能是这个原因导致你导出的 sql 出了编码问题,这个问题在 7.4 开始得到了修复,7.4 以后不额外转换编码而是直接写入数据。
Windows 自带的 PowerShell 还是 5.x 版本自然还是未修复之前的行为。
yinmin
55 天前
#5 正解。Windows 自带 PowerShell 5.1 ,重定向二进制数据会导致数据损坏。使用最新的 PowerShell 7.4 导致中文正常。 因此,需要使用 cmd.exe 和 PowerShell 7.4 才行。

https://learn.microsoft.com/zh-cn/powershell/module/microsoft.powershell.core/about/about_redirection?view=powershell-7.4 的内容:从本机命令重定向二进制数据
从 PowerShell 7.4 开始,PowerShell 将原生命令的 stdout 流重定向到文件时,或者将字节流数据通过管道传输到原生命令的 stdin 流时,保留字节流数据。

https://learn.microsoft.com/zh-cn/powershell/module/microsoft.powershell.core/about/about_redirection?view=powershell-5.1 的内容: 重定向二进制数据
PowerShell 不支持重定向二进制数据。 如果重定向字节流数据,PowerShell 会将数据视为字符串。 此重定向会导致数据损坏。
adoal
55 天前
除了前面友们说的 PowerShell 问题,另外问一下 OP ,你为啥执着于转成 GBK ?如果 UTF-16 里的内容是对的,转到 UTF-8 不好么? GBK 的字符集比较小,从基于 Unicode 的编码转过来有转丢的很正常。
bthulu
55 天前
可以先恢复到 utf16 编码的数据库里, 再重新导出一遍
jackmod
55 天前
我依然不懂 pg 这么个新东西为什么不在一切环境下把所有东西默认设定成 utf8 。
已经被默认使用客户端控制台编码这个骚决定坑过好多次了。
zjsxwc
55 天前
数据库运维不建议使用 windows 系统,一大原因就是这种编码问题。
mingl0280
55 天前
如果是全文件都是 UTF-16 ,直接 Notepad++打开转码就行。
sbmzhcn
49 天前
这个问题怎么产生的我也不太清楚,各种方法都试了,绝对不是随便转码就能成功的,我也找到一些高手尝试解决,无法搞定,目前是转为 gb18030 是效果最好的,几乎 90%的字符是可以转换成功的,但其它任何编码都不行。
sbmzhcn
49 天前
@hez2010 Windows 终端 版本: 1.20.11781.0 这个损坏了,是不是 没有办法修复了。
sbmzhcn
49 天前
终端版本是这个 $psversiontable
Name Value
---- -----
PSVersion 5.1.22621.4249
PSEdition Desktop
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
BuildVersion 10.0.22621.4249
CLRVersion 4.0.30319.42000
WSManStackVersion 3.0
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1084064

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX