【问题标题】:Exactly two capitalized words on a line一行正好两个大写单词
【发布时间】:2018-05-27 05:58:16
【问题描述】:

我想创建一个正则表达式,它可以用字符“X”替换恰好包含两个以大写开头的单词的行。

我目前正在使用这个:

sed -e '/\b[A-Z][a-z]*\b c X /home/Morgan/desktop/test

问题如下:它只更改包含 1 个或多个单词的行,这些单词由我的 test.txt 中的正则表达式描述。

我不知道怎么说,我只想要一个 X 只在正好有 2 个以大写开头的单词的行上。任何一个词都可以出现在该行中的任何位置。

我的 test.txt 包含:

Bonjour oui oui Bonjour -> 必须用 X 替换

Bonjour Bonjour Bonjour -> 这绝对不能

Bonjour Oui bonjour oui -> 必须用 X 替换

【问题讨论】:

  • sed -e '/\b[A-Z][a-z]*\b c X /home/Morgan/desktop/test 语法无效。您能否提供一个您当前尝试的工作示例?
  • 您当前的尝试是语法错误。您是否试图用某些东西替换正则表达式? sed 无论如何都不懂 \b
  • @tripleee 由于 OP 已标记 ubuntu,我假设 GNU sed 可用,因此 \b 将被识别为单词边界
  • @Sundeep 的确如此。感谢您的提示。

标签: regex ubuntu sed


【解决方案1】:

您似乎正在尝试使用 Perl/PCRE 字边界 \b 但典型的 sed 实现不理解这种正则表达式方言。根据您的问题描述,无论如何,您正在寻找行首和行尾;这是一个非常基本的正则表达式锚,已在原始 grep 中引入:^ 匹配行首,$ 匹配行尾。

没有锚,正则表达式将匹配行中的任何位置。要说“只有两个”,您确实必须检查整行并确保没有三个或更多您要查找的内容。

在您尝试编写正则表达式之前,需要重新措辞或修改一下“查找正好包含两个以大写开头的单词的行”。如果我们——在本次讨论中——暂时将w定义为“不以大写开头的单词”,将W定义为以大写开头的单词,那么您想要^w*Ww*Ww*$——正好是两个大写单词和零或多个非大写单词,位于它们之前、之间或之后的任何位置。

以大写开头的单词是[A-Z][a-z]*(这要求所有后续字符都是小写),而不是[a-z][a-z]*(如果您的sed支持该正则表达式变体,则为[a-z]\+) .

因为单词之间需要空格,可选的单词表达式需要用括号括起来,这样您就可以说“整个序列的零个或多个”。通常,sed 正则表达式也要求分组括号也使用反斜杠,尽管这在版本之间有所不同。

所以,试试这个:

sed 's/^\([a-z][a-z]* \)*[A-Z][a-z]*\( [a-z][a-z]*\)* [A-Z][a-z]*\( [a-z][a-z]*\)*$/X/' file

如果你确实有 GNU sed,这可以简化一点:

sed -r 's/^([a-z]+ )*[A-Z][a-z]*( [a-z]+)* [A-Z][a-z]*( [a-z]+)*$/X/' file

这个“词”的定义可能还不够;也许您可以根据自己的情况对其进行改进。特别是,假定间距是规则的(单词之间正好有一个空格;行上没有前导或尾随空格),并且任何文本都不能包含空格之外的字符以及大写或小写的字母 a-z。 (像 è 和 Á 这样的重音字符是否也被视为此范围内的字母取决于您的语言环境设置。如果法语语言环境设置很重要,可以在您的脚本中设置 LC_ALL=fr_FR.utf-8。)

还请注意sed 替换命令如何需要恰好三个分隔符 - 传统上,我们使用斜杠,但您可以使用任何标点符号。格式为 s/regex/replacement/flags,其中正则表达式、替换和标志都可以为空,但始终需要 s 和分隔符。

【讨论】:

  • 确实,OP 应该澄清什么是有效输入。例如:have 1 Mango and 3 Apples 是否应该更改? sed -E '/^([^A-Z]*\b[A-Z][a-z]*\b[^A-Z]*){2}$/c X' 在这种情况下可能会有所帮助,但可能整个事情更适合带有字段拆分而不是复杂的正则表达式的 awk/perl
  • 完美运行,感谢@tripleee 和其他人
猜你喜欢
  • 2014-02-04
  • 1970-01-01
  • 2017-12-11
  • 1970-01-01
  • 2011-09-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多