我正在运行 Ubuntu,echo $LANG 告诉我我正在使用 UTF-8:“en_US.UTF-8”。
\n\n我创建了一个目录,其中包含一个名为“\'\xc3\xb6\”(德语元音变音)的文件
\n\nronald@lala:~/tempX/test$ ls\n\xc3\xb6\nRun Code Online (Sandbox Code Playgroud)\n\n我的理解是,由于 utf-8 编码,文件名由代表一个字符的两个字节组成。因此我很惊讶这个匹配:
\n\nronald@lala:~/tempX/test$ ls | grep "^\\W\\W$"\n\xc3\xb6\nronald@lala:~/tempX/test$ ls | egrep "^\\W{2,}$"\n\xc3\xb6\nronald@lala:~/tempX/test$ ls | grep -P "^\\W{2,}$"\n\xc3\xb6\nronald@lala:~/tempX/test$ ls | pcregrep "^\\W{2,}$"\n\xc3\xb6\nRun Code Online (Sandbox Code Playgroud)\n\n为什么 grep 将 \'\xc3\xb6\' 视为两个非单词字符而不只是一个?
\n\n最好的问候,\n罗纳德
\n除了设置正确的环境变量之外,还需要存在正确的区域设置文件,然后 grep 才能正确解释非 ASCII 文本。locale-gen en_US.UTF-8跟着跑export LANG="en_US.UTF-8",你应该就可以走了。如果这不起作用(或者如果您没有locale-gen安装),请尝试export LANG=C.UTF-8.
问题示例:
\n\n$ O_WITH_UMLAUT="\xc3\xb6"\n\n$ printf "%s" "$O_WITH_UMLAUT" | grep -E \'^[^\\w]$\'\n\n$ printf "%s" "$O_WITH_UMLAUT" | grep -E \'^[^\\w]{2}$\'\n\xc3\xb6\nRun Code Online (Sandbox Code Playgroud)\n\n第一次尝试不会产生任何输出,但是当您要求 grep 连续搜索两个非单词字符时,就会出现\xe2\x80\xa6
\n\n出现此行为的原因是非 ASCII 字符使用多字节编码方案(在当今时代几乎应始终为 UTF-8,但古老/过时的系统可能使用更奇特的编码)。
\n\n$ printf "%s" "$O_WITH_UMLAUT" | od -Ax -tx1\n000000 c3 b6\n000002\nRun Code Online (Sandbox Code Playgroud)\n\n注意:如果您的终端模拟器由于相关编码问题而不允许您粘贴 \'\xc3\xb6\',那么您仍然可以将其放入环境变量中,如下所示以进行测试: O_WITH_UMLAUT=$(printf "\\xC3\\xB6")
解决此问题的通常建议是将LANG环境变量(充当LC_*环境变量的后备)设置为类似en_US.UTF-8(或en_GB.UTF-8为你pl_PL.UTF-8有什么,等等\xe2\x80\xa6),以便grep 可以知道输入数据应该采用什么编码:ru_RU.UTF-8C.UTF-8
$ export LANG="en_US.UTF-8"\nRun Code Online (Sandbox Code Playgroud)\n\n\xe2\x80\xa6但是,如果 \xe2\x80\x99 不起作用怎么办?
\n\n$ printf "%s" "$O_WITH_UMLAUT" | grep -E \'^[^\\w]$\'\n\n$ printf "%s" "$O_WITH_UMLAUT" | grep -E \'^[^\\w]{2}$\'\n\xc3\xb6\nRun Code Online (Sandbox Code Playgroud)\n\n在这种情况下,首先要检查的是输出locale:
$ locale\nlocale: Cannot set LC_CTYPE to default locale: No such file or directory\nlocale: Cannot set LC_MESSAGES to default locale: No such file or directory\nlocale: Cannot set LC_ALL to default locale: No such file or directory\nLANG=en_US.UTF-8\nLANGUAGE=\nLC_CTYPE="en_US.UTF-8"\nLC_NUMERIC="en_US.UTF-8"\nLC_TIME="en_US.UTF-8"\nLC_COLLATE="en_US.UTF-8"\nLC_MONETARY="en_US.UTF-8"\nLC_MESSAGES="en_US.UTF-8"\nLC_PAPER="en_US.UTF-8"\nLC_NAME="en_US.UTF-8"\nLC_ADDRESS="en_US.UTF-8"\nLC_TELEPHONE="en_US.UTF-8"\nLC_MEASUREMENT="en_US.UTF-8"\nLC_IDENTIFICATION="en_US.UTF-8"\nLC_ALL=\nRun Code Online (Sandbox Code Playgroud)\n\n看起来有些区域设置文件丢失了。
\n\n第一段locale-gen解释了原因:
\n\n\n编译后的语言环境文件大约需要 50MB 的磁盘空间,并且大多数用户只需要很少的语言环境。为了节省磁盘空间,编译后的语言环境文件不会分布在 locales 包中,而是在通过运行 locale-gen 程序安装此包时自动生成选定的语言环境。
\n
所以,我们所要做的就是:
\n\n$ locale-gen en_US.UTF-8\nGenerating locales (this might take a while)...\n en_US.UTF-8... done\n\n$ locale # no more warnings!\nLANG=en_US.UTF-8\nLANGUAGE=\nLC_CTYPE="en_US.UTF-8"\nLC_NUMERIC="en_US.UTF-8"\nLC_TIME="en_US.UTF-8"\nLC_COLLATE="en_US.UTF-8"\nLC_MONETARY="en_US.UTF-8"\nLC_MESSAGES="en_US.UTF-8"\nLC_PAPER="en_US.UTF-8"\nLC_NAME="en_US.UTF-8"\nLC_ADDRESS="en_US.UTF-8"\nLC_TELEPHONE="en_US.UTF-8"\nLC_MEASUREMENT="en_US.UTF-8"\nLC_IDENTIFICATION="en_US.UTF-8"\nLC_ALL=\n\n$ printf "%s" "$O_WITH_UMLAUT" | grep -E \'^[^\\w]$\' # works as it should!\n\xc3\xb6\nRun Code Online (Sandbox Code Playgroud)\n\n\xe2\x80\xa6但是,如果那样呢??
\n\n$ locale-gen en_US.UTF-8\nbash: locale-gen: command not found\nRun Code Online (Sandbox Code Playgroud)\n\n无奈之下,你可以尝试一下C.UTF-8,它几乎在任何地方都可以轻松获得:
$ export LANG="C.UTF-8"\n\n$ printf "%s" "$O_WITH_UMLAUT" | grep -E \'^[^\\w]$\'\n\xc3\xb6\nRun Code Online (Sandbox Code Playgroud)\n\n如果这仍然不起作用,您可以尝试设置LC_ALL(充当严厉的覆盖)而不是LANG(如前所述,这仅充当后备)。
在您的情况下,您的非 ASCII 数据不是来自环境变量,而是来自文件系统上的目录(或者更具体地说,ls\xe2\x80\x99s 选择该目录的文本表示形式\ xe2\x80\x99s name\xe2\x80\xa6),因此最好了解某些文件系统(或其 API,或类似工具)ls\xe2\x80\xa6 这样的工具)会以与您预期不同的方式存储/生成信息,这可能会导致类似(但不相关)的问题。
例如,考虑在 Linux 系统上执行的以下操作:
\n\n$ mkdir -p /tmp/dirs\n$ cd /tmp/dirs\n$ python -i\n\n>>> import os\n>>> os.getcwd()\n\'/tmp/dirs\'\n>>> os.listdir(\'.\')\n[]\n>>> # Create a directory with this name:\n>>> # U+00F6: LATIN SMALL LETTER O WITH DIAERESIS\n>>> # (total Unicode code-points: 1)\n>>> os.makedirs(\'\\xc3\\xb6\')\n>>> os.listdir(\'.\')\n[\'\\xc3\\xb6\']\n>>> # Now create a directory with *this* name:\n>>> # U+006F: LATIN SMALL LETTER O (ASCII)\n>>> # followed by U+00A8: DIAERESIS (non-ASCII modifier)\n>>> # (total Unicode code-points: 2)\n>>> os.makedirs(\'o\\xcc\\x88\')\n>>> os.listdir(\'.\')\n[\'\\xc3\\xb6\', \'o\\xcc\\x88\']\n>>> exit()\n\n$ ls | grep -E \'^[^\\w]$\'\n\xc3\xb6\n\n$ ls | grep -E \'^[^\\w]{2}$\'\no\xcc\x88\n\n$ ls -Fl\ntotal 8\ndrwxr-xr-x 2 docker docker 4096 May 15 20:52 o\xcc\x88/\ndrwxr-xr-x 2 docker docker 4096 May 15 20:51 \xc3\xb6/\nRun Code Online (Sandbox Code Playgroud)\n\n(怎么\xe2\x80\x99s 让人困惑?!)
\n\n现在,同样的事情,在 Mac OS X (HFS+) 系统上, \xe2\x80\x94 值得庆幸的是 \xe2\x80\x94 不允许这样的恶作剧,但代价是你的文件/目录可能无法以相当的方式表示您可能期望的方式:
\n\n>>> import os\n>>> os.getcwd()\n\'/private/tmp/dirs\'\n>>> os.listdir(\'.\')\n[]\n>>> os.makedirs(\'\\xc3\\xb6\')\n>>> os.listdir(\'.\')\n[\'o\\xcc\\x88\'] # ...that\'s not what we asked it to create...\n>>> os.makedirs(\'o\\xcc\\x88\')\nOSError: [Errno 17] File exists: \'o\\xcc\\x88\'\n>>> os.makedirs(\'\\xc3\\xb6\')\nOSError: [Errno 17] File exists: \'\\xc3\\xb6\'\n>>> exit()\n\n$ ls | grep -E \'^[^\\w]$\' # nothing...\n\n$ ls | grep -E \'^[^\\w]{2}$\' # there it is.\no\xcc\x88\nRun Code Online (Sandbox Code Playgroud)\n\n因此,一旦您确定您的语言环境已设置并正常运行,如果您的正则表达式仍然无法正常工作,那么接下来要检查的就是确保您的文件系统(或您的构建ls,或您在 grep 管道中使用的任何其他实用程序\xe2\x80\x99)都\xe2\x80\x99t 在幕后对您的内容进行转码。(我可以编一篇关于 MinGW/MSYS 实用程序和 NTFS/exFAT 的故事,它会让你的头发和我在那个特定的越轨\xe2\x80\xa6 中拔出的头发一样多,但是,我离题了。)
希望有帮助!
\n\n进一步阅读:
\n\n非常规的“答案”,但我的答案是你的Ubuntu坏了,或者你需要使用与我相同的语言环境!我正在使用 OSX Mavericks。
\n\nls ??\n<nothing>\n\nls ?\n\xc2\xa8\n\nls ?| xxd\n0000000: c2a8 0a ...\n\nls | grep "^\\W\\W$"\n<nothing>\n\nls | grep "^\\W$"\n\xc2\xa8\n\necho $LANG\nen_GB.UTF-8\nRun Code Online (Sandbox Code Playgroud)\n
| 归档时间: |
|
| 查看次数: |
11097 次 |
| 最近记录: |