将带有未定义字符的字节文字写入CSV文件(Python 3)

时间:2015-02-13 23:39:06

标签: python python-3.x character-encoding

使用Python 3.4.2,我想获得一个网站的一部分。根据元标记,该网站使用iso-8859-1进行编码。我想将一个部分(以及其他部分)写入CSV文件。

但是,此部分包含一个十六进制值为0x8b的未定义字符。为了尽可能保留部件,我想将它原样写入CSV文件。但是,Python不允许我这样做。

这是一个最小的例子:

import urllib.request
import urllib.parse
import csv

if __name__ == "__main__":
    with open("bytewrite.csv", "w", newline="") as csvfile:
        a = b'\x8b' # byte literal by urllib.request
        b = a.decode("iso-8859-1")

        w = csv.writer(csvfile)
        w.writerow([b])

这是输出:

Traceback (most recent call last):
  File "D:\Eigene\Dateien\Code\Python\writebyte.py", line 12, in <module>
    w.writerow([b])
  File "C:\Python34\lib\encodings\cp1252.py", line 19, in encode
    return codecs.charmap_encode(input,self.errors,encoding_table)[0]
UnicodeEncodeError: 'charmap' codec can't encode character '\x8b' in position 0: character maps to <undefined>

最终,我手动完成了。它只是使用Notepad ++进行复制和粘贴,并根据十六进制编辑器正确插入了值。但是我怎么能用Python 3做到这一点?为什么Python甚至关心0x8b代表什么,而不仅仅是将其写入文件?

根据iso8859_1.py中的cp1252.py(以及C:\Python34\lib\encodings\# iso8859_1.py '\x8b' # 0x8B -> <control> # cp1252.py '\u2039' # 0x8B -> SINGLE LEFT-POINTING ANGLE QUOTATION MARK ,查找表似乎没有干扰,这进一步让我感到恼火:

{{1}}

2 个答案:

答案 0 :(得分:1)

引自csv文档:

  

由于open()用于打开CSV文件进行读取,因此该文件将由   默认使用系统默认编码解码为unicode(请参阅   是locale.getpreferredencoding())。使用不同的文件解码文件   编码,使用open的编码参数:

import csv
with open('some.csv', newline='', encoding='utf-8') as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)
  

同样适用于写入系统默认编码以外的其他内容:在打开输出文件时指定encoding参数。

正在发生的事情是您已从iso-8859-1解码为Unicode,但getpreferredencoding()返回cp1252并且该编码不支持Unicode字符\x8b

更正了最小例子:

import csv
with open('bytewrite.csv', 'w', encoding='iso-8859-1', newline='') as csvfile:
    a = b'\x8b'
    b = a.decode("iso-8859-1")
    w = csv.writer(csvfile)
    w.writerow([b])

答案 1 :(得分:0)

您对encodings中查找表的解释是不正确的。您列出的代码:

# iso8859_1.py
    '\x8b'     #  0x8B -> <control>
# cp1252.py
    '\u2039'   #  0x8B -> SINGLE LEFT-POINTING ANGLE QUOTATION MARK

告诉你两件事:

  1. 如何将unicode字符'\ x8b'映射到iso8859-1中的字节,它只是一个控制字符。
  2. 如何将unicode字符'\ u2039'映射到cp1252中的字节,这是一个标点符号:<< / li>

    这个告诉你如何将unicode字符'\ x8b'映射到cp1252中的字节,这是你要做的。

    问题的根源是“\ x8b”不是有效的iso8859-1字符。看看这里的表格:

    http://en.wikipedia.org/wiki/ISO/IEC_8859-1#Codepage_layout

    8b未定义,因此它只是作为控制字符解码。在它被解码后我们在unicode的土地上,什么是0x8b?这有点难以找到,但它是在unicode数据库here中定义的:

    008B;<control>;Cc;0;BN;;;;;N;PARTIAL LINE FORWARD;;;;
    

    现在,CP1252是否具有此控制字符,“PARTIAL LINE FORWARD”?

    http://en.wikipedia.org/wiki/Windows-1252#Code_page_layout

    不,它没有。因此,在尝试在CP1252中对其进行编码时会出错。

    不幸的是,没有好的解决方案。一些想法:

    1. 猜猜页面的实际编码是什么。它可能是CP1252,而不是ISO-8859-1,但谁知道呢。它甚至可能包含混合的编码或错误编码的数据(mojibake)。您可以使用chardet来猜测编码,或者强制此网址在您的程序中使用CP1252(覆盖元标记所说的内容),或者您可以尝试使用一系列编解码器并使用第一个解码和编码的编解码器。编码成功。

    2. 使用某种有问题的字符like this的映射来修复输入文本或解码的unicode字符串。这大部分时间都可以工作,但是如果你试图“修复”没有意义的数据,它将无声地失败或做一些奇怪的事情。

    3. 请勿尝试从ISO-8859-1转换为CP1252,因为它们彼此不兼容。如果您使用可能更好的UTF-8。

    4. 使用编码错误处理程序。有关处理程序列表,请参阅this table。使用xmlcharrefreplacebackslashreplace将保留信息(但需要您在解码时执行额外的步骤),而replaceignore将默默跳过错误的字符。< / p>

    5. 由旧编码引起的这些类型的问题实际上很难解决,并且没有完美的解决方案。这就是为什么发明unicode的原因。