将具有未定义字符的字节文字写入 CSV 文件 (Python 3)
Write bytes literal with undefined character to CSV file (Python 3)
使用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\
中查找 table 似乎没有干扰,这进一步激怒了我:
# iso8859_1.py
'\x8b' # 0x8B -> <control>
# cp1252.py
'\u2039' # 0x8B -> SINGLE LEFT-POINTING ANGLE QUOTATION MARK
引自csv 文档:
Since open() is used to open a CSV file for reading, the file will by
default be decoded into unicode using the system default encoding (see
locale.getpreferredencoding()). To decode a file using a different
encoding, use the encoding argument of open:
import csv
with open('some.csv', newline='', encoding='utf-8') as f:
reader = csv.reader(f)
for row in reader:
print(row)
The same applies to writing in something other than the system default encoding: specify the encoding argument when opening the output file.
您已经从 iso-8859-1
解码为 Unicode,但是 getpreferredencoding()
returns 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])
您对 encodings
中查找 table 的解释不正确。您列出的代码:
# iso8859_1.py
'\x8b' # 0x8B -> <control>
# cp1252.py
'\u2039' # 0x8B -> SINGLE LEFT-POINTING ANGLE QUOTATION MARK
告诉你两件事:
- 如何将unicode字符'\x8b'映射到iso8859-1中的字节,它只是一个控制字符。
- 如何将unicode字符'\u2039'映射到cp1252中的字节,是一段标点符号:‹
本文不会告诉您如何将 unicode 字符 '\x8b' 映射到 cp1252 中的字节,而这正是您想要做的。
问题的根源在于“\x8b”不是有效的 iso8859-1 字符。看看这里的table:
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 中对其进行编码时会出错。
不幸的是,对此没有好的解决方案。一些想法:
猜猜页面实际上是什么编码。可能是 CP1252,而不是 ISO-8859-1,但谁知道呢。它甚至可能包含混合编码或编码不正确的数据(mojibake). You can use chardet 猜测编码,或强制此 URL 在您的程序中使用 CP1252(覆盖元标记所说的内容),或者您可以尝试一系列编解码器并使用第一个成功解码和编码的编解码器。
使用某种有问题的字符映射来修复输入文本或解码的 unicode 字符串 like this。这在大多数情况下都有效,但如果您尝试 "fix up" 没有意义的数据,则会无声地失败或做一些奇怪的事情。
不要尝试从 ISO-8859-1 转换为 CP1252,因为它们彼此不兼容。如果您使用 UTF-8 可能会更好。
使用编码错误处理程序。有关处理程序列表,请参阅 this table。使用 xmlcharrefreplace
和 backslashreplace
将保留信息(但随后需要您在解码时执行额外的步骤),而 replace
和 ignore
将默默地跳过坏字符。
这些由旧编码引起的问题真的很难解决,没有完美的解决方案。这就是发明unicode的原因。
使用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\
中查找 table 似乎没有干扰,这进一步激怒了我:
# iso8859_1.py
'\x8b' # 0x8B -> <control>
# cp1252.py
'\u2039' # 0x8B -> SINGLE LEFT-POINTING ANGLE QUOTATION MARK
引自csv 文档:
Since open() is used to open a CSV file for reading, the file will by default be decoded into unicode using the system default encoding (see locale.getpreferredencoding()). To decode a file using a different encoding, use the encoding argument of open:
import csv
with open('some.csv', newline='', encoding='utf-8') as f:
reader = csv.reader(f)
for row in reader:
print(row)
The same applies to writing in something other than the system default encoding: specify the encoding argument when opening the output file.
您已经从 iso-8859-1
解码为 Unicode,但是 getpreferredencoding()
returns 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])
您对 encodings
中查找 table 的解释不正确。您列出的代码:
# iso8859_1.py
'\x8b' # 0x8B -> <control>
# cp1252.py
'\u2039' # 0x8B -> SINGLE LEFT-POINTING ANGLE QUOTATION MARK
告诉你两件事:
- 如何将unicode字符'\x8b'映射到iso8859-1中的字节,它只是一个控制字符。
- 如何将unicode字符'\u2039'映射到cp1252中的字节,是一段标点符号:‹
本文不会告诉您如何将 unicode 字符 '\x8b' 映射到 cp1252 中的字节,而这正是您想要做的。
问题的根源在于“\x8b”不是有效的 iso8859-1 字符。看看这里的table:
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 中对其进行编码时会出错。
不幸的是,对此没有好的解决方案。一些想法:
猜猜页面实际上是什么编码。可能是 CP1252,而不是 ISO-8859-1,但谁知道呢。它甚至可能包含混合编码或编码不正确的数据(mojibake). You can use chardet 猜测编码,或强制此 URL 在您的程序中使用 CP1252(覆盖元标记所说的内容),或者您可以尝试一系列编解码器并使用第一个成功解码和编码的编解码器。
使用某种有问题的字符映射来修复输入文本或解码的 unicode 字符串 like this。这在大多数情况下都有效,但如果您尝试 "fix up" 没有意义的数据,则会无声地失败或做一些奇怪的事情。
不要尝试从 ISO-8859-1 转换为 CP1252,因为它们彼此不兼容。如果您使用 UTF-8 可能会更好。
使用编码错误处理程序。有关处理程序列表,请参阅 this table。使用
xmlcharrefreplace
和backslashreplace
将保留信息(但随后需要您在解码时执行额外的步骤),而replace
和ignore
将默默地跳过坏字符。
这些由旧编码引起的问题真的很难解决,没有完美的解决方案。这就是发明unicode的原因。