【问题标题】:Parsing a very particular string structure in ruby (baseball data)在 ruby​​ 中解析一个非常特殊的字符串结构(棒球数据)
【发布时间】:2012-02-14 16:57:16
【问题描述】:

我正在尝试使用来自 retrosheet.org 的数据编写一个小型棒球统计程序,但我无法解析他们的线分数据。在一场比赛中,一支球队在任何特定局中都没有得分上双,他们的线得分看起来像这样001003000(他们在第三局得分 1 分,在第六局得分 3 分)。但是,如果一支球队的得分达到两位数,则数据将如下所示00100(10)000(第三场比赛有 1 场比赛,第六场比赛有 10 场比赛)。

现在,我只是试图解析出每一局的比分并将其放入一个数组中。这是我目前所拥有的:

scores = %w{00100300800 32004300X 00(11)34000 0000(15)000X 0000(15)000(13) 10(18)47(11)8(10)3}

scores.each do |s|
  game = []
  if s.include? "("
    # HELP!
  else 
    s.each_char { |c| game << c }
  end
  puts game.join("+")
end

我确定解决方案涉及正则表达式,我不擅长,所以我一直在尝试各种糟糕的字符串操作方法。最后,我认为寻求帮助会更好。

那么,你们会怎么做呢?

【问题讨论】:

  • 我会写一个非常小的解析器而不使用正则表达式。
  • 有趣的是,我能写的最小的解析器确实是slower而不是正则表达式。
  • @louism 如果您使用基于 C 的 StringScanner,它可能会执行得更快。
  • @Phrogz,很高兴知道。 String#[] 和 String#each_char 的表现似乎很糟糕。

标签: ruby regex string


【解决方案1】:

你可以用scan解析那些:

s.scan(/\(\d+\)|\d/)

例如:

>> scores = %w{00100300800 32004300X 00(11)34000 0000(15)000X 0000(15)000(13) 10(18)47(11)8(10)3}
>> scores.each { |s| puts s.scan(/\(\d+\)|\d/).inspect }
["0", "0", "1", "0", "0", "3", "0", "0", "8", "0", "0"]
["3", "2", "0", "0", "4", "3", "0", "0"]
["0", "0", "(11)", "3", "4", "0", "0", "0"]
["0", "0", "0", "0", "(15)", "0", "0", "0"]
["0", "0", "0", "0", "(15)", "0", "0", "0", "(13)"]
["1", "0", "(18)", "4", "7", "(11)", "8", "(10)", "3"]

然后去掉括号并调用to_i:

s.scan(/\(\d+\)|\d/).map { |s| s[/\d+/].to_i }

例如:

>> scores.each { |s| puts s.scan(/\(\d+\)|\d/).map { |s| s[/\d+/].to_i }.inspect }
[0, 0, 1, 0, 0, 3, 0, 0, 8, 0, 0]
[3, 2, 0, 0, 4, 3, 0, 0]
[0, 0, 11, 3, 4, 0, 0, 0]
[0, 0, 0, 0, 15, 0, 0, 0]
[0, 0, 0, 0, 15, 0, 0, 0, 13]
[1, 0, 18, 4, 7, 11, 8, 10, 3]

【讨论】:

    【解决方案2】:

    你可以这样做:

    str = '00(11)34000'
    str.scan(/\d{1}|\(\d{2}\)/).map { |a| a.gsub(/[()]/, '') }
    # => ["0", "0", "11", "3", "4", "0", "0", "0"]
    

    在这里我得到像["0", "0", "(11)", "3", "4", "0", "0", "0"] 这样的数组并删除所有()。我在这里不使用复杂的正则表达式 - 反而很难阅读。

    【讨论】:

      【解决方案3】:

      这是一个非常小的解析器,可以满足您的需要:

      scores = %w{00100300800 32004300X 00(11)34000 0000(15)000X 0000(15)000(13) 10(18)47(11)8(10)3}
      games = []
      
      scores.each do |s|
      
        game = []
        cs = s.split('')
      
        cs.each_with_index do |c,i| 
          if cs[i-2] == '('
            game[-1] += c
          elsif !(c == '(' || c == ')')
            game << c
          end
        end
      
        games << game
      
      end
      

      注意它的slower 比使用正则表达式。

      【讨论】:

        【解决方案4】:

        翻转:

        str = "0000(15)000X"
        tmp = ""
        res = []
        str.each_char do |c|
          if c=='(' .. c==')' #flip-flop, read like "If we are between '('and ')'.
            tmp << c
          else
           unless tmp.empty?
             res << tmp[1..-2] 
             tmp = ""
           end 
           res << c
          end
        end
        p res #=>["0", "0", "0", "0", "15", "0", "0", "0", "X"]
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-07-16
          • 2011-04-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-03-18
          相关资源
          最近更新 更多