如何将ISO-8859-1字符正确替换为UTF-8



我想替换下面文件中的ISO-8859-1字符,使其对UTF-8编码有效。

<HTML>
<HEAD>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
</HEAD>
<BODY>
<A NAME="top"></A>
<TABLE border=0 width=609 cellspacing=0 cellpadding=0>
<TR><td rowspan=2><img src="http://www.example.com" width=10></td>
<TD width=609 valign=top>
<p>'</p>
<p>*</p>
<p>-</p>
<p>—</p>
<p>§</p>
<p>«</p>
<p>»</p>
<p>¿</p>
<p>Á</p>
</TD>
</TR>
</TABLE>
</body>
</html>

经过一些研究,我发现这个问题与locale语言有关,我能够构建这个awk程序,但只替换了前两个字符('*(

LC_ALL=ISO_8859-1 awk '{
gsub(/charset=iso-8859-1/, "charset=UTF-8"  ,  $0)
gsub(/47/, "\&apos;"  ,  $0)
gsub(/*/, "\&ast;"      ,  $0)
gsub(/–/, "\&ndash;"    ,  $0)
gsub(/—/, "\&mdash;"    ,  $0)
gsub(/§/, "\&sect;"     ,  $0)
gsub(/«/, "\&laquo;"    ,  $0)
gsub(/»/, "\&raquo;"    ,  $0)
gsub(/¿/, "\&iquest;"   ,  $0)
gsub(/Á/, "\&Aacute;"   ,  $0)
print
}' t.html | iconv -f ISO_8859-1 -t UTF-8

这是当前输出(显示在下面的部分输出,仅显示受程序影响的行(:

<p>&apos;</p>
<p>&ast;</p>
<p>-</p>
<p>-</p>
<p>§</p>
<p>«</p>
<p>»</p>
<p>¿</p>
<p>Á</p>

预期输出为:

<p>&ast;</p>
<p>&ndash;</p>
<p>&mdash;</p>
<p>&sect;</p>
<p>&laquo;</p>
<p>&raquo;</p>
<p>&iquest;</p>
<p>&Aacute;</p>

我已经使用sed尝试过类似的代码,但问题相同。

如何解决此问题?

以下区域设置配置:

***Ubuntu 18.04.1 LTS
$ locale
LANG=C.UTF-8
LANGUAGE=
LC_CTYPE="C.UTF-8"
LC_NUMERIC="C.UTF-8"
LC_TIME="C.UTF-8"
LC_COLLATE="C.UTF-8"
LC_MONETARY="C.UTF-8"
LC_MESSAGES="C.UTF-8"
LC_PAPER="C.UTF-8"
LC_NAME="C.UTF-8"
LC_ADDRESS="C.UTF-8"
LC_TELEPHONE="C.UTF-8"
LC_MEASUREMENT="C.UTF-8"
LC_IDENTIFICATION="C.UTF-8"
LC_ALL=

此问题可能是由于输入文件和awk脚本之间的编码不匹配造成的。

请首先注意,ISO-8859-1和Windows-1252之间可能存在(非常常见的(混淆。原始文章中的html示例包含em/en-dash字符,这些字符不属于ISO-8859-1布局的一部分,因此它肯定使用了另一种编码,可能是Windows-1252(它是ISO-8859--1的超集,包括短划线字符(,因为OP报告通过Windows子系统层使用Ubuntu。

然后我假设html输入文件确实是用Windows-1252编码的。因此,非ASCII字符(代码点≥128(只使用一个字节。

如果awk程序是从UTF-8编码的文件加载的,或者甚至是直接在使用UTF-8内码的终端窗口中键入的,那么程序中嵌入的正则表达式和文字字符串也将使用UTF-8编码。因此,非ASCII字符使用多个字节。

例如,字符§(代码点167=0xA7(在Windows-1252中由字节A7表示,在UTF-8中由字节序列C2 A7表示。如果在UTF-8编码的awk程序中使用gsub(/§/, "S"),那么awk会在只包含A7的输入文件中查找序列C2 A7。它不会匹配。除非你(不(幸运地在你的§之前挂了一个字符Â(代码点194=0xC2(。

更改语言环境在这里没有帮助,因为它只是告诉awk如何解析其输入(数据和程序(,而您在这里需要的是对数据或正则表达式进行代码转换。为了实现这一点,您必须能够独立于程序的区域设置来指定数据的区域设置,这是不受支持的。

因此,假设您的系统设置为UTF-8语言环境,并且您的awk脚本使用该语言环境(无论是从文件加载还是在终端中键入(,您可以使用以下几种方法将输入文件和正则表达式对齐在同一编码上,以便gsub按预期工作。

请注意,这些建议坚持使用您的第一个awk命令,因为它是问题的根源。只有当您有意不将输入中的所有特殊字符转换为html实体时,才需要iconv的最后一个管道。否则,awk的输出是纯ASCII,因此已经符合UTF-8。

选项1:将输入文件从Windows-1252转换为UTF-8

在任何情况下都不需要在此之后的另一个CCD_ 15步骤。

iconv -f WINDOWS-1252 t.html | awk '{
gsub(/charset=iso-8859-1/, "charset=UTF-8")
gsub(/47/, "\&apos;")
gsub(/*/, "\&ast;")
gsub(/–/, "\&ndash;")
gsub(/—/, "\&mdash;")
gsub(/§/, "\&sect;")
gsub(/«/, "\&laquo;")
gsub(/»/, "\&raquo;")
gsub(/¿/, "\&iquest;")
gsub(/Á/, "\&Aacute;")
print
}'

选项2:将awk程序从UTF-8转换为Windows-1252

因为awk程序可能也想玩得开心。让我们使用过程替换。

awk -f <(iconv -t WINDOWS-1252 <<'EOS'
{
gsub(/charset=iso-8859-1/, "charset=UTF-8")
gsub(/'/, "\&apos;")
gsub(/*/, "\&ast;")
gsub(/–/, "\&ndash;")
gsub(/—/, "\&mdash;")
gsub(/§/, "\&sect;")
gsub(/«/, "\&laquo;")
gsub(/»/, "\&raquo;")
gsub(/¿/, "\&iquest;")
gsub(/Á/, "\&Aacute;")
print
}
EOS
) t.html

选项3:将awk/schell脚本保存在Windows-1252中编码的文件中

使用您最喜欢的工具。

选项4:将终端会话的编码切换到Windows-1252

当然,如果您在终端中键入/粘贴awk命令。

请注意,这与设置区域设置(LC_CTYPE(不同。我不知道有什么方法可以通过编程实现这一点。如果有人知道,请随意贡献。

选项5:在awk程序中完全避免使用非ASCII字符

在我看来,这听起来是个不错的做法。

awk '{
gsub(/charset=iso-8859-1/, "charset=UTF-8")
gsub(/47/, "\&apos;")
gsub(/*/, "\&ast;")
gsub(/226/, "\&ndash;")
gsub(/227/, "\&mdash;")
gsub(/247/, "\&sect;")
gsub(/253/, "\&laquo;")
gsub(/273/, "\&raquo;")
gsub(/277/, "\&iquest;")
gsub(/301/, "\&Aacute;")
print
}' t.html

最新更新