我已经尝试了几十种正则表达式的排列来解决这个问题,但我没有遇到任何运气。
我需要遍历数十个文件,在“the/a/an”和可能数字为 1-4 的数字之间提取特定短语,忽略标点符号,例如 {}()[]。
例子
敏捷的棕色狐狸 {15} 以某种方式跳过懒狗 [20] 4,这绝对不适合所有观众 (0012)。
应该返回:
快速棕色狐狸 15
懒狗 20
某种方式 4
观众0012
消除标点符号不是问题:sed 's/[][{}()]//g'
有什么建议吗?
在 GNU awk 中,您可以将输入拆分为以数字结尾的记录,这些记录可选地用标点符号包围:
$ cat file
The quick brown fox {15} jumps over the lazy dog [20] in a certain way 4 that is definitely not appropriate for all of the viewers (0012).
$ gawk -v RS='[[:punct:]]*[[:digit:]]+[[:punct:]]*' 'RT{print $0 RT}' file
The quick brown fox {15}
jumps over the lazy dog [20]
in a certain way 4
that is definitely not appropriate for all of the viewers (0012).
然后您需要做的就是打印您想要的记录部分和记录终止符:
$ gawk -v RS='[[:punct:]]*[[:digit:]]+[[:punct:]]*' 'RT{print gensub(/.*\y(the|a|an)\y/,"\\1","") gensub(/[[:punct:]]/,"","g",RT)}' file
The quick brown fox 15
the lazy dog 20
a certain way 4
the viewers 0012
我刚刚注意到,在您的示例中,您将输出转换为全部小写。只需$0=tolower($0)
在 print 之前添加一个 in 即可(也解决了the|a|an
比较不区分大小写的问题):
$ gawk -v RS='[[:punct:]]*[[:digit:]]+[[:punct:]]*' 'RT{$0=tolower($0); print gensub(/.*\y(the|a|an)\y/,"\\1","") gensub(/[[:punct:]]/,"","g",RT)}' file
纯 Bash 和使用正则表达式的练习:
while read line ; do
line=" $line" # add leading space as word boundary
while [ -n "$line" ] ; do
[[ "$line" =~ [[:space:]]((an|a|the|An|A|The)([[:space:]]+[^[:digit:]]+)([[:digit:]]{1,4}))(.+$) ]]
match="${BASH_REMATCH[2]}${BASH_REMATCH[3]}${BASH_REMATCH[4]}"
match=${match//[()\[\]\{\}]/} # remove parentheses
[ -n "$match" ] && echo "'$match'" # print if not empty
line="${BASH_REMATCH[5]}" # the postmatch
done
done < "$infile"
输出:
'The quick brown fox 15'
'the lazy dog 20'
'a certain way 4'
'the viewers 0012'
grep -ioP "(a|an|the).*?\d{1,4}" files
将-o
仅打印匹配的文本,并且每个匹配项都在其自己的行上。 -P
用于不情愿的量词,也使正则表达式自动扩展。您当然可以sed
按照上面的建议将此输出通过管道传输到。
这可能对您有用(GNU sed):
sed -r '/\b(the|an|a)\b/I!d;s//\n&/;s/[^\n]*\n//;s/\{([0-9]{1,4})\}|\(([0-9]{1,4})\)|\[([0-9]{1,4})\]|\b([0-9]{1,4})\b/\1\2\3\4\n/;P;D' file