我有一个文本文件,每行都有一个单词。我正在尝试删除至少没有两个不同字母的行。例如,文件看起来像这样:
words
books
aaa
letters
zzzz
Run Code Online (Sandbox Code Playgroud)
我希望输出文件看起来像这样:
words
books
letters
Run Code Online (Sandbox Code Playgroud)
我尝试将每个单词分解为单独的字母,而不是通过使用uniq -c
然后将它们组合在一起,wc -l
但卡在了 if 语句上。也相信必须有一种更简单的方法来做到这一点,我只是想不出任何其他方法来解决这个问题。
假设您的意思是字符而不是字母(例如,您还想删除包含...
或11
即使.
或1
不是字母的行):
grep -vx -e '' -e '\(.\)\1*'
Run Code Online (Sandbox Code Playgroud)
或者:
grep -vx '\(\(.\)\2*\)\{0,1\}'
Run Code Online (Sandbox Code Playgroud)
即删除 ( -v
) 空行或以一个字符 ( .
) 开头的行,后跟同一个字符(\1
作为对所捕获内容的反向引用\(...\)
)重复 0 次或更多次 ( *
) 直到行尾(-x
将模式锚定在行的开始和结束)。
可移植地,您不能使用egrep
orgrep -E
在这里,因为标准 ERE 没有反向引用(只有 BRE 有)。
对于包含至少两个不同字母的行,忽略其他类型的字符(我们将[[:alpha:]]
在此处用于letter,即在您的语言环境中被视为字母的任何字符):
grep -vx '[^[:alpha:]]*
[^[:alpha:]]*\([[:alpha:]]\)\([^[:alpha:]]*\1\)*[^[:alpha:]]*'
Run Code Online (Sandbox Code Playgroud)
(在两行上,这是传递两种不同模式的另一种方式)。或者:
grep -vx '[^[:alpha:]]*\([^[:alpha:]]*\([[:alpha:]]\)\([^[:alpha:]]*\2\)*[^[:alpha:]]*\)\{0,1\}'
Run Code Online (Sandbox Code Playgroud)
那个会删除像12345aaa
(只有一个字母)或-+-+-+-
(没有字母)这样的行。
如果您还想删除Aaaa
行(即比较字母时忽略大小写),请添加该-i
选项。
请注意,它在字符级别工作,因此如果有多个字符表示的字素,它可能无法达到您的预期。例如,它会通过以下方式删除与该输出类似的行:
$ printf 'e\u0300e\u0301\n'
e?e?
Run Code Online (Sandbox Code Playgroud)
(假设 GNUprintf
或兼容),但不是像:
$ printf '\ue8\ue9\n'
èé
Run Code Online (Sandbox Code Playgroud)
(其中e\u300
是字素的分解形式和\ue8
预组合形式è
;e
(U+0065) 和è
(U+00E8) 是按字母顺序排列的,但不是 U+0300 或 U+0301 组合严重/尖锐的口音)。
要使用字素,您可以使用pcregrep
或 GNUgrep
及其-P
选项:
对于第一种情况(至少两个不同的字素簇):
grep -vxP '(?:(\X)\1*)?'
Run Code Online (Sandbox Code Playgroud)
对于第二种情况(至少两个不同的字母字形簇):
grep -vxP '(?:(?=\PL)\X)*(?:((?=\pL)\X)(?:(?:(?=\PL)\X)*\1(?!\pM))*(?:(?=\PL)\X)*)?'
Run Code Online (Sandbox Code Playgroud)
哪里(?=\PL)\X
是非字母字形簇(字形簇 ( \X
),前提是(?=...)
它以非字母 ( \PL
) 和(?=\pL)\X
字母字形簇开头。
\pL
正确匹配字母unicode。与[:alpha:]
POSIX 字符类相反,它还包括来自非字母脚本的字母。
请注意,它会将e\u300\u301
, e\u301\u300
, \ue9\u300
,\ue8\u301
视为四个不同的簇,即使它们e
都是带有锐音和重音的 a。
还要注意像?
(U+FB03) 这样在一个字符中包含多个字母的字符。
使用 PCRE,您还可以采取积极的方法:
至少 2 个不同的字符:
grep -P '(.).*(?!\1).'
Run Code Online (Sandbox Code Playgroud)至少 2 个不同的字母字符:
grep -P '(\pL).*(?!\1)\pL'
Run Code Online (Sandbox Code Playgroud)至少 2 个不同的字形簇:
grep -P '^\X*(\X)\X*(?!\1(?!\pM))\X'
Run Code Online (Sandbox Code Playgroud)
一个人不能正常使用分解形式的古兰经韩文(至少)。PCRE(与perl
的 RE相反\b{g}
)没有字素边界运算符 (AFAIK),并且对 unicode 属性的支持有限。我们使用(?!\pM)
(这意味着在这种情况下:“前提是后面没有组合标记字符”)作为近似值,但这不适用于多部分韩文字母/音节字符,其中部分没有财产。????????
例如,它会删除。现在人们也可能会争辩说,每个部分都是一个不同的字母......
使用perl
5.22 或更高版本,您可以编写它:
perl -Mopen=locale -lne 'print if /\b{g}(\X).*\b{g}(?!\1\b{g})\X/'
Run Code Online (Sandbox Code Playgroud)至少 2 个不同的字母字形簇:
grep -P '^\X*((?=\pL)\X)\X*(?!\1(?!\pM))(?=\pL)\X'
Run Code Online (Sandbox Code Playgroud)
同样,不适用于????????
. 与perl
:
perl -Mopen=locale -lne 'print if /\b{g}(?=\pL)(\X).*\b{g}(?!\1\b{g})(?=\pL)\X/'
Run Code Online (Sandbox Code Playgroud)使用perl
,我们可以使用更直接的方法,例如:
至少 2 个不同的字符:
perl -Mopen=locale -MList::MoreUtils=uniq -lne '
print if uniq(/./g) >= 2'
Run Code Online (Sandbox Code Playgroud)至少 2 个不同的字母字符:
perl -Mopen=locale -MList::MoreUtils=uniq -lne '
print if uniq(/\pL/g) >= 2'
Run Code Online (Sandbox Code Playgroud)至少 2 个不同的字形簇:
perl -Mopen=locale -MList::MoreUtils=uniq -lne '
print if uniq(/\X/g) >= 2'
Run Code Online (Sandbox Code Playgroud)至少 2 个不同的字母字形簇:
perl -Mopen=locale -MList::MoreUtils=uniq -lne '
print if uniq(grep /^\pL/, /\X/g) >= 2'
Run Code Online (Sandbox Code Playgroud)