检查一行是否至少有 2 个不同的字母

检查一行是否至少有 2 个不同的字母

我有一个文本文件,每行都有一个单词。我正在尝试删除至少没有两个不同字母的行。例如,该文件看起来像这样:

words
books
aaa
letters 
zzzz

我希望输出文件如下所示:

words
books
letters

我尝试将每个单词分解为单独的字母,而不是使用uniq -cthen将它们组合在一起wc -l,但陷入了 if 语句。还相信一定有一种更简单的方法来做到这一点,我只是想不出任何其他方法来解决这个问题。

答案1

假设你的意思是特点而不是字母(例如,您还想删除包含...或的行,11即使.1不是字母):

grep -vx -e '' -e '\(.\)\1*'

或者:

grep -vx '\(\(.\)\2*\)\{0,1\}'

即删除 ( -v) 空行或以一个字符 ( .) 开头的行,后跟同一字符(\1对 捕获的内容的反向引用\(...\))重复 0 次或多次 ( *) 直到行尾(-x将模式锚定在行的开头和结尾)。

可移植的是,您不能在此处使用egrep或 ,grep -E因为标准 ERE 没有反向引用(只有 BRE 有)。

对于至少包含两个不同字母的行,忽略其他类型的字符(我们将[[:alpha:]]在此处使用,即被视为的任何字符按字母顺序排列的在您的语言环境中):

grep -vx '[^[:alpha:]]*
[^[:alpha:]]*\([[:alpha:]]\)\([^[:alpha:]]*\1\)*[^[:alpha:]]*'

(在两条线上,这是传递两种不同模式的另一种方式)。或者:

grep -vx '[^[:alpha:]]*\([^[:alpha:]]*\([[:alpha:]]\)\([^[:alpha:]]*\2\)*[^[:alpha:]]*\)\{0,1\}'

那个会删除像12345aaa(只有一个字母)或-+-+-+-(没有字母)这样的行。

如果您还想删除Aaaa行(即比较字母时忽略大小写),请添加该-i选项。

请注意,它在字符级别工作,因此如果存在用多个字符表示的字素,它可能不会执行您所期望的操作。例如,它将删除类似于以下输出的行:

 $ printf 'e\u0300e\u0301\n'
 èé

(假设 GNUprintf或兼容),但不是这样的:

 $ printf '\ue8\ue9\n'
 èé

(其中e\u300是字素的分解形式和\ue8预组合形式èe(U+0065) 和è(U+00E8) 是按字母顺序排列的,但不是结合重音/尖音的 U+0300 或 U+0301)。

要使用字素,您可以使用pcregrep或 GNUgrep及其-P选项:

对于第一种情况(至少两个不同的字素簇):

grep -vxP '(?:(\X)\1*)?'

对于第二种情况(至少有两个不同的字素簇):

grep -vxP '(?:(?=\PL)\X)*(?:((?=\pL)\X)(?:(?:(?=\PL)\X)*\1(?!\pM))*(?:(?=\PL)\X)*)?'

其中(?=\PL)\X是一个非字母字素簇(一个字素簇 ( \X) ,前提是(?=...)它以非字母 ( \PL) 和(?=\pL)\X字母字素簇开头。

\pL匹配于正确的unicode。与 POSIX 字符类相反[:alpha:],它还包括非字母脚本中的字母。

请注意,它会将e\u300\u301, e\u301\u300, \ue9\u300,\ue8\u301视为四个不同的簇,即使它们都是e带有锐音和重音的 a。

还要注意像(U+FB03) 这样在一个字符中包含多个字母的字符特点


通过PCRE,您还可以采取积极的方法:

  • 至少 2 个不同的字符:

    grep -P '(.).*(?!\1).'
    
  • 至少 2 个不同的字母字符:

    grep -P '(\pL).*(?!\1)\pL'
    
  • 至少 2 个不同的字素簇:

    grep -P '^\X*(\X)\X*(?!\1(?!\pM))\X'
    

    (至少)分解形式的古兰经朝鲜文无法正常工作。 PCRE(perl与 的 RE相反\b{g})没有字素边界运算符 (AFAIK),并且对 unicode 属性的支持有限。我们使用(?!\pM)(在这种情况下意味着:“只要它后面没有组合标记字符”)作为近似值,但这对于多部分朝鲜文不起作用字母/音节字符,其中部分没有那个财产。려련련例如,它会删除。现在人们也可能会争辩说,每个部分是一个独特的...

    对于perl5.22或更高版本,你可以这样写:

    perl -Mopen=locale -lne 'print if /\b{g}(\X).*\b{g}(?!\1\b{g})\X/'
    
  • 至少2个不同的字素簇:

    grep -P '^\X*((?=\pL)\X)\X*(?!\1(?!\pM))(?=\pL)\X'
    

    再次强调,不适用于려련련.和perl

    perl -Mopen=locale -lne 'print if /\b{g}(?=\pL)(\X).*\b{g}(?!\1\b{g})(?=\pL)\X/'
    

有了perl,我们可以使用更直接的方法,例如:

  • 至少 2 个不同的字符:

    perl -Mopen=locale -MList::MoreUtils=uniq -lne '
      print if uniq(/./g) >= 2'
    
  • 至少 2 个不同的字母字符:

    perl -Mopen=locale -MList::MoreUtils=uniq -lne '
      print if uniq(/\pL/g) >= 2'
    
  • 至少 2 个不同的字素簇:

    perl -Mopen=locale -MList::MoreUtils=uniq -lne '
      print if uniq(/\X/g) >= 2'
    
  • 至少2个不同的字素簇:

    perl -Mopen=locale -MList::MoreUtils=uniq -lne '
      print if uniq(grep /^\pL/, /\X/g) >= 2'
    

答案2

根据您的预期输出 - 您想要跳过超过 2 个单词完全相同的人物:

grep方法:

grep -vE '(.)(\1){2,}' file

输出:

words
books
letters 

修改文件到位您可以应用以下内容sed方法:

sed -Ei '/(.)(\1){2}/d' file

答案3

查询的正向搜索:

while read -r line; do 
    n=$(echo "$line" | egrep -o . | sort -u);
    [[ ${#n} -gt 1 ]] && echo "$line"; 
done < file

相关内容