【问题标题】:How to combine these two Ruby string tests in one regular expression?如何将这两个 Ruby 字符串测试组合在一个正则表达式中?
【发布时间】:2016-09-07 07:59:32
【问题描述】:

我正在尝试在 Ruby 中编写一个正则表达式,我想查看字符串是否包含某个单词(例如“字符串”),后跟括号中的 url 和链接名称。

我现在正在做:

string.include?("string") && string.scan(/\(([^\)]+)\)/).present?

我在两个条件中的输入都是一个字符串。在第一个中,我正在检查它是否包含“链接”一词,然后我将在括号中包含链接和链接名称,如下所示:

"Please go to link( url link_name)"

验证后,我提取 HTML 链接。

有没有办法可以使用正则表达式组合它们?

【问题讨论】:

  • 您能否提供一个输入示例以及如何根据这些示例来想象输出?
  • @RionWilliams 我在两个条件句中的输入基本上都是一个字符串。在第一个中,我正在检查它是否包含“链接”这个词,然后我将在一对括号之间放置一些东西。链接和链接名称将存储在括号中。例如“请转到链接(url link_name)”然后我正在做一些字符串操作以将其更改为 html 链接。

标签: ruby-on-rails ruby regex string ruby-on-rails-4


【解决方案1】:

您可以做的最重要的改进是还要测试单词和括号之间的关系是否正确。如果我理解正确,"link(url link_name)" 应该匹配,但 "(url link_name)link""link stuff (url link_name)" 不应该匹配。所以匹配"link"、括号和它们的内容,并一次捕获内容:

"stuff link(url link_name) more stuff".match(/link\((\S+?) (\S+?)\)/)&.captures
=> ["url", "link_name"]

&. is Ruby 2.3;在旧版本中使用 Rails 的.try :captures。)

旁注:string.scan(regex).present? 更简洁地写成string =~ regex

【讨论】:

  • 如果没有匹配,您将获得nil.captures,这会引发异常。此外,您的正则表达式需要在括号表达式之前加上“特定单词”,但顺序不是规范的一部分:"stuff (url link_name) link more stuff".match(/link\((\S+?) (\S+?)\)/) #=> nil
  • 一个字符避免了异常。我从 learningruby347 对他们问题的评论中推断出规范,他们似乎同意,但是,learningruby347,如果我误解了,就说这个词。
  • 很好的修复(!),但你应该解释&.(至少是一个链接)给新手和那些不了解最近对 Ruby 进行更改的人。 (我本来建议使用scan而不是match,但是你必须使用flatten,当没有匹配时会导致同样的问题。¯\_(ツ)_/¯)跨度>
【解决方案2】:

检查单词是否包含

如果您想在字符串中的某处找到包含特定单词的匹配项,您可以通过前瞻来完成:

# This will match any string that contains your string "{your-string-here}"
(?=.*({your-string-here}).*).*

您可以考虑构建表达式的字符串版本并使用变量传递您要查找的单词:

wordToFind = "link"
if stringToTest =~ /(?=.*(#{wordToFind}).*).*/
    # stringToTest contains "link"
else
    # stringToTest does not contain "link"
end

检查单词和括号

如果您还想确保字符串中的某处有一组括号,其中包含一些内容您之前对单词的前瞻,您可以使用:

# This will match any strings that contain your word and contain a set of parentheses 
(?=.*({your-string-here}).*).*\([^\)]+\).*

可能用作:

wordToFind = "link"
if stringToTest =~ /(?=.*(#{wordToFind}).*).*\([^\)]+\).*/
    # stringToTest contains "link" and some non-empty parentheses
else
    # stringToTest does not contain "link" or non-empty parentheses
end

【讨论】:

  • 抱歉,如何在 Ruby 中使用它?
  • 我添加了一些可能工作的示例,尽管我已经有一段时间没有编写任何 Ruby。
  • 在这种情况下,您可能希望使用#{Regexp.escape(wordToFind)} 而不是#{wordToFind}
  • 只是为了唤起你的记忆,这是蛇形 Ruby。 :-)
【解决方案3】:
def has_both?(str, word)
  str.scan(/\b#{word}\b|(?<=\()[^\(\)]+(?=\))/).size == 2
end

has_both?("Wait for me, Wild Bill.", "Bill")
  #=> false 
has_both?("Wait (for me), Wild William.", "Bill")
  #=> false 
has_both?("Wait (for me), Wild Billy.", "Bill")
  #=> false 
has_both?("Wait (for me), Wild bill.", "Bill")
  #=> false 
has_both?("Wait (for me, Wild Bill.", "Bill")
  #=> false 
has_both?("Wait (for me), Wild Bill.", "Bill")
  #=> true 
has_both?("Wait ((for me), Wild Bill.", "Bill")
  #=> true 
has_both?("Wait ((for me)), Wild Bill.", "Bill")
  #=> true 

这些是计算

word = "Bill"
str = "Wait (for me), Wild Bill."

r = /
    \b#{word}\b  # match the value of the variable 'word' with word breaks for and aft
    |         # or
    (?<=\()   # match a left paren in a positive lookbehind
    [^\(\)]+  # match one or more characters other than parens
    (?=\))    # match a right paren in a positive lookahead
    /x        # free-spacing regex definition mode
  #=> /
      \bBill\b  # match the value of the variable 'word' with word breaks for and aft
      |         # or
      (?<=\()   # match a left paren in a positive lookbehind
      [^\(\)]+  # match one or more characters other than parens
      (?=\))    # match a right paren in a positive lookahead
      /x 

arr = str.scan(r)
  #=> ["for me", "Bill"]
arr.size == 2
  #=> true

【讨论】:

    【解决方案4】:

    我会使用这样的正则表达式:

    /link\s*\(([^\)\s]+)\s*([^\)]+)?\)/i
    

    这将找到以单词 link 开头的任何匹配项,后跟任意数量的空格,然后是 url,后跟链接名称,都在括号中。在这个正则表达式中,链接名称是可选的,但 url 不是。匹配不区分大小写,所以会匹配linkLINK 完全相同。

    您可以使用Regexp#match 方法将正则表达式与字符串进行比较,并检查匹配和捕获的结果,如下所示:

    m = /link\s*\(([^\)\s]+)\s*([^\)]+)?\)/i.match("link (stackoverflow.com StackOverflow)")
    if m  # the match array is not nil
      puts "Matched: #{m[0]}"
      puts " -- url: {m[1]}"
      puts " -- link-name: #{m[2] || 'none'}"
    else  # the match array is nil, so no match was found
      puts "No match found"
    end
    

    如果您想使用不同的字符串来识别匹配项,您可以使用 non-capturing 组,将link 更改为:

    (?:link|site|website|url)
    

    在这种情况下,(?: 语法表示不捕获这部分匹配。如果要捕获匹配的术语,只需将其从 (?: 更改为 (,并将捕获索引调整 1 以考虑新的捕获值。

    这是一个简短的 Ruby 测试程序:

    data = [
      [ true, "link (http://google.com Google)", "http://google.com", "Google" ],
      [ true, "LiNk(ftp://website.org)", "ftp://website.org", nil ],
      [ true, "link   (https://facebook.com/realstanlee/ Stan Lee) linkety link", "https://facebook.com/realstanlee/", "Stan Lee" ],
      [ true, "x  link (https://mail.yahoo.com Yahoo! Mail)", "https://mail.yahoo.com", "Yahoo! Mail" ],
      [ false, "link lunk (http://www.com)", nil, nil ]
    ]
    
    data.each do |test_case|
      link = /link\s*\(([^\)\s]+)\s*([^\)]+)?\)/i.match(test_case[1])
      url = link ? link[1] : nil
      link_name = link ? link[2] : nil
      success = test_case[0] == !link.nil?  && test_case[2] == url && test_case[3] == link_name
      puts "#{success ? 'Pass' : 'Fail'}: '#{test_case[1]}' #{link ? 'found' : 'not found'}"
      if success && link
        puts " -- url: '#{url}' link_name: '#{link_name || '(no link name)'}'"
      end
    end
    

    这会产生以下输出:

    Pass: 'link (http://google.com Google)' found
     -- url: 'http://google.com' link_name: 'Google'
    Pass: 'LiNk(ftp://website.org)' found
     -- url: 'ftp://website.org' link_name: '(no link name)'
    Pass: 'link   (https://facebook.com/realstanlee/ Stan Lee) linkety link' found
     -- url: 'https://facebook.com/realstanlee/' link_name: 'Stan Lee'
    Pass: 'x  link (https://mail.yahoo.com Yahoo! Mail)' found
     -- url: 'https://mail.yahoo.com' link_name: 'Yahoo! Mail'
    Pass: 'link lunk (http://www.com)' not found
    

    如果您想在单词“链接”和第一个括号之间允许除空格以外的任何内容,只需将 \s* 更改为 [^\(]* 即可。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-01-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-25
      相关资源
      最近更新 更多