【问题标题】:How to split a file containing non-ascii characters into words, in bash?如何在bash中将包含非ASCII字符的文件拆分为单词?
【发布时间】:2014-09-17 19:32:52
【问题描述】:

例如,我有一个普通文本的文件,例如:

"Word1 Kuͦn, buͤtten; word4:"

我想得到一个每行 1 个单词的文件,保持标点,并有序:

,
:
;
Word1
Kuͦn
buͤtten
word4

我使用的代码:

grep -Eo '\w+|[^\w ]' input.txt | sort -f >> output.txt  

这段代码几乎可以完美运行,除了一件事:它将变音字符与它们所属的字母分开,就好像它们是单独的单词一样:

    ,
    :
    ;
    Word1
    Ku
    ͦ      
    n
    bu 
    ͤ   
    tten
    word4

字母 uͦ、uͤ 和其他具有相同变音符号的字母不在 ASCII 表中。如何在不删除或替换这些字符的情况下正确拆分文件?

编辑:

locale 输出:

LANG=
LC_COLLATE="C"
LC_CTYPE="UTF-8"
LC_MESSAGES="C"
LC_MONETARY="C"
LC_NUMERIC="C"
LC_TIME="C"
LC_ALL=

【问题讨论】:

  • 您能否更新您的问题,包括locale 的输出?
  • 问题更新了

标签: bash split non-ascii-characters


【解决方案1】:

很遗憾,U+366(组合拉丁小写字母 O)不是字母字符。是无间距标记,unicode类别Mn,一般映射到Posix ctype cntrl

粗略地说,字母字素是一个字母字符,可能后跟一个或多个组合字符。如果您有一个实现 Unicode 通用类别的正则表达式库,则可以将其编写为正则表达式模式。 Gnu grep 通常使用流行的pcre(Perl 兼容的正则表达式)库的接口编译,该库具有相当好的 Unicode 支持。因此,如果您有 Gnu grep,那么您很幸运。

要启用“perl-like”正则表达式,您需要使用-P 选项(或pgrep)调用grep。但是,这还不够,因为默认情况下grep 将使用 8 位编码,即使语言环境指定了 UTF-8 编码。因此,您需要将正则表达式系统置于“UTF-8”模式,以使其能够识别您的字符编码。

将所有这些放在一起,您最终可能会得到以下结果:

grep -Po '(*UTF8)(\p{L}\p{M}*|\p{N})+|[\p{P}\p{S}]'

-P      patterns are "perl-compatible"
 -o     output each substring matched

(*UTF8) If the pattern starts with exactly this sequence,
        pcre is put into UTF-8 mode.
\p{...} Select a character in a specified Unicode general category
\P{...} Select a character not in a specified Unicode general category
\p{L}   General category L: letters
\p{N}   General category N: numbers
\p{M}   General category M: combining marks
\p{P}   General category P: punctuation
\p{S}   General category S: symbols
\p{L}\p{M}*       A letter possibly followed by various combining marks
\p{L}\p{M}*|\p{N} ... or a number

有关 Unicode 一般类别和一般 Unicode 正则表达式匹配的更多信息,请参阅Unicode Technical Report 18 正则表达式匹配。但请注意,TR 中描述的语法是一个建议,大多数正则表达式库并未完全实现。特别是,pcre支持有用的符号\p{L|N}(字母或数字)。相反,您需要使用[\p{L}\p{N}]

关于pcre 的文档可能在您的系统上可用(man pcre);如果没有,have a link on me

如果您没有 Gnu grep,或者在不太可能的情况下您的版本是在没有 pcre 支持的情况下编译的,您也许可以使用 perlpython 或其他具有正则表达式功能的语言。然而,这样做是非常困难的。经过一些实验,我发现以下 Perl 咒语似乎有效:

perl -CIO -lne 'print $& while /(\p{L}\p{M}*|\p{N})+|[\p{P}\p{S}]/g'

这里,-CIO 告诉 Perl 以 UTF-8 输入和输出,-nle 是一个标准的咒语,意思是“在打印后自动输出新的**行;循环遍历每个 **n **e 的输入,**e** 在循环中执行以下“。

【讨论】:

  • 哇,我永远不会想出这个...谢谢!虽然我尝试了该命令,但产生错误:>> grep -Po '(*UTF8)(\p{L}\p{M}*|\p{N})+|[\p{P}\p{S}]' sample_corpus.clean.txt >> usage: grep [-abcDEFGHhIiJLlmnOoPqRSsUVvwxZ] [-A num] [-B num] [-C[num]] [-e pattern] [-f file] [--binary-files=value] [--color=when] [--context[=num]] [--directories=action] [--label] [--line-buffered]
  • @user3241376:你可能没有 Gnu grep,或者你有一个非常旧的版本。 grep -V 告诉你什么?
  • @user3241376 我想你有 Mac OS X。根据stackoverflow.com/questions/16658333/…grep -P 的支持在去年 NacOSX 10.8 中被删除了。这个答案有一个 perl 替代方案。
  • 是的,忘了说,我有 Mac OS X。grep -V 输出 grep (BSD grep) 2.5.1-FreeBSD 我会看看这个答案,谢谢!
  • @user3241376:在答案中加入 Perl 咒语。希望对你有效。我不知道如何用 Python 做到这一点,在我太沮丧之前我放弃了。如果我是你,我只会安装 Gnu grep。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-04-17
  • 2022-07-06
  • 1970-01-01
  • 2017-04-28
  • 2010-12-30
  • 2019-06-03
  • 1970-01-01
相关资源
最近更新 更多