我最近在剪贴板和 Microsoft Excel 中遇到了这个问题
随着用于数据科学的多语言内容不断增加,不再采用 utf-8 的安全方法(在我的例子中,excel 采用 UTF-16,因为我的大部分数据都包含繁体中文(普通话) ?)。
根据Microsoft Docs,Windows 中使用了以下 BOM:
|----------------------|-------------|-----------------------|
| Encoding | Bom | Python encoding kwarg |
|----------------------|-------------|-----------------------|
| UTF-8 | EF BB BF | 'utf-8' |
| UTF-16 big-endian | FE FF | 'utf-16-be' |
| UTF-16 little-endian | FF FE | 'utf-16-le' |
| UTF-32 big-endian | 00 00 FE FF | 'utf-32-be' |
| UTF-32 little-endian | FF FE 00 00 | 'utf-32-le' |
|----------------------|-------------|-----------------------|
我想出了以下方法,似乎可以很好地使用文件开头的字节顺序标记检测编码:
def guess_encoding_from_bom(filename, default='utf-8'):
msboms = dict((bom['sig'], bom) for bom in (
{'name': 'UTF-8', 'sig': b'\xEF\xBB\xBF', 'encoding': 'utf-8'},
{'name': 'UTF-16 big-endian', 'sig': b'\xFE\xFF', 'encoding':
'utf-16-be'},
{'name': 'UTF-16 little-endian', 'sig': b'\xFF\xFE', 'encoding':
'utf-16-le'},
{'name': 'UTF-32 big-endian', 'sig': b'\x00\x00\xFE\xFF', 'encoding':
'utf-32-be'},
{'name': 'UTF-32 little-endian', 'sig': b'\xFF\xFE\x00\x00',
'encoding': 'utf-32-le'}))
with open(filename, 'rb') as f:
sig = f.read(4)
for sl in range(3, 0, -1):
if sig[0:sl] in msboms:
return msboms[sig[0:sl]]['encoding']
return default
# Example using python csv module
def excelcsvreader(path, delimiter=',',
doublequote=False, quotechar='"', dialect='excel',
escapechar='\\', fileEncoding='UTF-8'):
filepath = os.path.expanduser(path)
fileEncoding = guess_encoding_from_bom(filepath, default=fileEncoding)
if os.path.exists(filepath):
# ok let's open it and parse the data
with open(filepath, 'r', encoding=fileEncoding) as csvfile:
csvreader = csv.DictReader(csvfile, delimiter=delimiter,
doublequote=doublequote, quotechar=quotechar, dialect=dialect,
escapechar='\\')
for (rnum, row) in enumerate(csvreader):
yield (rnum, row)
我意识到这需要打开文件以读取两次(一次是二进制文件,一次是编码文本),但在这种特殊情况下,API 并没有真正让其他操作变得容易。
无论如何,我认为这比简单地假设 utf-8 更可靠,而且显然自动编码检测不起作用......