grep 提取独特的大写短语

grep 提取独特的大写短语

下面的代码片段生成输入文本中所有大写单词的列表:

grep -o '[^ ]*[[:upper:]][^ ]*' book_text.txt > Capitalized_words.txt

现在我想提取并计算任意长度的唯一大写短语的出现次数。

也就是说,我想要一个共享首字母大写的空格分隔单词的唯一字符串的计数。给定的唯一短语不包含标点符号或非大写单词,因此University of British Columbia将是两个短语,UniversityBritish Columbia

输入示例:

Harvard archaeologists in Mexico also participated in the International
School of American Archaeology and Ethnology in Mexico City with scholars from
Mexico, Prussia and the United States.

预期输出:

1 - Harvard
1 - International School
1 - American Archaeology
1 - Ethnology
1 - Mexico City
2 - Mexico
1 - Prussia
1 - United States

请注意,在示例中,MexicoMexico City是共享一个单词的两个不同的独特短语。

答案1

如果使用 GNUgrep构建 PCRE 支持:

$ grep -Pow '(\p{Lu}\w*)(\s+(?1))*' input | sort | uniq -c
      1 American Archaeology
      1 Ethnology
      1 Example Input
      1 Harvard
      1 International School
      2 Mexico
      1 Mexico City
      1 Prussia
      1 United States

或者:

<input tr -s '[:space:]' '[ *]' |
  grep -Pow '(\p{Lu}\w*)(\s+(?1))*' |
  sort |
  uniq -c

首先将所有空白字符序列(包括换行符)转换为单个空格,例如Example InputExample InputExample\nInput被认为是相同的。

请注意,这-w不是为了空格分隔单词,单词边界位于单词和非单词字符之间(单词字符是数字和下划线)。你确实说你想要 以空格分隔的单词,但这与您的输入中的期待United States代替United States.Mexico代替相矛盾。Mexico, Prussia

另请注意,[^ ]*[[:upper:]][^ ]*将匹配空白分隔的单词包含至少有一个大写字母,但这不一定是在开头。例如,它将匹配fooBar0xAB+12。您需要(?<!\S)\p{Lu}\S*一个以大写字母开头的空格分隔的单词。

$ grep -Po '(?<!\S)(\p{Lu}\S*)(\s+(?1))*' input | sort | uniq -c
      1 American Archaeology
      1 Ethnology
      1 Example Input:
      1 International School
      1 Mexico
      1 Mexico City
      1 Mexico, Prussia
      1 United States."

Harvard丢失是因为我有一整行Example Input: "Havard ..."input所以空格分隔的单词"Harvard不是以大写字母开头)。

您还可以在中间添加一些ins 和s:of

$ grep -Pow '(\p{Lu}\w*)((\s+(in|of))?\s+(?1))*' input | sort | uniq -c
      1 Ethnology in Mexico City
      1 Example Input
      1 Harvard
      1 International School of American Archaeology
      2 Mexico
      1 Prussia
      1 United States

如果处理非英文文本,您可能还想替换\w((?=\w)\X),即代替单词字符,匹配一个以单词字符开头的字素簇

$ echo $'Universidad Nacional Auto\u0301noma de Me\u0301xico' |
  grep -Pow '(\p{Lu}\w*)((\s+(in|of|de))?\s+(?1))*' | sort | uniq -c
      1 Me
      1 Universidad Nacional Auto
$ echo $'Universidad Nacional Auto\u0301noma de Me\u0301xico' |
  grep -Pow '((?=\p{Lu})\X((?=\w)\X)*)((\s+(in|of|de))?\s+(?1))*' |
  sort | uniq -c
      1 Universidad Nacional Autónoma de México

那仍然会匹配Barin $'foo\u0301Bar'

您可能还需要细化单词字符/字素的构成和/或分隔符要涵盖的名称,例如苏西的厨房,奥布莱恩小学,让·保罗·萨特中学, ETC。

将所有这些放在一起,我们最终可以得到:

first_grapheme='(?: (?= \p{Lu} ) \X )'
word_character="[\w'-]"
 word_grapheme="(?: (?= $word_character ) \X )"
          word="$first_grapheme $word_grapheme *"
     separator='(?: [ ] (?: in | on | of | de | en ) )? [ ]'

<input tr -s '[:space:]' '[ *]' |
  grep -Po "(?x) (?<! \pM | $word_character ) $word (?: $separator $word ) *" |
  sort |
  uniq -c

答案2

使用 Raku(以前称为 Perl_6)

raku -e '.subst(",", " and ", :g).subst(".", " ", :g).comb(/ <( [ <:Lu> <:Ll>+ \h+ ]+ )> <:Ll>* /).map(*.trim-trailing).Bag.antipairs.join("\n").say for lines();'

输入示例:

Harvard archaeologists in Mexico also participated in the International School of American Archaeology and Ethnology in Mexico City with scholars from Mexico, Prussia and the United States.

示例输出(最终):

1   Ethnology
1   Prussia
2   Mexico
1   American Archaeology
1   Harvard
1   Mexico City
1   International School
1   United States

有趣的问题,我决定用 Raku 来解决它,因为它被认为有一个相当先进的正则表达式引擎(根据底部的参考文献,比 PCRE 更先进)。

我们可以首先将 Raku 代码分为三个主要部分。该comb部分使用正则表达式匹配器将文本输入分解为所需的元素。您可能已经熟悉许多符号(或概念上熟悉)。例如,Raku 中的捕获标记是<(…)>。仅此comb部分就返回 8/9 的预期值,尽管仅部分返回了 1 个。

raku -e '.comb(/ <( [ <:Lu> <:Ll>+ \h+ ]+ )> <:Ll>* /).join("\n").say for lines();'

示例输出(尝试#1):

Harvard 
Mexico 
International School 
American Archaeology 
Ethnology 
Mexico City 
Prussia 
United

我们立即看到必须对标点符号采取一些措施,因为值Mexico,和部分值States.都被留下了。第二次尝试:

raku -e '.subst(",", " and ", :g).subst(".", " ", :g).comb(/ <( [ <:Lu> <:Ll>+ \h+ ]+ )> <:Ll>* /).join("\n").say for lines();'

示例输出(尝试#2):

Harvard 
Mexico 
International School 
American Archaeology 
Ethnology 
Mexico City 
Mexico 
Prussia 
United States

以上返回 9/9 的预期值。最后我决定,用 , 替换逗号and,用空格替换.句号。 (您必须为您的文本决定最佳的操作方案)。

为了获得顶部的最终结果,.map(*.trim-trailing).Bag.antipairs在代码中插入了调用,从而产生了所需的结果。

https://slides.yowconference.com/yowwest2015/Conway-EverythingYouKnowAboutRegexesIsWrong.pdf
https://youtu.be/ubvSjW6Nyqk
https://raku.org

相关内容