【问题标题】:Ruby split string and preserve separatorRuby拆分字符串并保留分隔符
【发布时间】:2016-11-26 16:45:30
【问题描述】:

在 Ruby 中,以下列方式拆分字符串最简单的方法是什么?

  • 'abc+def' 应该拆分为['abc', '+', 'def']

  • 'abc\*def+eee' 应该拆分为['abc', '\*', 'def', '+', 'eee']

  • 'ab/cd*de+df' 应该拆分为 ['ab', '/', 'cd', '\*', 'de', '+', 'df']

想法是拆分关于这些符号的字符串:['-', '+', '*', '/'],并将这些符号保存在结果中的适当位置。

【问题讨论】:

  • 提示:.split 接受正则表达式
  • 欢迎来到 Stack Overflow。请阅读“How to Ask”和链接页面。我们需要看到您努力解决问题的证据。你写代码了吗?如果没有,为什么不呢?您在哪里搜索以找到解决方案?如果你没有搜索,为什么?目前,您的问题看起来像是请求我们为您编写代码,这不是 SO 的用途。
  • @theTinMan 对不起,关于那个。我会在以后提问时牢记这一点。

标签: ruby string symbols


【解决方案1】:

选项 1

/\b/word boundary,它的宽度为零,因此不会消耗任何字符

'abc+def'.split(/\b/)
# => ["abc", "+", "def"]

'abc*def+eee'.split(/\b/)
# => ["abc", "*", "def", "+", "eee"]

'ab/cd*de+df'.split(/\b/)
# => ["ab", "/", "cd", "*", "de", "+", "df"]

选项 2

如果您的字符串包含其他单词边界字符并且您只想在-+*/ 上进行拆分,那么您可以使用capture groups。如果使用捕获组,String#split 还将在结果中包含捕获的字符串。 (感谢您指出这一点 @Jordan)(@Cary Swoveland 抱歉,我在进行此编辑时没有看到您的回答)

'abc+def'.split /([+*\/-])/
# => ["abc", "+", "def"]

'abc*def+eee'.split /([+*\/-])/
# => ["abc", "*", "def", "+", "eee"]

'ab/cd*de+df'.split /([+*\/-])/
# => ["ab", "/", "cd", "*", "de", "+", "df"]

选项 3

最后,对于那些使用可能不支持使用捕获组进行字符串拆分的语言的人,您可以使用两个lookarounds。 Lookarounds 也是零宽度匹配,因此它们不会消耗任何字符

'abc+def'.split /(?=[+*\/-])|(?<=[+*\/-])/
# => ["abc", "+", "def"]

'abc*def+eee'.split /(?=[+*\/-])|(?<=[+*\/-])/
# => ["abc", "*", "def", "+", "eee"]

'ab/cd*de+df'.split /(?=[+*\/-])|(?<=[+*\/-])/
# => ["ab", "/", "cd", "*", "de", "+", "df"]

这里的想法是分割前面有一个分隔符的任何字符,或者后面有一个分隔符的任何字符。让我们做一点视觉效果

ab ⍿ / ⍿ cd ⍿ * ⍿ de ⍿ + ⍿ df

符号前面或后面是分隔符之一。所以这就是字符串将被切断的地方。


选项 4

也许您的语言没有字符串split 函数或与正则表达式交互的合理方式。很高兴知道您不必坐下来猜测是否有巧妙的内置程序可以神奇地解决您的问题。几乎总有一种方法可以使用基本说明来解决您的问题

class String
  def head
    self[0]
  end
  def tail
    self[1..-1]
  end
  def reduce acc, &f
    if empty?
      acc
    else
      tail.reduce yield(acc, head), &f
    end
  end
  def separate chars
    res, acc = reduce [[], ''] do |(res, acc), char|
      if chars.include? char
        [res + [acc, char], '']
      else
        [res, acc + char]
      end
    end
    res + [acc]    
  end
end

'abc+def'.separate %w(- + / *)
# => ["abc", "+", "def"]

'abc*def+eee'.separate %w(- + / *)
# => ["abc", "*", "def", "+", "eee"]

'ab/cd*de+df'.separate %w(- + / *)
# => ["ab", "/", "cd", "*", "de", "+", "df"]

【讨论】:

  • 太棒了!尤其是环视。非常感谢。干杯!
  • 很好的解释。
  • 环顾四周是多余的。 String#split 将保留捕获,因此您只需要 string.split /([+*\/-])/
  • 第二个示例字符串是'abc\*def+eee',不是'abc*def+eee',但答案很好。
  • @CarySwoveland 原来的问题不是这样写的:(
【解决方案2】:

我认为这与@naomic 的部分答案很接近,但我会留下一些小的差异。

splitters = ['-', '+', '*', '/']

r = /(#{ Regexp.union(splitters) })/ 
  # => /((?-mix:\-|\+|\*|\/))/

'abc+def'.split r
  #=> ["abc", "+", "def"] 
"abc\*def+eee".split r
  #=> ["abc", "*", "def", "+", "eee"] 
'ab/cd*de+df'.split r
  #=> ["ab", "/", "cd", "*", "de", "+", "df"] 

注意事项:

  • 正则表达式将#{ Regexp.union(splitters) } 置于捕获组中,导致String#split 包含进行拆分的字符串(第三段的最后一句)。
  • 第二个示例字符串必须用双引号括起来才能转义*

【讨论】:

  • 很好地使用了Regexp.union。很高兴我了解到String#split 允许使用捕获组。我以前从来不知道^_^
猜你喜欢
  • 1970-01-01
  • 2022-11-03
  • 2013-08-08
  • 1970-01-01
  • 2018-12-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多