Postgres COPY FROM文件抛出unicode错误,而引用的字符显然不在文件中

时间:2015-02-25 01:24:30

标签: python postgresql unicode grep

首先,感谢Stack Overflow上的所有人过去,现在和将来的帮助。你们已经把我从灾难中拯救出来(我自己的设计和其他方面)太多次了。

本期是我公司决定从Microsoft SQL Server 2005数据库过渡到PostgreSQL 9.4的一部分。我们一直在关注Postgres wiki(https://wiki.postgresql.org/wiki/Microsoft_SQL_Server_to_PostgreSQL_Migration_by_Ian_Harding)上的注释,这些是我们对相关表格所遵循的步骤:

  1. 在Windows客户端上下载表数据:

    bcp "Carbon.consensus.observations" out "Carbon.consensus.observations" -k -S [servername] -T -w
    
  2. 复制到Postgres服务器[运行CentOS 7]

  3. 在Postgres服务器上运行Python预处理脚本以更改编码并清理:

    import sys
    import os
    import re
    import codecs
    import fileinput
    
    base_path = '/tmp/tables/'
    cleaned_path = '/tmp/tables_processed/'
    files = os.listdir(base_path)
    
    for filename in files:
    
        source_path = base_path + filename
        temp_path = '/tmp/' + filename
        target_path = cleaned_path + filename
    
        BLOCKSIZE = 1048576 # or some other, desired size in bytes
        with open(source_path, 'r') as source_file:
            with open(target_path, 'w') as target_file:
                start = True
                while True:
                    contents = source_file.read(BLOCKSIZE).decode('utf-16le')
                    if not contents:
                        break
                    if start:
                        if contents.startswith(codecs.BOM_UTF8.decode('utf-8')):
                            contents = contents.replace(codecs.BOM_UTF8.decode('utf-8'), ur'')
                    contents = contents.replace(ur'\x80', u'')
                    contents = re.sub(ur'\000', ur'', contents)
                    contents = re.sub(ur'\r\n', ur'\n', contents)
                    contents = re.sub(ur'\r', ur'\\r', contents)
                    target_file.write(contents.encode('utf-8'))
                    start = False
    
        for line in fileinput.input(target_path, inplace=1):
            if '\x80' in line:
                line = line.replace(r'\x80', '')
            sys.stdout.write(line)
    
  4. 执行SQL加载表:

    COPY consensus.observations FROM '/tmp/tables_processed/Carbon.consensus.observations';
    
  5. 问题是COPY命令失败并出现unicode错误:

    [2015-02-24 19:52:24] [22021] ERROR: invalid byte sequence for encoding "UTF8": 0x80
    Where: COPY observations, line 2622420: "..."
    

    鉴于这很可能是因为表中的数据不好(也包含合法的非ASCII字符),我试图在上下文中找到实际的字节序列,我不能这样做在任何地方找到它(看看有问题的行,正则表达式替换字符作为预处理的一部分,等等)。作为参考,这个grep什么都不返回:

    cat /tmp/tables_processed/Carbon.consensus.observations | grep --color='auto' -P "[\x80]"
    

    我在追踪这个字节序列在上下文中的位置时做错了什么?

1 个答案:

答案 0 :(得分:0)

我建议将 SQL 文件(看似 /tmp/tables_processed/Carbon.consensus.observations )加载到具有 hex的编辑器中模式。这应该允许您在上下文中查看它(取决于确切的编辑器)。

gVim (或基于终端的 Vim )是我推荐的一个选项。

例如,如果我在 gVim 中打开包含此内容的 SQL 副本文件:

1   1.2
2   1.1
3   3.2

我可以通过命令%!xxd(在 gVim 或终端 Vim 中)或菜单选项工具>将其转换为十六进制模式。转换为HEX

产生这个显示:

0000000: 3109 312e 320a 3209 312e 310a 3309 332e  1.1.2.2.1.1.3.3.
0000010: 320a                                     2.

然后,您可以运行%!xxd -r将其转换回来,或者菜单选项工具>转换回来

注意:这实际上修改了文件,因此建议对原始文件的副本执行此操作,以防万一以某种方式写入更改(您必须明确保存缓冲区)在 Vim )。

这样,你可以在左边看到十六进制序列,在右边看到它们的 ASCII 等价物。如果您搜索80,您应该可以在上下文中查看它。使用 gVim 时,两种模式的行编号会有所不同,如本示例所示。

但是,您发现的第一个80可能就是那条线,因为如果有更早的那条,那么它很可能会失败。

另一个可能有助于我过去使用过的工具是图形十六进制编辑器GHex。由于这是一个GNOME项目,不太确定它是否可以与 CentOS 一起使用。 wxHexEditor据说可以与 CentOS 一起工作,看起来很有希望从网站上看,虽然我还没有使用它。它是作为大量文件"的十六进制编辑器,所以如果您的 SQL 文件很大,那么可能就是这样。