使用grep筛选符合特定字母重复规则的文本行求助
文本处理:筛选符合特定字母重复规则的行
需求说明
筛选满足以下条件的文本行,仅关注每行空格前的字符串:
- 字符串中无重复字母,唯一例外是允许仅存在一处「前单词尾字母=后单词首字母」的衔接结构(如
FLOWER-RAIN) - 若该衔接结构之后还存在任何重复字母(包括单词内部重复或其他跨单词重复),则不输出该行
示例输入
VULTURE-CURVE-FANCIEST-INSULT IAF-ETU-CRN-VLS RAZE-PLUSHY-YON OYL-AHE-RNP-USZ JOCKEYS-SMELTED-OBSOLETE-COCKED-OCELOT-SECEDED DTE-BMO-CKJ-LSY HUSH-MUTES-PETREL-SINISTER THP-EMN-SLG-IRU ALMOST-TRAGIC-CREEL AME-OCG-BLT-SIR BASIC-RIDES-GAUZY GIE-DRY-BSZ-CUA PWNS-SAD-OMIT-TUB DIP-ATU-MNS-WBO OUTVOTES-SAWN-BAH-EASE-HEADBAND-BESTOWS BHT-EWD-ONS-UVA
预期输出
RAZE-PLUSHY-YON OYL-AHE-RNP-USZ PWNS-SAD-OMIT-TUB DIP-ATU-MNS-WBO
当前问题
你使用的grep -Ev '([A-Z])[A-Z-]*\1[A-Z]?(-[A-Z-]*)* ' letter_boxed.txt规则不够精准,导致多匹配了约100行。
解决方案
1. 精准的grep命令
通过反向排除所有不符合规则的行来实现:
grep -Ev '([A-Z])[A-Z]*\1|([A-Z]).*\2.*\2' letter_boxed.txt
规则解释
([A-Z])[A-Z]*\1:排除单词内部存在重复字母的行(比如CREEL里的重复E、JOCKEYS里的重复J)([A-Z]).*\2.*\2:排除整个前缀字符串中同一字母重复出现2次以上的行(不管是跨单词还是单词内部)
这两个规则组合后,仅保留两种合法情况:
- 前缀字符串完全无重复字母
- 前缀字符串仅有一处跨单词的首尾字母重复,且无其他任何重复
2. 备选方案:awk实现(更直观)
如果grep的正则逻辑难以理解,用awk可以更清晰地实现规则:
{ prefix = $1 split(prefix, words, "-") valid = 1 seen_chars = "" dup_count = 0 # 遍历每个单词检查合法性 for (i=1; i<=length(words); i++) { word = words[i] # 检查单词内部是否有重复字母 for (j=1; j<=length(word); j++) { c = substr(word, j, 1) if (index(seen_chars, c) != 0) { # 仅允许一次跨单词的首尾重复 if (i > 1 && c == substr(words[i-1], length(words[i-1]), 1) && dup_count == 0) { dup_count++ } else { valid = 0 break } } seen_chars = seen_chars c } if (!valid) break } # 仅输出符合条件的行 if (valid && dup_count <= 1) { print $0 } }
将上述代码保存为filter.awk,执行命令:
awk -f filter.awk letter_boxed.txt
内容的提问来源于stack exchange,提问作者Viktor Devriendt
相关产品推荐
相关产品推荐

