【问题标题】:How to split a string without getting an empty string inserted in the array如何拆分字符串而不在数组中插入空字符串
【发布时间】:2017-08-01 10:21:39
【问题描述】:

假设存在匹配项,我无法使用正则表达式从字符串中拆分字符。

我想从字符串的第一部分中分离出一个“m”或一个“f”字符,假设下一个字符是一个或多个数字,后跟可选的空格字符,然后是我拥有的数组中的字符串.

我试过了:

2.4.0 :006 > MY_SEPARATOR_TOKENS = ["-", " to "]
 => ["-", " to "] 
2.4.0 :008 > str = "M14-19"
 => "M14-19" 
2.4.0 :011 > str.split(/^(m|f)\d+[[:space:]]*#{Regexp.union(MY_SEPARATOR_TOKENS)}/i)
 => ["", "M", "19"] 

请注意我的数组开头的多余“”元素,还请注意最后一个表达式只是“19”,而我想要字符串中的所有其他内容(“14-19”)。

如何调整我的正则表达式,以便只有被拆分的表达式部分最终出现在数组中?

【问题讨论】:

  • 我不知道您所说的“拆分”或“字符串的第一部分”是什么意思。如果原始字符串是str,当str[0] =~ /[mf]/i 满足其他两个条件时,您是否希望返回str[1..-1]?不匹配返回什么,str

标签: ruby regex split


【解决方案1】:

我发现match 在 Ruby 中从正则表达式中提取字符时更加优雅:

string = "M14-19"
string.match(/\A(?<m>[M|F])(?<digits>\d{2}(-| to )\d{2})/)[1, 2]
=> ["M", "14-19"]
# also can extract the symbols from match
extract_string = string.match(/\A(?<m>[M|F])(?<digits>\d{2}(-| to )\d{2})/)
[[extract_string[:m], extract_string[:digits]]
=> ["M", "14-19"]
string = 'M14 to 14'
extract_string = string.match(/\A(?<m>[M|F])(?<digits>\d{2}(-| to )\d{2})/)[1, 2]
=> ["M", "14 to 14"]

【讨论】:

    【解决方案2】:
     TOKENS = ["-", " to "]
    
     r = /
         (?<=\A[mMfF])             # match the beginning of the string and then one
                                   # of the 4 characters in a positive lookbehind
         (?=                       # begin positive lookahead
           \d+                     # match one or more digits
           [[:space:]]*            # match zero or more spaces
           (?:#{TOKENS.join('|')}) # match one of the tokens
         )                         # close the positive lookahead
         /x                        # free-spacing regex definition mode
    

    (?:#{TOKENS.join('|')}) 替换为(?:-| to )

    这当然可以用通常的方式来写。

    r = /(?<=\A[mMfF])(?=\d+[[:space:]]*(?:#{TOKENS.join('|')}))/
    

    r 上拆分时,两个字符之间(在正向后视和正向前瞻之间)进行拆分,因此不会消耗任何字符。

    "M14-19".split r
      #=> ["M", "14-19"]
    "M14     to 19".split r
      #=> ["M", "14     to 19"]
    "M14     To 19".split r
      #=> ["M14     To 19"]
    

    如果希望在最后一个示例中返回["M", "14 To 19"],请将[mMfF] 更改为[mf] 并将/x 更改为/xi

    【讨论】:

    • (?:#{TOKENS.join('|')}) 以前是 #{Regexp.union(TOKENS)}。我在阅读@thetinman 的回答后更改了它,这阐明了我对Regexp::union 及其潜在陷阱的理解。
    【解决方案3】:

    您的代码中正在酝酿一个错误。不要养成这样做的习惯:

    #{Regexp.union(MY_SEPARATOR_TOKENS)}
    

    您正在为自己设置一个非常难以调试的问题。

    这是发生了什么:

    regex = Regexp.union(%w(a b)) # => /a|b/
    /#{regex}/ # => /(?-mix:a|b)/
    /#{regex.source}/ # => /a|b/
    

    /(?-mix:a|b)/ 是一个嵌入的子模式,其正则表达式标志集 mix 独立于周围模式的设置。

    考虑这种情况:

    'CAT'[/#{regex}/i] # => nil
    

    我们希望正则表达式 i 标志会匹配,因为它忽略大小写,但子表达式仍然只允许小写,导致匹配失败。

    使用裸(a|b)或添加source成功,因为内部表达式获取主表达式的i

    'CAT'[/(a|b)/i] # => "A"
    'CAT'[/#{regex.source}/i] # => "A"
    

    有关此问题的更多讨论,请参阅“How to embed regular expressions in other regular expressions in Ruby”。

    【讨论】:

      【解决方案4】:

      如果你得到一个匹配,空元素将始终存在,因为捕获的部分出现在字符串的开头,并且字符串开头和匹配之间的字符串被添加到结果数组中,无论是空的或非空字符串。一旦你得到一个匹配,要么shift/drop它,要么用.reject { |c| c.empty? }删除所有空数组元素(见How do I remove blank elements from an array?)。

      然后,14-\d+[[:space:]]... 模式部分吃掉(消耗) - 将其放入(?=...) 前瞻中,它将仅检查模式匹配,但不会消耗字符。

      使用类似的东西

      MY_SEPARATOR_TOKENS = ["-", " to "]
      s = "M14-19"
      puts s.split(/^(m|f)(?=\d+[[:space:]]*#{Regexp.union(MY_SEPARATOR_TOKENS)})/i).drop(1)
      #=> ["M", "14-19"]
      

      Ruby demo

      【讨论】:

      • 你能不能把reject换成drop 1,这样更形象?
      • @CarySwoveland:我修改了代码示例。 .drop(1) 只是摆脱第一个数组元素的另一种方法,但在大多数情况下,删除所有空元素是预期的行为。
      猜你喜欢
      • 2011-11-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多