使用 sed 维护替换词的复数性和适当的大写

Maintaining plurality and proper capitalization of replaced words using sed

我知道这个标题很糟糕,但无论如何,我有一个任务要使用 sed 将 "cat" 的所有实例更改为 "dog"。很简单,但它还包括像 "catapult" 和 "bearcat" 这样的词,我试图通过在代码中放置 space 来避免这些词。我的问题是每个单词都变成了 "dog",在某些情况下我希望它变成 "Dog" 或 "dogs"...

这是我要更改的文本文件:

Dear Homeowner,

Cats are important to people. We all enjoy the company of cats. If you have ever wanted to own a cat we can help. We are attempting to hold a “cat comes home” day for our city. To help us we've enlisted the NWMSU Bearcat cheerleaders, the organizers of the city's annual catapult toss, and local celebrities to help find homes for cats.

There is a cat that needs you to provide a home for them. So if you are a cat lover, please come and see if there isn't some way to find a home in your heart for a cat.

Thanks!!

Cats are people too.

这是我得到的明显错误的输出

Dear Homeowner,

dog are important to people. We all enjoy the company of cats. If you have ever wanted to own a cat we can help. We are attempting to hold a “cat comes home” day for our city. To help us we've enlisted the NWMSU Bearcat cheerleaders, the organizers of the city's annual catapult toss, and local celebrities to help find homes for cats.

There is a cat that needs you to provide a home for them. So if you are a cat lover, please come and see if there isn't some way to find a home in your heart for a cat.

Thanks!!

dog are people too.

这是我的代码:

sed 's/[Cc]at[s] /dog /g' cats-dogs.txt 

我很确定您不能仅在(单个)RegEx 中执行此操作。

也就是说,简单的解决方案可能是这里最好的,因为似乎只有两种可能的情况(大写和小写)和一个替换词(而且 sed 允许轻松进行多个替换)。

所以像这样的东西应该可以工作(假设 GNU sed):

sed -r 's/\bCat(s?)\b/Dog/g; s/\bcat(s?)\b/dog/g' cats-dogs.txt

使用扩展的正则表达式,因为在命令行中引用它远没有那么可怕。也请注意此处的单词边界扫描。

可能有一种非常聪明(且不可读)的sed方法可以使用\u和缓冲区来做到这一点..

让我们分析一下你到目前为止的尝试。

s/[Cc]at[s] /dog /g

这将搜索正则表达式 [Cc]at[s] 并替换 dog。它不起作用的原因有几个...

  • 首字母大写失败。
  • 第二个区间,[s]就是"the letter s".

如果您使用的是 Linux,那么您系统上安装的 sed 版本可能是 GNU sed,以下可能适用:

sed -r 's/\bcat(s?)\b/dog/g;s/\bCat(s?)\b/Dog/g'

注意 -r 选项,它告诉 sed 使用 "Extended" 正则表达式表示法而不是默认的 "Basic" 表示法。

此解决方案依赖于 sed 对 \b 字边界的理解,但重要的是要注意此 shorthand 在其他操作系统(FreeBSD,OSX、Solaris 等)。如果便携性很重要,请避免使用 \b 和类似的东西。

这个 shorthand 很好,但实际上不是必需的。这是 BRE 中的相同内容:

sed 's/[[:<:]]cat\(s*\)[[:>:]]/dog/g;s/[[:<:]]Cat\(s*\)[[:>:]]/Dog/g'

这是 BRE 而不是 ERE,所以我们不使用 -r 选项。我应该指出,这也将匹配 "catssss",因为我们使用 s* 而不是 s?。许多 sed 实现中的 BRE 不包括识别 一个 原子出现的方法。

传统的 类 [[:<:]][[:>:]] 适用于单词的开头或结尾,有时可能优于 GNU sed 的 "word boundary" 可以使用用于单词的开头或结尾。

非 GNU RE 格式可以在任何具有 man re_format 的 unix 上看到。

(注意: sed 的 -r 选项也不是通用的。在 OSX 中,使用 -E 代替。这是因为 OSX 的 sed 是从旧版本的 FreeBSD 派生出来的,它只是在几个版本前添加了 -r 作为 -E 的等效选项。)

使用 perl,但它并不漂亮:

perl -pe 's/\b(c)at(?=s?\b)/  =~ m{[[:upper:]]} ? "Dog" : "dog" /ige' <<END
scat cat cats Cats Cat Catskills 
END

产出

scat dog dogs Dogs Dog Catskills