word-grep 中相同字符的确切数量

word-grep 中相同字符的确切数量

我的任务是找到带有egrep的行,其中第一个单词包含确切地三个相同的字母。我尝试过使用反向引用,但只找到了一种构建模式的方法,该模式可以找到由 3 个或更多相同字符构建的单词:

egrep -i '^[^[:alpha:]]*\<[a-z]*([a-z])[a-z]*(\1[a-z]*){2}\>'

答案1

以下匹配仅包含 3 个相同 [:alpha:] 字符的行开头的任何“单词”:

grep -i '^\([[:alpha:]]\)\1\1\b' 

或者,使用 grep 的-E( --extended-regexp) 或-P(又名--perl-regexp)选项:

grep -iE '^([[:alpha:]])\1\1\b'

grep -iP '^([[:alpha:]])\1\1\b'

它们与 GNU grep 一起使用,并且(版本除外-P)与 FreeBSD 的 grep 一起使用。它们可能无法与其他版本的 grep 一起使用。


如果你想匹配任意长度的包含以下内容的单词3个或更多在其中的任何位置使用相同的字母字符,这有点困难。你需要使用一个负前瞻,这需要 perl 兼容的正则表达式。

即它不能用grep -E(又名egrep,已被已弃用)。

例如:

$ grep -iP '^[[:alpha:]]*([[:alpha:]])((?:(?!\1)[[:alpha:]])*\1){2}[[:alpha:]]*\b' /usr/share/dict/words
Aaliyah
Aaliyah's
Aarau
Aargau
Aaronical
Abadan
Abbottstown
Abbottstown's
Aberdeen
Aberdeen's
...
zoozoo
zoozoos
zuzzes
zwitterionic
zygogeneses
zygomorphous
zymogeneses
zyzzyva
zyzzyvas
zzz

(根据wc -l,这与我的 /usr/share/dict/words 文件中的 344817 个单词中的 67117 个匹配)


最后,仅匹配单词正好 3其中任意位置具有相同的 [:alpha:] 字符:

$ grep -iP '^[[:alpha:]]*([[:alpha:]])((?:(?!\1)[[:alpha:]])*\1){2}[[:alpha:]]*\b' /usr/share/dict/words | 
  grep -viP '^[[:alpha:]]*([[:alpha:]])((?:(?!\1)[[:alpha:]])*\1){3}'

第一个 grep 查找具有 3 个或更多相同字符的单词,第二个 grep 排除具有 4 个或更多相同字符的单词。

我不确定这是否可以用单个正则表达式来完成。

(这与我的 /usr/share/dict/words 文件中的 56820 个单词匹配)。

答案2

我认为您无法使用grep正则表达式来做到这一点,即使使用 Perl/PCRE 功能(例如零长度断言和反向引用)也是如此。

这很可能是一些理论上的兔子洞,但我对这些东西不感兴趣。

所以就用perl 来做吧。 “算法”可以很容易地翻译成 awk、ruby、python 等:

perl -CiI -anle 'my ($i,%l); ($n=++$l{$_})==3 ? $i++ : $n==4 ? $i-- : () for $F[0]=~/\pL/g; print if $i' file

这可以很容易地进行调整。例如,如果您想查找 3 个字母重复 3 次的单词:

perl -CiI -anle 'my ($i,%l); ($n=++$l{$_})==3 ? $i++ : $n==4 ? $i-- : () for $F[0]=~/\pL/g; print if $i >= 3' /usr/share/dict/words
...
entertainment
...
totalitarianism

或 7 个字母重复 2 次:

perl -CiI -anle 'my ($i,%l); ($n=++$l{$_})==2 ? $i++ : $n==3 ? $i-- : () for $F[0]=~/\pL/g; print if $i >= 7' /usr/share/dict/words
...
electroencephalograph
...
telecommunication

您还可以更改\pL为仅.匹配任何字母、$F[0]=~/..//../或不-a切换以匹配整行、省略-CiI仅考虑 ascii 字母等。

答案3

仅使用 ERE(扩展正则表达式)构建此类正则表达式的方法。

与 GNU grep (perl regex) (匹配 3 个或更多重复字符)更接近的是:

grep -P '(\w)(((?!\1)\w)*\1){2}' filename

因此,删除重复 4 次或以上的单词,您将得到答案:

grep -P      '(\w)(((?!\1)\w)*\1){2}' filename | 
    grep -Pv '(\w)(((?!\1)\w)*\1){3}'

GNU awk 的替代方案是:

awk '{
      a=$1;
      while (length(a)){
                        b=gensub(substr(a,0,1),"","g",a);
                        if(length(a)-length(b)==3){print $0;next};
                        a=b
                       }
     }' filename

它的工作原理是删除第一个字符的所有重复,如果删除的是 3 个字符,则打印它,否则,删除下一​​个第一个字母,直到没有更多的字符可以替换(改进是仅测试剩余长度是否相等或大于所需的重复次数)。

假设您想要计数A为相当于a,然后使用以下方法过滤您的文件:

cat /usr/share/dict/words | tr [[:upper:]] [[:lower:]] > words

这两个解决方案相似但不相等。两者在independence单词上有所不同,例如上面生成的词典文件。

是的,independence包含 3n但包含 4 e。根据首先找到的单词,可能会包含或不包含该单词。 awk 解决方案是稳定的,并且将包含以下单词:任何字符恰好重复 3 次。正则表达式解决方案更加灵活,并且在某些条件下会匹配,而在其他条件下则不匹配。

此外,正则表达式将仅匹配单词不包含的字符'(并且文件包含多个带有该字符的单词)。

总之,匹配的行数为(使用 awk 则多出 1527 行):

 13758 awklist
 12231 greplist

并且,删除'(使用 awk 还可以删除 184 个):

 9236 awklist2
 9052 greplist2

应该tastelessness teleconferencing teletypewriter teletypewriters tempestuousness timelessness tintinnabulation tintinnabulations tirelessness transcontinental transgressors transubstantiation(仅列出一些)被拒绝吗?

都确实有3一个字符和四个(或更多)另一个字符。

相关内容