问题描述
大多数情况下,应该能够更改 UTF-8 的编码 通过简单的 iconv 调用将文件转换为 Latin-1 (ISO-8859-1) 编码,例如:
iconv -c -f utf-8 -t ISO-8859-1//TRANSLIT
然而,这无法正确处理重音字符。考虑 例如:
$ echo $LC_ALL
C
$ cat Gonzalez.txt
González,M.
$ file Gonzalez.txt
Gonzalez.txt: UTF-8 Unicode text
$ iconv -c -f utf-8 -t ISO-8859-1//TRANSLIT < Gonzalez.txt > out
$ file out
out: ASCII text
$ cat out
Gonzalez,M.
我尝试了上述的各种变体,但没有一个正确处理 带重音的“a”,重点是 Latin-1 确实有带重音的“a”。
确实,uconv
确实正确处理了这种情况:
$ uconv -x Any-Accents -f utf-8 -t l1 < Gonzalez.txt > out
$ file out
out: ISO-8859 text
在 emacs 中打开文件或
Sublime 正确显示了带重音的“a”。同样的事情使用 -x nfc
。
不幸的是,我的目标环境不允许使用“uconv”的解决方案, 所以我正在寻找一个使用 iconv 或 python3 的简单解决方案。
python3 尝试
到目前为止,我尝试使用 python3 还没有成功。 例如,以下内容:
import sys
import fileinput # allows file to be specified or else reads from STDIN
for line in fileinput.input():
l=line.encode("latin-1","replace")
sys.stdout.buffer.write(l)
产生:
Gonza?lez,M.
(这是一个字面意思的“?”。)
我尝试了各种其他的 python3 可能性,但都没有成功。
请注意,我已经查看了许多关于此主题的 SO 问题,但使用 iconv 或 python3 的答案无法正确处理 Gonzalez.txt。
解决方法
有两种方法可以在 Unicode 中对 A WITH ACUTE ACCENT 进行编码。
一种是使用组合字符,如下面的 Python 内置 ascii
函数所示:
>>> ascii('á')
"'\\xe1'"
但您也可以在无重音字母 a
后使用组合重音符号:
>>> ascii('á')
"'a\\u0301'"
根据显示的应用程序,这两种变体可能看起来无法区分(在我的终端中,后者看起来有点奇怪,因为重音太大了)。
现在,Latin-1 有一个带重音的字母 a
,但没有组合重音,所以这就是为什么在用 errors="replace"
编码时,尖音变成问号的原因。
幸运的是,您可以在两种变体之间自动切换。
不赘述(这里有很多的细节),Unicode定义了两种规范化形式,称为composed和decomposed,分别缩写为NFC和NFD .
在 Python 中,您可以使用标准库模块 unicodedata
:
>>> import unicodedata as ud
>>> ascii(ud.normalize('NFD','á'))
"'a\\u0301'"
>>> ascii(ud.normalize('NFC','á'))
"'\\xe1'"
在您的具体情况下,您可以将输入字符串转换为 NFC 形式,这将增加拉丁 1 字符的覆盖范围:
>>> n = 'Gonza\u0301lez,M.'
>>> print(n)
González,M.
>>> n.encode('latin1',errors='replace')
b'Gonza?lez,M.'
>>> ud.normalize('NFC',n).encode('latin1',errors='replace')
b'Gonz\xe1lez,M.'