【问题标题】:How can I split this string up?如何拆分此字符串?
【发布时间】:2017-09-22 19:28:01
【问题描述】:

我试图在有一个完整的 2 位数字被空格包围的地方之前拆分字符串。最终我希望它可以在 Python 中工作,但我一直在使用 sed 进行工作,但我无法弄清楚。

我的测试数据如下所示:

13 13 13 13 13 9:07.18 9:12.09 9:15.65
14 14 14 2:04.86 2:05.99 2:06.87 14 4:21.51 4:23.51 4:25.00 14 8:56.28 9:01.09 9:04.58
15 15 57.18 57.61 57.95 15 2:02.61 2:03.72 2:04.58 15 4:17.31 4:19.28 4:20.75 15 8:47.15 8:51.87 8:55.30
16 16 56.34 56.76 57.09 16 2:00.69 2:01.78 2:02.63 16 4:13.75 4:15.69 4:17.14 16 8:39.71 8:44.37 8:47.75
17 25.69 25.85 25.99 17 55.62 56.03 56.36 17 1:59.07 2:00.15 2:00.99 17 4:10.76 4:12.69 4:14.11 17 8:33.73 8:38.34 8:41.68
18 25.43 25.59 25.73 18 55.01 55.42 55.74 18 1:57.74 1:58.81 1:59.63 18 4:08.34 4:10.24 4:11.66 18 8:33.73 8:37.04
19 25.20 25.36 25.49 19 54.50 54.91 55.23 19 1:57.74 1:58.56 19 4:08.34 4:09.74 19 8:33.73

我希望它像这样拆分(注意逗号','的位置):

13, 13, 13, 13, 13 9:07.18 9:12.09 9:15.65
14, 14, 14 2:04.86 2:05.99 2:06.87, 14 4:21.51 4:23.51 4:25.00, 14 8:56.28 9:01.09 9:04.58
15, 15 57.18 57.61 57.95, 15 2:02.61 2:03.72 2:04.58, 15 4:17.31 4:19.28 4:20.75, 15 8:47.15 8:51.87 8:55.30
16, 16 56.34 56.76 57.09, 16 2:00.69 2:01.78 2:02.63, 16 4:13.75 4:15.69 4:17.14, 16 8:39.71 8:44.37 8:47.75
17 25.69 25.85 25.99, 17 55.62 56.03 56.36, 17 1:59.07 2:00.15 2:00.99, 17 4:10.76 4:12.69 4:14.11, 17 8:33.73 8:38.34 8:41.68
18 25.43 25.59 25.73, 18 55.01 55.42 55.74, 18 1:57.74 1:58.81 1:59.63, 18 4:08.34 4:10.24 4:11.66, 18 8:33.73 8:37.04
19 25.20 25.36 25.49, 19 54.50 54.91 55.23, 19 1:57.74 1:58.56, 19 4:08.34 4:09.74, 19 8:33.73

上面的数据是相当规律的,因为两位整数在 [13,19] 范围内,但我应该期望的范围是 [10,99]。

有人可以建议一种方法来执行上述转换吗?我已经用正则表达式处理了一段时间,但我无法涵盖所有​​情况。

【问题讨论】:

  • 你的数据结构是什么? - 如果你有你的数据在string 然后mydata = mydata.split(' ')
  • @GiantsLoveDeathMetal 简单地拆分并不能满足 OP 的要求。查看第一行所需的输出:有一个看起来像时间组件的东西,需要与前面的整数保持在同一个“元素”中。
  • @not_a_robot 是的 - 棘手
  • 所以,13 13 9:07.18 9:12.09 应该拆分为 13, 13 9:07.18 9:12.09 而不是 13, 13, 9:07.18 9:12.09
  • 你能split 字符串并根据下一个元素的内容在函数中重建它吗? - 您需要的输出格式是什么? Liststrings?

标签: python sed


【解决方案1】:

添加到VMRuiz's answer,这将为每一行输出一个列表,而不是一个大字符串。我必须更改正则表达式才能使用re.split 而不是re.sub,我不确定它是否等效。

for line in a.split('\n'):
    re.split('(?<=\d{2}) (?=\d{2} |$)', line)

编辑:这肯定是一样的,但有点尴尬:

for line in re.sub('(\d{2}) (?=\d{2}( |$))', '\g<1>,', a).split('\n'):
    line.split(',')

【讨论】:

    【解决方案2】:

    如果你想要一个非正则表达式 Python 解决方案,你可以这样做:

    s = """\
    13 13 13 13 13 9:07.18 9:12.09 9:15.65
    14 14 14 2:04.86 2:05.99 2:06.87 14 4:21.51 4:23.51 4:25.00 14 8:56.28 9:01.09 9:04.58
    15 15 57.18 57.61 57.95 15 2:02.61 2:03.72 2:04.58 15 4:17.31 4:19.28 4:20.75 15 8:47.15 8:51.87 8:55.30
    16 16 56.34 56.76 57.09 16 2:00.69 2:01.78 2:02.63 16 4:13.75 4:15.69 4:17.14 16 8:39.71 8:44.37 8:47.75
    17 25.69 25.85 25.99 17 55.62 56.03 56.36 17 1:59.07 2:00.15 2:00.99 17 4:10.76 4:12.69 4:14.11 17 8:33.73 8:38.34 8:41.68
    18 25.43 25.59 25.73 18 55.01 55.42 55.74 18 1:57.74 1:58.81 1:59.63 18 4:08.34 4:10.24 4:11.66 18 8:33.73 8:37.04
    19 25.20 25.36 25.49 19 54.50 54.91 55.23 19 1:57.74 1:58.56 19 4:08.34 4:09.74 19 8:33.73"""
    
    
    res=""
    for line in s.splitlines():
        buf=line.split()
        for i, e in enumerate(buf[1:], 1):
            buf[i-1]+=", " if e.isdigit() else " "
        res+=''.join(buf)+"\n"  
    
    >>> res
    13, 13, 13, 13, 13 9:07.18 9:12.09 9:15.65
    14, 14, 14 2:04.86 2:05.99 2:06.87, 14 4:21.51 4:23.51 4:25.00, 14 8:56.28 9:01.09 9:04.58
    15, 15 57.18 57.61 57.95, 15 2:02.61 2:03.72 2:04.58, 15 4:17.31 4:19.28 4:20.75, 15 8:47.15 8:51.87 8:55.30
    16, 16 56.34 56.76 57.09, 16 2:00.69 2:01.78 2:02.63, 16 4:13.75 4:15.69 4:17.14, 16 8:39.71 8:44.37 8:47.75
    17 25.69 25.85 25.99, 17 55.62 56.03 56.36, 17 1:59.07 2:00.15 2:00.99, 17 4:10.76 4:12.69 4:14.11, 17 8:33.73 8:38.34 8:41.68
    18 25.43 25.59 25.73, 18 55.01 55.42 55.74, 18 1:57.74 1:58.81 1:59.63, 18 4:08.34 4:10.24 4:11.66, 18 8:33.73 8:37.04
    19 25.20 25.36 25.49, 19 54.50 54.91 55.23, 19 1:57.74 1:58.56, 19 4:08.34 4:09.74, 19 8:33.73
    

    awk 你可以这样做:

    awk '{n=split($0,a)
          for (i=2;i<=n;i++)
              printf "%s%s", a[i-1], a[i]~/^[[:digit:]]+$/ ?  ", " : " "
          print a[n]
        }' file
    13, 13, 13, 13, 13 9:07.18 9:12.09 9:15.65
    14, 14, 14 2:04.86 2:05.99 2:06.87, 14 4:21.51 4:23.51 4:25.00, 14 8:56.28 9:01.09 9:04.58
    15, 15 57.18 57.61 57.95, 15 2:02.61 2:03.72 2:04.58, 15 4:17.31 4:19.28 4:20.75, 15 8:47.15 8:51.87 8:55.30
    16, 16 56.34 56.76 57.09, 16 2:00.69 2:01.78 2:02.63, 16 4:13.75 4:15.69 4:17.14, 16 8:39.71 8:44.37 8:47.75
    17 25.69 25.85 25.99, 17 55.62 56.03 56.36, 17 1:59.07 2:00.15 2:00.99, 17 4:10.76 4:12.69 4:14.11, 17 8:33.73 8:38.34 8:41.68
    18 25.43 25.59 25.73, 18 55.01 55.42 55.74, 18 1:57.74 1:58.81 1:59.63, 18 4:08.34 4:10.24 4:11.66, 18 8:33.73 8:37.04
    19 25.20 25.36 25.49, 19 54.50 54.91 55.23, 19 1:57.74 1:58.56, 19 4:08.34 4:09.74, 19 8:33.73
    

    【讨论】:

      【解决方案3】:

      为了 sed 的乐趣,并且因为您似乎对 sed 参考资料感兴趣以便理解。

      sed ":a;s/\([^,]\)\(\s[0-9]\{2\}\s\)/\1,\2/;ta"
      

      sed -E ":a;s/([^,])(\s[0-9]{2}\s)/\1,\2/;ta"
      
      • 开始循环
        • 寻找
          • , 以外的其他内容,对于稍后循环很重要
          • 一个空格、两个数字和一个空格
        • 用非逗号、逗号和其余部分替换
      • 如果替换了某些东西则循环

      输出(完全符合期望的输出):

      13, 13, 13, 13, 13 9:07.18 9:12.09 9:15.65
      14, 14, 14 2:04.86 2:05.99 2:06.87, 14 4:21.51 4:23.51 4:25.00, 14 8:56.28 9:01.09 9:04.58
      15, 15 57.18 57.61 57.95, 15 2:02.61 2:03.72 2:04.58, 15 4:17.31 4:19.28 4:20.75, 15 8:47.15 8:51.87 8:55.30
      16, 16 56.34 56.76 57.09, 16 2:00.69 2:01.78 2:02.63, 16 4:13.75 4:15.69 4:17.14, 16 8:39.71 8:44.37 8:47.75
      17 25.69 25.85 25.99, 17 55.62 56.03 56.36, 17 1:59.07 2:00.15 2:00.99, 17 4:10.76 4:12.69 4:14.11, 17 8:33.73 8:38.34 8:41.68
      18 25.43 25.59 25.73, 18 55.01 55.42 55.74, 18 1:57.74 1:58.81 1:59.63, 18 4:08.34 4:10.24 4:11.66, 18 8:33.73 8:37.04
      19 25.20 25.36 25.49, 19 54.50 54.91 55.23, 19 1:57.74 1:58.56, 19 4:08.34 4:09.74, 19 8:33.73
      

      【讨论】:

      • 非常好。只是想知道,您能否通过使用扩展正则表达式来消除一些反斜杠?
      【解决方案4】:

      前瞻断言(?=...)可以解决这个问题:

      >>> a = """13 13 13 13 13 9:07.18 9:12.09 9:15.65
      14 14 14 2:04.86 2:05.99 2:06.87 14 4:21.51 4:23.51 4:25.00 14 8:56.28 9:01.09 9:04.58
      15 15 57.18 57.61 57.95 15 2:02.61 2:03.72 2:04.58 15 4:17.31 4:19.28 4:20.75 15 8:47.15 8:51.87 8:55.30
      16 16 56.34 56.76 57.09 16 2:00.69 2:01.78 2:02.63 16 4:13.75 4:15.69 4:17.14 16 8:39.71 8:44.37 8:47.75
      17 25.69 25.85 25.99 17 55.62 56.03 56.36 17 1:59.07 2:00.15 2:00.99 17 4:10.76 4:12.69 4:14.11 17 8:33.73 8:38.34 8:41.68
      18 25.43 25.59 25.73 18 55.01 55.42 55.74 18 1:57.74 1:58.81 1:59.63 18 4:08.34 4:10.24 4:11.66 18 8:33.73 8:37.04
      19 25.20 25.36 25.49 19 54.50 54.91 55.23 19 1:57.74 1:58.56 19 4:08.34 4:09.74 19 8:33.73"""
      
      >>> print(re.sub("(\d{2}) (?=\d{2}( |$))","\g<1>, ", a))
      13, 13, 13, 13, 13 9:07.18 9:12.09 9:15.65
      14, 14, 14 2:04.86 2:05.99 2:06.87, 14 4:21.51 4:23.51 4:25.00, 14 8:56.28 9:01.09 9:04.58
      15, 15 57.18 57.61 57.95, 15 2:02.61 2:03.72 2:04.58, 15 4:17.31 4:19.28 4:20.75, 15 8:47.15 8:51.87 8:55.30
      16, 16 56.34 56.76 57.09, 16 2:00.69 2:01.78 2:02.63, 16 4:13.75 4:15.69 4:17.14, 16 8:39.71 8:44.37 8:47.75
      17 25.69 25.85 25.99, 17 55.62 56.03 56.36, 17 1:59.07 2:00.15 2:00.99, 17 4:10.76 4:12.69 4:14.11, 17 8:33.73 8:38.34 8:41.68
      18 25.43 25.59 25.73, 18 55.01 55.42 55.74, 18 1:57.74 1:58.81 1:59.63, 18 4:08.34 4:10.24 4:11.66, 18 8:33.73 8:37.04
      19 25.20 25.36 25.49, 19 54.50 54.91 55.23, 19 1:57.74 1:58.56, 19 4:08.34 4:09.74, 19 8:33.73
      

      所以,正则表达式。你需要 is(\d{2}) (?=\d{2}( |$)) 这意味着:

      1. (\d{2}) => 在第 1 组中存储 2 个数字并匹配一个额外的空格。
      2. (?=\d{2}( |$)) => 匹配 2 个数字和 1 个空格或 EOL,但不要使用它们。

      这里的关键是不消费第二个匹配组,下次应用子函数时会再次处理。最后,\g&lt;1&gt;, 将用相同的数字和附加的, 替换 1.。

      【讨论】:

      • 请解释一下你的正则表达式:)
      • @GiantsLoveDeathMetal 如果您需要任何其他解释,请告诉我 :)
      • 很高兴看到正则表达式在过去 40 年中取得了进步;我的正则表达式 foo 非常以 sed 为中心,感谢您指出这个不错的补充。
      猜你喜欢
      • 2013-01-14
      • 1970-01-01
      • 2023-04-04
      • 1970-01-01
      • 2017-07-08
      • 1970-01-01
      相关资源
      最近更新 更多