【问题标题】:How to add the removed space in a sentence?如何在句子中添加删除的空格?
【发布时间】:2019-05-10 04:00:34
【问题描述】:

我有以下字符串:

x = "marchTextIWantToDisplayWithSpacesmarch"

我想删除字符串开头的 'march' 部分,然后在其余部分的每个大写字母之前添加一个空格,以产生以下结果:

"Text I Want To Display With Spacesmarch"

为了插入空白,我使用了gsub("([a-z]?)([A-Z])", "\\1 \\2", x, perl= T),但我不知道如何修改模式,以便从返回的字符串中排除第一个 'march'。我正在努力在这方面做得更好,所以任何帮助都将不胜感激。

【问题讨论】:

  • 您可以使用一个正则表达式,但要使用trimws: trimws(gsub("^\\p{Ll}+|(?!^)(?=\\p{Lu})", " ", x, perl=TRUE))。或者,您可以使用类似的正则表达式和回调对stringr::str_replace_all 进行一次调用(此处需要一些自定义替换逻辑)。

标签: r regex regex-lookarounds


【解决方案1】:

一种选择是将大写字母捕获为一个组 ((...)),并在替换中创建一个空格,后跟捕获组的反向引用 (\\1)

gsub("([A-Z])", " \\1", x)
#[1] "march Text I Want To Display With Spacesmarch"

如果我们需要删除'march'

sub("\\b[a-z]\\w+\\s+", "", gsub("([A-Z])", " \\1", x))
[#1] "Text I Want To Display With Spacesmarch"

数据

x <- "marchTextIWantToDisplayWithSpacesmarch"

【讨论】:

  • 感谢@akrun,但我希望学习如何在单个 gsub 中实现这两种效果,这可能吗?我尝试与(?:^[a-z]*) 这样的非捕获组一起玩,但没有奏效。
【解决方案2】:

不,您无法使用单个 gsub 实现替换,因为在您的一个要求中,您希望从头开始删除所有小写字母,而您的第二个要求是在每个大写字母之前引入一个空格,除了从文本开头删除所有小写字母后,结果字符串的第一个大写字母。

在单个gsub 调用中执行此操作是可能的,因为我们可以以某种方式重新使用一些现有字符来进行条件替换,这在此处是不可能的。所以第一步,你可以使用^[a-z]+正则表达式来去掉字符串开头的所有小写字母,

sub('^[a-z]+', '', "marchTextIWantToDisplayWithSpacesmarch")

把这个留给你,

[1] "TextIWantToDisplayWithSpacesmarch"

下一步,您可以使用此(?&lt;!^)(?=[A-Z]) 正则表达式在除第一个大写字母之外的每个大写字母之前插入一个空格,因为您可能不希望句子前有额外的空格。但是你可以把两者结合起来写成这样,

gsub('(?<!^)(?=[A-Z])', ' ', sub('^[a-z]+', '', "marchTextIWantToDisplayWithSpacesmarch"), perl=TRUE)

这将为您提供所需的字符串,

[1] "Text I Want To Display With Spacesmarch"

编辑: (?&lt;!^)(?=[A-Z])模式的解释

首先,让我们采用(?=[A-Z]) 模式,

See the pink markers in this demo

如您所见,在演示中,每个大写字母前面都有一个粉红色标记,即插入空格的位置。但是我们不希望在第一个字母之前插入空格,因为这不是必需的。因此我们需要一个正则表达式的条件,它不会选择出现在字符串开头的第一个大写字母。为此,我们需要在(?&lt;!^) 后面使用否定的外观,这意味着不要选择字符串开头之前的位置,因此(?&lt;!^) 有助于丢弃大写字母前面是字符串的开头。

See this demo where the pink marker is gone from the very first uppercase letter

希望这可以阐明如何选择其他所有大写字母,但不是第一个。如果您有任何疑问,请告诉我。

【讨论】:

  • 谢谢@Pushpesh。我不太明白外部 gsub 是如何工作的。我读到的模式是“获取所有后跟大写字母 (?=[A-Z]) 并且前面没有字符串开头 (?
  • @user51462:当然,让我添加细节和演示来澄清它。
  • @user51462:我已经为这个(?&lt;!^)(?=[A-Z]) 正则表达式添加了解释,我希望我能够澄清。如果没有,请随时进一步询问。
【解决方案3】:

您可以使用对gsub 的单个正则表达式调用加上trimws 来修剪结果字符串:

trimws(gsub("^\\p{Ll}+|(?<=.)(?=\\p{Lu})", " ", x, perl=TRUE))
## => [1] "Text I Want To Display With Spacesmarch"

它还支持所有 Unicode 小写 (\p{Ll}) 和大写 (\p{Lu}) 字母。

请参阅R demo onlineregex demo

详情

  • ^\\p{Ll}+ - 字符串开头有 1 个或多个小写字母
  • | - 或
  • (?&lt;=.)(?=\\p{Lu}) - 除换行符以外的任何字符与大写字母之间的任何位置。

这是一个替代方法,只需调用 gsubfn 正则表达式和一些 ifelse 逻辑:

> gsubfn("^\\p{Ll}*(\\p{L})|(?<=.)(?=\\p{Lu})", function(n) ifelse(nchar(n)>0,n," "), x, perl=TRUE,backref=-1) 
[1] "Text I Want To Display With Spacesmarch"

这里,^\\p{Ll}*(\\p{L}) 部分匹配 0+ 个小写字母,并将下一个大写字母捕获到组 1 中,该组将通过将 n 参数传递给匿名函数来访问。如果n 长度不为零,则此替代匹配并且我们需要用此值替换。否则,我们用空格替换。

【讨论】:

  • 感谢@Wiktor,正则表达式演示非常有用。我没想过要使用环视。也感谢您编辑问题并删除错误的 perl 标签。
  • @user51462 我添加了一个 sn-p,展示了如何将单个正则表达式解决方案与 gsubfn 一起使用。
  • gsubfn 解决方案更接近我的想法,因为它使用单个正则表达式模式。我会更改接受的答案以反映这一点,但我不确定这是否是不好的礼仪。尽管如此,还是感谢您的帮助,我对正则表达式还很陌生,所以我真的很感激。
【解决方案4】:

由于这被标记为 perl,我的 2 美分:

您能否将sub()gsub() 中的替换链接在一起?在较新的 perl 版本中,可以将 /r 选项添加到 s/// 替换中,因此可以“非破坏性地”返回匹配的字符串,然后再次匹配。这允许在不掌握高级语法的情况下进行骇人听闻的匹配/替换/重新匹配,例如

perl -E '
  say "marchTextIWantToDisplayWithSpacesmarch" =~
  s/\Amarch//r =~ s/([[:upper:]])/ $1/gr  =~ s/\A\s//r;'

输出

Text I Want To Display With Spacesmarch

这似乎是 @pushpesh-kumar-rajwanshi 和 @akrun 通过将 gsub 包裹在 sub() 中所做的(反之亦然)。总的来说,我不认为perl = T 捕捉到了 perl 正则表达式的全部高级疯狂;-),但gsub/sub 必须在向量上快速操作,不是吗?

【讨论】:

  • 这与 perl 无关,它是 R 基本正则表达式函数中的一个 about perl=TRUE 选项,可以使用 PCRE 正则表达式引擎。我从问题中删除了标签。
  • 谢谢@G。 Cito,我很抱歉使用了不正确的标签!我不认为替换可以在 R 中链接,但是看到它是如何在 perl 中完成的非常整洁,所以我赞成你的答案。
  • @user51462 不仅可以将一个函数放入另一个函数的参数中,如答案所示,还可以使用magrittr 包在R 中传递内容。
猜你喜欢
  • 2014-04-20
  • 2013-01-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-31
  • 2015-05-03
  • 1970-01-01
相关资源
最近更新 更多