给定单词列表,对文本正文进行全词匹配
Whole-word matching on a body of text, given a list of words
注:
在进入正题之前,我想指出一些其他的 SO 帖子,它们没有完全回答我的问题,并且不是这个帖子的重复:
- How to grep with a list of words
- How to make grep only match if the entire line matches?
- how to grep for the whole word
- Grep extract only whole word
背景:
我在名为 words.txt
的文件中有一个单词列表(每行一个单词)。我想从另一个更大的文件 file.txt
中找到所有行,该文件包含 words.txt
中的任何单词。但是,我只想要 全字 匹配。这意味着当来自 file.txt
的一行至少包含一个来自 words.txt
的词被找到 "all by itself" 的实例时,应该进行匹配(我知道这是模糊的,所以请允许我解释一下).
换句话说,应该在以下情况下进行匹配:
- 这个词单独在一条线上
- 单词被 non-alphanumeric/non-hyphen 个字符包围
- 单词在一行的开头,后面跟着一个non-alphanumeric/non-hyphen字符
- 单词在一行的末尾,前面有一个 non-alphanumeric/non-hyphen 个字符
例如,如果 words.txt
中的一个词是 cat
,我希望它的行为如下:
cat #=> match
cat cat cat #=> match
the cat is gray #=> match
mouse,cat,dog #=> match
caterpillar cat #=> match
caterpillar #=> no match
concatenate #=> no match
bobcat #=> no match
catcat #=> no match
cat100 #=> no match
cat-in-law #=> no match
之前的研究:
有一个 grep
命令 几乎 适合我的需要。具体如下:
grep -wf words.txt file.txt
其中的选项是:
-w, --word-regexp
Select only those lines containing matches that form whole words.
The test is that the matching substring must either be at the beginning
of the line, or preceded by a non-word constituent character.
Similarly, it must be either at the end of the line or followed by a
non-word constituent character. Word-constituent characters are
letters, digits, and the underscore.
-f FILE, --file=FILE
Obtain patterns from FILE, one per line. The empty file contains
zero patterns, and therefore matches nothing.
我遇到的最大问题是它将连字符(即 -
)视为 "non-word constituent character"。因此(基于上面的例子)做一个 whole-word 搜索 cat
将 return cat-in-law
,这不是我想要的。
我意识到 -w
选项可能达到了很多人想要的效果。但是,在我的特殊情况下,如果一个单词(例如 cat
)是连字符 followed/preceded,那么我需要将其视为更大单词的一部分(例如 cat-in-law
)而不是单词本身的一个实例。
此外,我知道我可以更改 words.txt
以包含正则表达式而不是固定字符串,然后使用:
grep -Ef words.txt file.txt
哪里
-E, --extended-regexp
Interpret PATTERN as an extended regular expression
但是,我想避免更改 words.txt
并使其不受正则表达式模式的影响。
问题:
是否有一个简单的 bash 命令可以让我给它一个单词列表并对文本正文执行 全词 匹配?
终于想到了解决办法:
grep -Ef <(awk '{print "([^a-zA-Z0-9-]|^)"[=10=]"([^a-zA-Z0-9-]|$)"}' words.txt) file.txt
解释:
words.txt
是我的单词列表(每行一个)。
file.txt
是我要搜索的正文。
awk
命令将即时预处理words.txt
,将每个单词包装在一个特殊的正则表达式中以定义其正式的开始和结束(基于我上面问题中发布的规范).
awk
命令被 <(
和 )
包围,因此它的输出被用作 -f
选项的输入。
- 我正在使用
-E
选项,因为我现在输入的是正则表达式列表,而不是来自 words.txt
. 的固定字符串
这里的好处是 words.txt
可以保持人类可读性,并且不必包含一堆正则表达式模式。
注:
在进入正题之前,我想指出一些其他的 SO 帖子,它们没有完全回答我的问题,并且不是这个帖子的重复:
- How to grep with a list of words
- How to make grep only match if the entire line matches?
- how to grep for the whole word
- Grep extract only whole word
背景:
我在名为 words.txt
的文件中有一个单词列表(每行一个单词)。我想从另一个更大的文件 file.txt
中找到所有行,该文件包含 words.txt
中的任何单词。但是,我只想要 全字 匹配。这意味着当来自 file.txt
的一行至少包含一个来自 words.txt
的词被找到 "all by itself" 的实例时,应该进行匹配(我知道这是模糊的,所以请允许我解释一下).
换句话说,应该在以下情况下进行匹配:
- 这个词单独在一条线上
- 单词被 non-alphanumeric/non-hyphen 个字符包围
- 单词在一行的开头,后面跟着一个non-alphanumeric/non-hyphen字符
- 单词在一行的末尾,前面有一个 non-alphanumeric/non-hyphen 个字符
例如,如果 words.txt
中的一个词是 cat
,我希望它的行为如下:
cat #=> match
cat cat cat #=> match
the cat is gray #=> match
mouse,cat,dog #=> match
caterpillar cat #=> match
caterpillar #=> no match
concatenate #=> no match
bobcat #=> no match
catcat #=> no match
cat100 #=> no match
cat-in-law #=> no match
之前的研究:
有一个 grep
命令 几乎 适合我的需要。具体如下:
grep -wf words.txt file.txt
其中的选项是:
-w, --word-regexp
Select only those lines containing matches that form whole words.
The test is that the matching substring must either be at the beginning
of the line, or preceded by a non-word constituent character.
Similarly, it must be either at the end of the line or followed by a
non-word constituent character. Word-constituent characters are
letters, digits, and the underscore.
-f FILE, --file=FILE
Obtain patterns from FILE, one per line. The empty file contains
zero patterns, and therefore matches nothing.
我遇到的最大问题是它将连字符(即 -
)视为 "non-word constituent character"。因此(基于上面的例子)做一个 whole-word 搜索 cat
将 return cat-in-law
,这不是我想要的。
我意识到 -w
选项可能达到了很多人想要的效果。但是,在我的特殊情况下,如果一个单词(例如 cat
)是连字符 followed/preceded,那么我需要将其视为更大单词的一部分(例如 cat-in-law
)而不是单词本身的一个实例。
此外,我知道我可以更改 words.txt
以包含正则表达式而不是固定字符串,然后使用:
grep -Ef words.txt file.txt
哪里
-E, --extended-regexp
Interpret PATTERN as an extended regular expression
但是,我想避免更改 words.txt
并使其不受正则表达式模式的影响。
问题:
是否有一个简单的 bash 命令可以让我给它一个单词列表并对文本正文执行 全词 匹配?
终于想到了解决办法:
grep -Ef <(awk '{print "([^a-zA-Z0-9-]|^)"[=10=]"([^a-zA-Z0-9-]|$)"}' words.txt) file.txt
解释:
words.txt
是我的单词列表(每行一个)。file.txt
是我要搜索的正文。awk
命令将即时预处理words.txt
,将每个单词包装在一个特殊的正则表达式中以定义其正式的开始和结束(基于我上面问题中发布的规范).awk
命令被<(
和)
包围,因此它的输出被用作-f
选项的输入。- 我正在使用
-E
选项,因为我现在输入的是正则表达式列表,而不是来自words.txt
. 的固定字符串
这里的好处是 words.txt
可以保持人类可读性,并且不必包含一堆正则表达式模式。