Ruby - 将多行制表符分隔的字符串解析为数组数组

Ruby - Parse a multi-line tab-delimited string into an array of arrays

如果在 Ruby 设置中已经有人问过这个问题,我深表歉意——我在发帖前进行了检查,但老实说,这是漫长的一天,如果我遗漏了明显的问题,我深表歉意前进!

我有以下字符串,其中包含系统上安装的软件包列表,由于某种原因,我很难解析它。我知道在 Ruby 中必须有一个直接的方法来做到这一点,但我一直在做空。

我想将下面的多行、制表符分隔的字符串解析为一个数组数组,然后我可以在其中使用 each_with_index 循环遍历每个数组元素并吐出 HTML 代码到我的 Rails 应用程序中。

str = 'Product and/or Software Full Name 5242     [version 6.5.24]     [Installed on: 12/31/2015]

 Product and/or Software Full Name 5426     [version 22.4]     [Installed on: 06/11/2013]

 Product and/or Software Full Name 2451     [version 1.63]     [Installed on: 12/17/2015]

 Product and/or Software Full Name 5225     [version 43.22.51]     [Installed on: 11/15/2011]

 Product and/or Software Full Name 2420     [version 43.51-r2]     [Installed on: 12/31/2015]'

最终结果将是一个包含 5 个元素的数组,如下所示:

[["Product and/or Software Full Name 5245"],["version 6.5.24"], ["Installed on: 12/31/2015"],["Product and/or Software Full Name 5426"],["version 22.4"],["Installed on: 06/11/2013"],["Product and/or Software Full Name 2451"],["version 1.63"],["Installed on: 12/17/2015"]]

请注意:为简洁起见,仅显示了 5 个阵列中的 3 个

我更愿意从 'version' 和 'Installed on' 中去掉括号,但如果不能轻易将其转化为答案,我可以单独使用 gsub 来做到这一点。

最后一件事是,多行字符串中的每一行不会总是有一个 'Installed on' 条目,因此答案需要在适用时考虑到这一点。

应该这样做:

expr = /(.+?)\s+\[([^\]]+)\](?:\s+\[([^\]]+)\])?/
str.scan(expr)

这个表达式实际上并没有看起来那么复杂。它看起来很复杂,因为我们要匹配必须转义的方括号,并且还使用正则表达式语言中用方括号括起来的字符 类。总之,它增加了很多噪音。

这里是分开的:

expr = /
  (.+?)  # Capture #1: Any characters (non-greedy)

  \s+    # Whitespace
  \[     # Literal '['
    (      # Capture #2:
      [^\]]+   # One or more characters that aren't ']'
    )
  \]     # Literal ']'

  (?:    # Non-capturing group
    \s+    # Whitespace
    \[     # Literal '['
      ([^\]]+) # Capture #3 (same as #2)
    \]     # Literal ']'
  )?     # Preceding group is optional
/x

如您所见,第三部分与第二部分相同,只是它位于非捕获组中,后跟 ? 使其成为可选。

值得注意的是,这可能会失败,例如产品名称包含方括号。如果可能的话,一个可能的解决方案是在匹配中包含 versionInstalled 文本,例如:

expr = /(.+?)\s+\[(version [^\]]+)\](?:\s+\[(Installed [^\]]+)\])?/

P.S。这是一个使用 String#split 的解决方案:

expr = /\]?\s+\[|\]$/
res = str.each_line.map {|ln| ln.strip.split(expr) }
        .reject {|arr| arr.empty? }

如果您的产品名称中有括号,这里可能的解决方法是指定各部分之间的最小空格数,例如:

expr = /\]?\s{3,}\[|\]$/

...这当然取决于产品名称的连续空格不超过三个。