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

我试图从US-ASCII到UTF-8的一堆文件转码。

为此,我使用iconv:

iconv -f US-ASCII -t UTF-8 file.php > file-utf8.php 

事情是我的原始文件是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中,文件编码(字符集)将切换为UTF-8。

就我而言,我想强制iconv将文件转码为UTF-8 。 是否有非ASCII字符。

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

Ilétait une fois … l'hommeséréanimée mythique d'Albert

巴里尔(Procidis),1岁

编辑

  • US-ASCII UTF-8一个子集(参见下面的Ned的答案 )
  • 这意味着US-ASCII文件实际上是用UTF-8编码的
  • 我的问题来自其他地方

ASCII是UTF-8的一个子集,所以所有的ASCII文件都已经被UTF-8编码了。 ASCII文件中的字节以及“将其编码为UTF-8”所产生的字节将是完全相同的字节。 他们之间没有区别,所以没有必要做任何事情。

看起来你的问题是这些文件实际上不是ASCII码。 你需要确定他们正在使用什么编码,并妥善转码。

简答

  • file只能猜测文件编码,可能是错误的。
  • 您可以使用hex查看非7位ASCII文本的字节,并与代码表进行通用编码(7位ASCII码,ISO-8859- *,UTF-8)比较,以确定编码是什么。
  • iconv将使用您指定的任何input/输出编码,而不pipe文件的内容是什么。 如果指定了错误的input编码,输出将会出现乱码。
  • 即使在运行iconv ,由于file尝试猜测编码的方式有限, file可能不会报告任何更改。 举一个具体的例子,看看我的长答案。

长答案

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

首先,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包括德文和西class牙文字符Ö,é,ñ和ß。 “扩展”意味着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在提示types的file查找幻数,但这些可能是错误的,不能保证正确性。 file也尝试通过查看文件中的字节来猜测字符编码。 基本上file有一系列的testing,可以帮助猜测文件types和编码。

我的文件是一个很大的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 

我的文件里面有元音(Ö)。 第一个非7位ascii直到超过10万行才显示出来。 我怀疑这是为什么file没有意识到文件编码不是US-ASCII。

 $ pcregrep -no '[^\x00-\x7F]' source-file | head -n1 102321:  

(我在Mac上,所以使用PCRE的grep 。使用gnu grep你可以使用-P选项。)

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

无论我的文件的编码是什么,这些非7位ASCII字符都会破坏。 我的德国CSV文件是; 分离和提取单个列不起作用。

 $ cut -d";" -f1 source-file > tmp cut: stdin: Illegal byte sequence $ wc -l * 3081673 source-file 102320 tmp 3183993 total 

请注意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 

其他方式。 我知道第一个非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 

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

重要的问题…我怎么知道这个字符是一个Ö不知道文件编码? 答案是上下文。 我打开文件,阅读文本,然后确定它应该是什么字符。 如果我在vim打开它,它显示为Ö因为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 

嗯。 file仍然告诉我这个文件甚至在转换之后是US-ASCII。 让我们再次检查与hexdump

 $ tail -n +102321 output-file | head -n1 | hexdump -C -s85 -n2 00000055 c3 96 |..| 00000057 

绝对是一个变化。 请注意,我们有两个字节的非7位ASCII(用右边的“。”表示),现在两个字节的hex代码是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 

很酷,我们有变音。 注意编码虽然是c3 96(utf-8)。 嗯。

再次在同一个文件中检查我们的其他元音变音:

 $ tail -n +102322 test-file | head -n1 | hexdump -C -s85 -n2 00000055 d6 4d |.M| 00000057 

ISO-8859-1。 哎呀! 只是去显示得到编码搞砸是多么容易。

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

 $ 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 

哎呀。 这是UTF-8的第一个变音符号被解释为ISO-8859-1,因为这是我们告诉iconv 。 第二个变音符号从d6正确转换为c3 96

我会再试一次,这次我会用vim做Ö插入而不是sedvim似乎更好地检测了编码(如“latin1”aka 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  $ 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 

看起来不错。 看起来像ISO-8859-1为新老变音器。

现在testing。

 $ 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 

繁荣! 故事的道德启示。 不要相信file总是猜测你的编码权。 易于在同一个文件中混合编码。 如有疑问,请看hex。

在处理大file时,解决file特定限制的黑客攻击(也容易失败)是缩短文件以确保特殊字符出现在文件的早期,因此file更有可能find它们。

 $ 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 

更新

Christos Zoulas更新了file ,使得可以configuration的字节数量。 有一天function要求转身,真棒!

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

该function已在file版本5.26中发布。

在猜测编码之前,先看看更多的大文件需要时间。 但是,如果有更好的猜测可能会超过额外的时间/ io,那么可以select具体的用例。

使用以下选项:

 −P, −−parameter name=value Set various parameter limits. Name Default Explanation bytes 1048576 max number of bytes to read from file 

就像是…

 file_to_check="myfile" bytes_to_scan=$(wc -c < $file_to_check) file -b --mime-encoding -P bytes=$bytes_to_scan $file_to_check 

…应该做的伎俩,如果你想在猜测之前强迫file看整个文件。 当然这只有在你有5.26或更新的file时才有效。

我还没有构build/testing最新版本。 我的机器目前大多数file 5.04(2010)…希望有一天这个版本将使其从上游。

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

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

第一:

 iconv -f us-ascii -t utf-16 yourfile > youfileinutf16.* 

第二:

 iconv -f utf-16le -t utf-8 yourfileinutf16 > yourfileinutf8.* 

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

希望能帮助到你。

我认为Ned得到了问题的核心 – 你的文件实际上不是ASCII码。 尝试

 iconv -f ISO-8859-1 -t UTF-8 file.php > file-utf8.php 

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

US-ASCII和UTF-8没有区别,所以不需要重新转换。 但是这里有一点提示,如果你在重新编码时遇到特殊问题。

在source-charset-Parameter之后添加// TRANSLIT。

例:

 iconv -f ISO-8859-1//TRANSLIT -t UTF-8 filename.sql > utf8-filename.sql 

这有助于我对奇怪的引号types,这是总是打破了字符集重新编码过程。