使用 iconv 或 python3 将 utf-8 重新编码为 Latin-1 (ISO-8859-1),保留重音字符

Use iconv or python3 to recode utf-8 to Latin-1 (ISO-8859-1) preserving accented characters

大多数人认为,应该能够更改 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.

我尝试了上述的各种变体,但 none 正确处理 带重音的“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 定义了两种规范化形式,称为 composeddecomposed,分别缩写为NFC和NFD。 在 Python 中,您可以使用标准库模块 unicodedata:

>>> import unicodedata as ud
>>> ascii(ud.normalize('NFD', 'á'))
"'a\u0301'"
>>> ascii(ud.normalize('NFC', 'á'))
"'\xe1'"

在您的具体情况下,您可以将输入字符串转换为 NFC 形式,这将增加 Latin-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.'