我最近开始使用诸如grep
、、等工具wc
,cat
因为我必须处理一些非常大的 CSV 文件(>10GB),这些文件没有完全正确地分隔(例如,出现分隔符里面一些领域。
在处理其中一个文件时,我运行了以下命令,试图找出一种方法来正确识别哪些实例是;
分隔符并将其替换为其他字符:
grep -v -n --text "[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9].*[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9].*[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9].*[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]" < Transactions.csv
正则表达式可能可以做得更好,但无论如何;令人惊讶的是,除其他外,上面的代码输出以下行:
12345678:2016-10-25;12345678912345;2016-10-25;gobbledegook �IDNR: 69 ;12345.67;.00;2003-09-05;12345678;2003-09-03;stuff stuff ;12345 fgadfkjgbsdkb;12/3/45678/9
(因为这实际上是交易数据,我更改了大多数字段的值,除了有问题的�
)也许我有点傻,但为什么上面的正则表达式不匹配该行?似乎正则表达式.*
由于某种原因不匹配该字符。
我怀疑该文件是使用 UTF-16 编码保存的,如果有任何区别的话。
编辑:感谢 @exore 的回答。事实证明,我的文件是用 ISO-8859-15 编码的,我能够通过将grep
包含特殊字符的行(相对较少)ping 到文件中并在 gedit 中打开来解决这个问题。然后我将iconv
其转换为 utf8,之后它工作正常!
答案1
这是一个典型的字符编码问题。.
表示任何字符。但哪个字节序列是合法字符则取决于编码。在不了解编码的情况下处理文本肯定会失败。您的 grep 命令可能期望 UTF-8 编码的字符串。UTF-8 是一种多字节编码,这意味着某些字符由多个字节表示。但是,并非所有字节序列都是有效的。例如,请参阅维基百科有关 UTF-8 的文章。
当 grep 遇到不是预期编码中的有效字符的字节序列时,它无法将其识别为字符,该行不匹配,而是输出。由于您的终端也无法识别该字符,因此您会得到一个�
。
您的情况有一个解决方法。告诉 grep 不要担心编码,并将一个字节视为一个字符。
env LANG=C grep ....
或者可能
env LANG=C LC_ALL=C grep ....
您可以轻松测试:
创建2个文件,一个为utf-8编码,一个为utf-16-be:
$ echo éléphant | tee file.std | iconv -f utf8 -t utf16be >file.utf16be
检查文件内容:
$ cat file*
éléphant
�l�phant
尝试 grep。utf16be 字符串无法识别,没有输出:
$ grep '^.*$' file*
file.std:éléphant
根本不要使用编码。一个字节是一个字符。所有字符串都匹配� 只是意味着终端无法将 utf16be 序列识别为有效的 utf-8 字符。请注意使用 来-a
告诉 grep 将二进制视为一些文本。
$ env LANG=C grep -a '^.*$' file*
file.std:éléphant
file.utf16be:�l�phant
或者,如果您知道编码,则可以iconv
先转换文件,然后使用 grep。以下方法之一应该有效。
iconv -f utf16 -t utf8 < file | grep ...
iconv -f utf16le -t utf8 < file | grep ...
iconv -f utf16be -t utf8 < file | grep ...