强制编码从US-ASCII到UTF-8(iconv)

eig*_*ive 55 utf-8 character-encoding iconv

我正在尝试将一堆文件从US-ASCII转码为UTF-8.

为此,我正在使用iconv:

iconv -f US-ASCII -t UTF-8 file.php > file-utf8.php
Run Code Online (Sandbox Code Playgroud)

事情是我的原始文件是US-ASCII编码,这使得转换不会发生.显然它会发生因为ASCII是UTF-8的一个子集......

http://www.linuxquestions.org/questions/linux-software-2/iconv-us-ascii-to-utf-8-or-iso-8859-15-a-705054/

引用:

在引入非ascii字符之前,不需要显示文本文件

真正.如果我在文件中引入非ASCII字符并保存它,那么假设使用Eclipse,文件编码(charset)将切换为UTF-8.

在我的情况下,我想强制iconv将文件转码为UTF-8.是否存在非ASCII字符.

注意:原因是我的PHP代码(非ASCII文件...)正在处理一些非ASCII字符串,这导致字符串不能很好地解释(法语):

Ilétait une fois ... l'hommesé©animéemythique d'Albert

Barillé(Procidis),1?

...

编辑

  • US-ASCII- - 的一部分UTF-8(见下面Ned的回答)
  • 这意味着US-ASCII文件在实际编码UTF-8
  • 我的问题来自其他地方

Ned*_*der 63

ASCII是UTF-8的子集,因此所有ASCII文件都已经过UTF-8编码.ASCII文件中的字节和"将其编码为UTF-8"所产生的字节将完全相同.他们之间没有区别,所以没有必要做任何事情.

看起来您的问题是文件实际上不是ASCII.您需要确定它们使用的编码,并正确转码.


mat*_*tpr 37

简答

  • file 只猜测文件编码可能是错误的(特别是在特殊字符只出现在大文件后期的情况下).
  • 您可以使用hexdump查看非7位ascii文本的字节,并与常见编码的代码表(iso-8859-*,utf-8)进行比较,以自行决定编码是什么.
  • iconv将使用您指定的任何输入/输出编码,无论文件的内容是什么.如果指定错误的输入编码,则输出将出现乱码.
  • 即使在运行之后iconv,file由于file尝试猜测编码的方式有限,也可能不会报告任何更改.有关具体示例,请参阅我的答案.
  • 7位ascii(aka us-ascii)在字节级别与utf-8和8位ascii扩展(iso-8859-*)相同.因此,如果您的文件只有7位字符,那么您可以将其命名为utf-8,iso-8859-*或us-ascii,因为在字节级别它们都是相同的.只有当您的文件具有7位ascii范围之外的字符时,才能谈论utf-8和其他编码(在此上下文中).

答案很长

我今天碰到了这个问题并遇到了你的问题.也许我可以添加更多信息来帮助遇到此问题的其他人.

首先,术语ASCII过载,这会导致混淆.

7位ASCII仅包含128个字符(十进制00-7F或0-127).7位ASCII也称为US-ASCII.

https://en.wikipedia.org/wiki/ASCII

UTF-8编码对前128个字符使用与7位ASCII相同的编码.因此,只包含前128个字符范围内的字符的文本文件在字节级别上是相同的,无论是用UTF-8还是7位ASCII编码.

https://en.wikipedia.org/wiki/UTF-8#Codepage_layout

术语扩展ascii(或高ascii)是指包含标准七位ASCII字符和其他字符的八位或更大字符编码.

https://en.wikipedia.org/wiki/Extended_ASCII

ISO-8859-1(又名"ISO Latin 1")是一种特定的8位ASCII扩展标准,涵盖了西欧的大多数字符.东欧语言和西里尔语有其他ISO标准.ISO-8859-1包括德语和西班牙语的Ö,é,ñ和ß字符."扩展"表示ISO-8859-1包含7位ASCII标准,并使用第8位为其添加字符.因此对于前128个字符,它在字节级别上等同于ASCII和UTF-8编码的文件.但是,当您开始处理超过前128个字符时,您在字节级别不再是UTF-8等效项,如果您希望"扩展ascii"文件是UTF-8编码,则必须进行转换.

https://en.wikipedia.org/wiki/Extended_ASCII#ISO_8859_and_proprietary_adaptations

我今天学到的一个教训是,我们不能相信file总能正确解释文件的字符编码.

https://en.wikipedia.org/wiki/File_%28command%29

该命令仅告知文件的外观,而不是它的内容(在文件查看内容的情况下).通过将一个幻数放入一个内容与之不匹配的文件中,很容易欺骗程序.因此,除特定情况外,该命令不能用作安全工具.

file在提示类型的文件中查找幻数,但这些可能是错误的,无法保证正确性. file还试图通过查看文件中的字节来猜测字符编码.基本上file有一系列测试可以帮助它猜测文件类型和编码.

我的文件是一个大型CSV文件. file将此文件报告为us-ascii编码,这是错误的.

$ ls -lh
total 850832
-rw-r--r--  1 mattp  staff   415M Mar 14 16:38 source-file
$ file -b --mime-type source-file
text/plain
$ file -b --mime-encoding source-file
us-ascii
Run Code Online (Sandbox Code Playgroud)

我的文件中有变音符号(即Ö).第一个非7位-ascii直到超过100k行才进入文件.我怀疑这就是为什么file没有意识到文件编码不是US-ASCII.

$ pcregrep -no '[^\x00-\x7F]' source-file | head -n1
102321:?
Run Code Online (Sandbox Code Playgroud)

我在Mac上,所以使用PCRE grep.使用gnu grep,您可以使用该-P选项.或者在Mac上,可以安装coreutils(通过自制软件或其他)以获得gnu grep.

我没有挖掘源代码file,并且手册页没有详细讨论文本编码检测,但我猜测file在猜测编码之前不会查看整个文件.

无论我的文件编码是什么,这些非7位ASCII字符都会破坏.我的德语CSV文件是;-separated,并且提取单个列不起作用.

$ cut -d";" -f1 source-file > tmp
cut: stdin: Illegal byte sequence
$ wc -l *
 3081673 source-file
  102320 tmp
 3183993 total
Run Code Online (Sandbox Code Playgroud)

注意cut错误,我的"tmp"文件只有102320行,第102321行有第一个特殊字符.

我们来看看这些非ASCII字符是如何编码的.我将第一个非7位-ascii转储到hexdump,进行一些格式化,删除换行符(0a)并只取前几个.

$ pcregrep -o '[^\x00-\x7F]' source-file | head -n1 | hexdump -v -e '1/1 "%02x\n"'
d6
0a
Run Code Online (Sandbox Code Playgroud)

其他方式.我知道第一个非7位ASCII字符在第102321行的第85位.我抓住那一行并告诉hexdump从位置85开始取两个字节.你可以看到特殊的(非7位ASCII)由"."表示的字符,下一个字节是"M"......所以这是一个单字节字符编码.

$ tail -n +102321 source-file | head -n1 | hexdump -C -s85 -n2
00000055  d6 4d                                             |.M|
00000057
Run Code Online (Sandbox Code Playgroud)

在这两种情况下,我们都看到了特殊字符d6.由于这个字符是德国字母Ö,我猜测ISO-8859-1应该包括这个.果然你可以看到"d6"是一个匹配(https://en.wikipedia.org/wiki/ISO/IEC_8859-1#Codepage_layout).

重要的问题...我怎么知道这个字符是Ö而不确定文件编码?答案是背景.我打开文件,阅读文本,然后确定它应该是什么字符.如果我打开它vim它显示为O,因为vim做一个更好的工作猜测的字符编码(在这种情况下)比file呢.

所以,我的文件似乎是ISO-8859-1.从理论上讲,我应该检查其余的非7位ASCII字符,以确保ISO-8859-1非常合适......没有什么能够迫使程序在编写文件时只使用单个编码磁盘(除了良好的举止).

我将跳过检查并转到转换步骤.

$ iconv -f iso-8859-1 -t utf8 source-file > output-file
$ file -b --mime-encoding output-file
us-ascii
Run Code Online (Sandbox Code Playgroud)

嗯. file仍然告诉我这个文件即使在转换后也是US-ASCII.我们再来看看吧hexdump.

$ tail -n +102321 output-file | head -n1 | hexdump -C -s85 -n2
00000055  c3 96                                             |..|
00000057
Run Code Online (Sandbox Code Playgroud)

绝对是一种变化.请注意,我们有两个字节的非7位ASCII(由右边的"."表示),现在是两个字节的十六进制代码c3 96.如果我们看看,似乎我们现在有UTF-8(c3 96是UTF-8中Ö的正确编码) http://www.utf8-chartable.de/

file仍然报告我们的文件us-ascii?嗯,我认为这可以追溯到file没有查看整个文件以及第一个非7位ASCII字符直到文件深处才出现的事实.

我将sed在文件的开头粘贴一个Ö,看看会发生什么.

$ sed '1s/^/Ö\'$'\n/' source-file > test-file
$ head -n1 test-file
Ö
$ head -n1 test-file | hexdump -C
00000000  c3 96 0a                                          |...|
00000003
Run Code Online (Sandbox Code Playgroud)

很酷,我们有一个变音符号.注意编码虽然是c3 96(utf-8).嗯.

再次检查同一文件中的其他变音符号:

$ tail -n +102322 test-file | head -n1 | hexdump -C -s85 -n2
00000055  d6 4d                                             |.M|
00000057
Run Code Online (Sandbox Code Playgroud)

ISO-8859-1.哎呀!只是表明将编码搞砸是多么容易.

让我们尝试使用前面的变音符号转换我们的新测试文件,看看会发生什么.

$ iconv -f iso-8859-1 -t utf8 test-file > test-file-converted
$ head -n1 test-file-converted | hexdump -C
00000000  c3 83 c2 96 0a                                    |.....|
00000005
$ tail -n +102322 test-file-converted | head -n1 | hexdump -C -s85 -n2
00000055  c3 96                                             |..|
00000057
Run Code Online (Sandbox Code Playgroud)

哎呀.第一个UTF-8变音符号被解释为ISO-8859-1,因为这就是我们所说的iconv.第二个变音符号正确转换d6c3 96.

我会再试一次,这次我会用vimÖ插入代替sed. vim似乎更好地检测编码(如"latin1"又名ISO-8859-1)所以它可能会以一致的编码插入新的Ö.

$ vim source-file
$ head -n1 test-file-2
?
$ head -n1 test-file-2 | hexdump -C
00000000  d6 0d 0a                                          |...|
00000003
$ tail -n +102322 test-file-2 | head -n1 | hexdump -C -s85 -n2
00000055  d6 4d                                             |.M|
00000057
Run Code Online (Sandbox Code Playgroud)

看起来不错.对于新旧变音符号看起来像ISO-8859-1.

现在测试.

$ file -b --mime-encoding test-file-2
iso-8859-1
$ iconv -f iso-8859-1 -t utf8 test-file-2 > test-file-2-converted
$ file -b --mime-encoding test-file-2-converted
utf-8
Run Code Online (Sandbox Code Playgroud)

繁荣!故事的道德启示.不要相信file总能猜出你的编码是正确的.易于在同一文件中混合编码.如有疑问,请查看十六进制.

file在处理大文件时解决此特定限制的黑客(也容易出现故障)将缩短文件以确保特殊字符出现在文件的早期,因此file更有可能找到它们.

$ first_special=$(pcregrep -o1 -n '()[^\x00-\x7F]' source-file | head -n1 | cut -d":" -f1)
$ tail -n +$first_special source-file > /tmp/source-file-shorter
$ file -b --mime-encoding /tmp/source-file-shorter
iso-8859-1
Run Code Online (Sandbox Code Playgroud)

更新

Christos Zoulas进行了更新,file以确保可配置的字节数量.有一天功能请求的转变,太棒了!

http://bugs.gw.com/view.php?id=533 https://github.com/file/file/commit/d04de269e0b06ccd0a7d1bf4974fed1d75be7d9e

该功能在file5.26版中发布.

在猜测编码之前查看更多大文件需要时间.然而,对于特定用例的选项是很好的,其中更好的猜测可能超过额外的时间/ io.

使用以下选项:

?P, ??parameter name=value

    Set various parameter limits.

    Name    Default     Explanation
    bytes   1048576     max number of bytes to read from file
Run Code Online (Sandbox Code Playgroud)

就像是...

file_to_check="myfile"
bytes_to_scan=$(wc -c < $file_to_check)
file -b --mime-encoding -P bytes=$bytes_to_scan $file_to_check
Run Code Online (Sandbox Code Playgroud)

...如果你想file在猜测前强行查看整个文件,应该这样做.当然,这仅适用于file5.26或更高版本.

我还没有构建/测试过最新版本.我的大多数机器目前都有file5.04(2010)...希望有一天这个版本将从上游下来.

  • 很好的解释。这应该是最重要的答案。我有你在这里描述的确切场景。 (2认同)

小智 16

所以人们说你不能和我理解你在提问和得到这样的答案时可能会感到沮丧.

如果你真的希望它以utf-8而不是us-ascii显示,那么你需要分两步完成.

第一:

iconv -f us-ascii -t utf-16 yourfile > youfileinutf16.*
Run Code Online (Sandbox Code Playgroud)

第二:

iconv -f utf-16le -t utf-8 yourfileinutf16 > yourfileinutf8.*
Run Code Online (Sandbox Code Playgroud)

那么如果你做一个文件-i你会看到新的字符集是utf-8.

希望能帮助到你.


sar*_*old 11

我认为Ned是问题的核心 - 你的文件实际上不是ASCII.尝试

iconv -f ISO-8859-1 -t UTF-8 file.php > file-utf8.php
Run Code Online (Sandbox Code Playgroud)

我只是猜测你实际上在使用iso-8859-1,它在大多数欧洲语言中都很受欢迎.