正则表达式:在特定单词后替换空格

Regex: substitute spaces after specific word

提问人:batteredveg 提问时间:10/31/2023 最后编辑:InSyncbatteredveg 更新时间:10/31/2023 访问量:96

问:

我正在尝试(但失败了)编写一个正则表达式 (PCRE2),它将在特定单词(即 •VAN•、•VON• 或 •DE•)的第一个实例之后用破折号 (-) 替换每个空格,该单词本身必须被空格包围。

例如:

HENRIETTA VON DER GRAAF
CAROLINE VAN OOSTEN DE WINKEL
MARC DE VRIES VAN JONG
ANNEKA VANHOVEN BAKKER
JOHN WILKINSON SMITH

可以翻译为:

HENRIETTA VON-DER-GRAAF
CAROLINE VAN-OOSTEN-DE-WINKEL
MARC DE-VRIES-VAN-JONG
ANNEKA VANHOVEN BAKKER (NB: Does not match VAN as not surrounded by spaces)
JOHN WILKINSON SMITH (NB: No substitution here as pattern not matched)

这是我所知道的,但它并没有替换比赛后的所有空格:

\b( VON| VAN| DE)+\s

https://regex101.com/r/s6BC1y/1

任何建议,非常感谢!

正则表达式 取代 SAS PCRE

评论

1赞 Nick 10/31/2023
你用的是什么编程语言?请适当地标记您的问题。
0赞 Barmar 10/31/2023
哪些空间不能替代?
0赞 Barmar 10/31/2023
为什么你想要的结果在 和 之后?这不是你想要匹配的词之一。-OOSTENVRIES
0赞 Barmar 10/31/2023
为什么你有后捕获组?这将匹配单词的多个连续出现,并仅用最后一个替换它们。+
0赞 batteredveg 10/31/2023
从本质上讲,如果它找到 •VAN•、•VON• 或 •DE•,我希望它用之后的每个空格替换破折号(包括 VAN、VON 或 DE 后面的空格)。

答:

0赞 Gilles Quénot 10/31/2023 #1

使用 Perl:

perl -anE '
    if (/\b(?:VON|VAN|DE)\b/) {
        @a = split /\s+/;
        say $a[0], " ", join "_", @a[1..$#a]
    } else {
        print;
    }
' file

HENRIETTA VON_DER_GRAAF
CAROLINE VAN_OOSTEN_DE_WINKEL
MARC DE_VRIES_VAN_JONG
ANNEKA VANHOVEN BAKKER
JOHN SMITH
3赞 InSync 10/31/2023 #2

这可以通过以下方式完成:\G\K

(?:                # Match either
  (?<!\S)          #                      but only if it is not preceded by a whitespace,
  (?:VON|VAN|DE)   # 'VON', 'VAN' or 'DE'
|                  # or
  \G(?!\A)         # the end of the last match
  \S+              # then a sequence of non-whitespace characters.
)                  # 
\K\x20             # Forfeit everything we just match, then match a space.

在 regex101.com 上试用。

由于 PCRE2 中缺乏对非固定宽度后视的支持,我们无法执行以下操作,这可以说更容易理解:

(?<=               # Match a position preceded by
  (?:VON|VAN|DE)   # either of the three words
  (?:\x20\S+)*     # then 0 or more (space + word),
)                  # 
\x20               # and a space at that position.

在 regex101.com 上试用。

\G 匹配最后一个匹配项末尾的位置整个字符串的开头位置。多亏了 ,后一个备选方案只有在我们匹配第一个备选方案时才会匹配: 。(?!\A)(?<!\S)(?:VON|VAN|DE)

视觉解释:

MARC DE VRIES VAN JONG
     ^ Start matching `(?<!\S)(?:VON|VAN|DE)`
MARC DE VRIES VAN JONG
       ^ ...then `\x20`.
MARC DE VRIES VAN JONG
        ^ `(?<!\S)(?:VON|VAN|DE)` doesn't match here; switch to `\S+`
MARC DE VRIES VAN JONG
             ^ `\x20` is matched.
MARC DE VRIES VAN JONG
              ^ Back to step 1.
MARC DE VRIES VAN JONG
                  ^ Back to step 3.
2赞 Nick 10/31/2023 #3

您可以使用此正则表达式实现所需的结果:

^(.*? (?:VAN|VON|DE)) |((?<!^)\G\w+) 

这与以下任一匹配:

  • ^(.*? (?:VAN|VON|DE)) :行首后的一些最小字符数,后跟一个空格,以及 或 中的一个,全部捕获在第 1 组中,然后是一个空格;或VONVANDE
  • ((?<!^)\G\w+) :从上次成功匹配的末尾开始(但不从字符串的开头开始,通常允许)开始的一定数量的单词字符,在第 2 组中捕获,然后是一个空格\G

然后,您可以将匹配项替换为(只有一个或将包含任何内容)。$1$2-$1$2

regex101 上的正则表达式演示

请注意,正则表达式可以简化为丢弃匹配的第一部分,只匹配单词后面的空格:\K

^.*? (?:VAN|VON|DE)\K |(?<!^)\G\w+\K 

那么替换就是简单的.-

regex101 上的正则表达式演示

2赞 Richard 10/31/2023 #4

您可以在没有正则表达式的情况下进行转换。

data have;
input text $CHAR50.;
datalines;
HENRIETTA VON DER GRAAF
CAROLINE VAN OOSTEN DE WINKEL
MARC DE VRIES VAN JONG
ANNEKA VANHOVEN BAKKER
JOHN WILKINSON SMITH
;

data want;
  set have;
  p = prxmatch('m/\b(VAN|VON|DE)( )/',text);
  if 0 < p < length(text) then 
    substr(text,p+1) = translate(substr(trim(text),p+1),'-',' ');
run;

enter image description here

评论

0赞 Therkel 11/1/2023
“你可以在没有正则表达式的情况下进行转换”,使用让我感到困惑。prxmatch