正则表达式和Sed / Perl:匹配在ISN之前没有另一个单词的单词


11

我想使用sedperl替换前面没有特定单词的单词的所有出现。

例如,我有一个文本文件,其中包含电影情节,并且我希望将所有出现的角色姓氏替换为其名字,但前提是角色的姓氏不紧跟其姓氏。

示例文本可能如下所示:

John Smith and Jane Johnson talk about Smith's car.

我希望它看起来像这样:

John Smith and Jane Johnson talk about John's car.

如果我只是这样做sed 's/Smith/John/' file,那么我将有:

John John and Jane Johnson talk about John's car.

在姓氏之前的名字将始终相同。我不必处理John SmithFrank Smith。我只需要一种匹配的方法Smith,而没有John之前的方法。


您在说哪个sed?
伊格纳西奥·巴斯克斯

GNU在Linux上sed 4.2.1
jonescb 2011年

Answers:


8

对于正则表达式能够支持的任何语言,这将很容易。当然,Perl是列表中的第一个:

perl -pe 's/(?<!John\W)Smith/John/g' <<< "John Smith and Jane Johnson talk about Smith's car."

弱点是在“约翰”和“史密斯”之间有多个非单词字符。不幸的是,像+for \W这样的量词会引发“可变长度后方未实现”错误。


6

编辑 ..发表您的评论。.这是一个新脚本,它本身与(William Smith)无关。它会暂时混淆保持为Smith(不变)的模式。

sed -r 's/\<(John) (Smith)\>/\1\x01x\2/g; 
        s/\<Smith\>/John/g;  s/\x01x/ /g'

如果您担心Mrs Mrs太太,那么这可行。

sed -r 's/\<(John|((M(r|rs|s))\.?)) (Smith)\>/\1\x01x\5/g
        s/\<Smith\>/John/g; s/\x01x/ /g'

您可以通过在列表中添加他的名字来迎合William,例如。
sed -r 's/\<(William|John|...


这是原始脚本

sed -r 's/(^|[[:punct:]] |\<[a-z]+ )(Smith\>)/\1John/'

这行得通,但是我发现的一个问题是,如果将Smith之前的单词大写(例如,它出现在句子中的第一个单词之后),则说明该单词不匹配。通过manatwork编写的perl解决方案没有这个问题,即使在其他情况下也会失败。幸运的是,我的文本文件没有像先生这样的标题或姓氏相同的人。
jonescb

是的,谢谢...我发布了一个修改过的脚本...
Peter.O 2011年

1
 sed -r 's/([^John] )Smith/\1John/g;s/([^Jane] )Johnson/\1Jane/g'

()将在姓氏之前捕获非姓氏,因此在替换中将它们反向引用。

编辑

@manatwork,吉尔

你是对的。怎么样

sed -r 's/(John Smith)/temp1/g;s/Smith/John/g;s/temp1/John Smith/g'

这似乎可以解决问题。


如果名称前没有其他字词,例如“史密斯和简·约翰逊谈论史密斯的汽车”,这将失败。
manatwork

2
[^John]匹配必须是一个一个字符John。我怀疑这是您的意图。正则表达式中没有否定构造(Perl具有(?!…)(?<!…),但是如果您将其视为否定,则可能无法满足您的期望)。
吉尔(Gilles)“所以,别再邪恶了”,

@Juaco:您的take-2可以工作,但是容易受到意外数据的影响。我使用了类似的方法(尽管有点勉强),因为sed不使用它会导致膨胀的sed逻辑…… temp1几乎总是可以的,但是!提防那辆公共汽车。为了减轻这种可能性,我认为最好使用(几乎)在拉丁脚本文本文件中几乎不会出现的字符,例如十六进制值\ x01 \ x02或它们的组合,或者也许是\ xe188b4 UTF-8语言环境(ሴ- ETHIOPIC SYLLABLE SEE) echo -e 'Z' |sed 's/./\xe1\x88\xb4/'=> 当区域设置为UTF-8 ..
Peter.O
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.