在没有循环的模式之前解析所有子串?

时间:2018-05-13 08:27:24

标签: python regex python-2.7

我有一个长字符串,由许多用空格分隔的数字组成(有时候甚至会抛出一个新行)。我想通过字符串将所有数字附加到0.000000000000000000e+00数字开头之前的新列表中。所以这是我的字符串示例:

my_string = '1.249132165057832031e+13 1.638194600635518555e+13 2.127995187558799219e+13 2.744617593148214062e+13 -2.558800658636701519e+28 5.918883595148564680e+30 3.603563681248702509e+31 4.325917213186498068e+31 4.911908042151239481e+31 4.463331378152286632e+31 3.684371076399113503e+31 2.500614504012405068e+31 9.997365425073173512e+30 -7.046725649106466938e+30 -2.192076417151744811e+31 -2.531287564917444482e+31 -6.962936418905874724e+30 3.281685507310205847e+31 9.241630178064907840e+31 1.730544785932614751e+32 2.619210949875333106e+32 2.984440142196566918e+32 8.964375812060072923e+31 -8.515727465135046667e+32 -3.425309034394939997e+33 -8.145884847188906515e+33 -9.922370830834364410e+33 -2.119464668318252366e+28 -1.689726703118075140e+27 1.440101653069986610e+26 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.186324149659251562e+13 8.113154959294240625e+13 1.053889122977165625e+14 1.359271226298647969e+14 -2.097046363337115528e+28 4.850777756495711585e+30 2.953274256558218597e+31 3.545273642763729060e+31 4.025456872055449111e+31 3.657581460085835446e+31 3.018816679659856350e+31 2.048223110003727437e+31 8.176806147340775115e+30 -5.796250740354887641e+30 -1.798839398031696094e+31 -2.076444435341100150e+31 -5.711669151245612857e+30 2.691583747083509247e+31 7.579958708961477309e+31 1.419395486743453834e+32 2.148287875274468622e+32 2.447859658750551118e+32 7.352862842410293685e+31 -6.984595303325589259e+32 -2.809449882735912952e+33 -6.681296633318354125e+33 -8.138406580426555140e+33 -1.740744048703962454e+28 -1.411749034480591280e+27 8.079362883576220633e+25 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00'

从这个字符串中,我最终想要的只有:

new_list = ['1.440101653069986610e+26', '8.079362883576220633e+25']

我在想我会使用正则表达式,但这看起来有点棘手,因为我将一堆0.000000000000000000e+00次出现组合在一起,而我只想在第一次出现零之前出现非零数字。我也不能假设总是有相同数量的零组合在一起。

我还考虑过拆分空间并迭代,但是我的完整字符串实际上太长了,无法有效地完成此操作。我怎么能这样做?

3 个答案:

答案 0 :(得分:2)

  

我也不能假设总是有相同数量的零组合在一起。

我们如何区分,例如,来自"一组零"的2个连续零值。 ?

好吧,鉴于您正在寻找至少5个0.000模式,您可以在这个多0模式上使用非捕获组(以避免匹配它),遵循非空白模式(对于数字)

re.findall("(\S+)\s+(?:0\.0+e\+00\s+){5,}",my_string)

如果除了模式本身之外不能有任何零,则可以推广为:

re.findall("(\S+)\s+(?:0\.0+e\+00\s+)+",my_string)

(您需要非捕获组末尾的+来捕获并丢弃所有

结果(在两种情况下):

['1.440101653069986610e+26', '8.079362883576220633e+25']

这也处理换行符,并且容忍小数部分中可变数量的零

答案 1 :(得分:1)

列表理解和压缩

这比其他解决方案快<10-70倍。

my_values = my_string.split()
output = [x for x,y in zip(my_values,my_values[1:]) 
           if (y == '0.000000000000000000e+00' and x != '0.000000000000000000e+00')]
print(output)

或者,正如@ Jean-FrançoisFabre所建议的那样,islice可以节省内存:

import itertools
my_values = my_string.split()
output = [x for x,y in zip(my_values,itertools.islice(myvalues,1,None)) 
               if (y == '0.000000000000000000e+00' and x != '0.000000000000000000e+00')]
print(output)

这可以通过成对分组元素(x,y)来实现。 x应该与0.00..不同,而y应该等于它。首先进行y检查,这将在大多数情况下快速评估False并继续迭代。 返回:

['1.440101653069986610e+26', '8.079362883576220633e+25']

熊猫和numpy

然而,另一个想法(我认为这里最聪明)将使用pandas和pd.to_numeric()。当你使用数字时,你很可能想要使用像numpy或pandas这样的库。这样会更安全,因为您也可以顺利处理错误。另请注意,我在两种情况下都将数字转换回字符串(您可以跳过)。

import pandas as pd

data = pd.Series(pd.to_numeric(my_string.split()))
output = data[(data != 0) & (data.shift(-1) == 0)].astype(str).tolist()
print(output)

#['1.440101653069986610e+26', '8.079362883576220633e+25']

numpy:

import numpy as np

data = np.loadtxt(my_string.split())
output = list(map(str,data[(data != 0) & (np.roll(ar, -1) == 0)]))
print(output)

#['1.440101653069986610e+26', '8.079362883576220633e+25']

时间比较

最快 - &gt;最慢的

100000 loops, best of 3: 9.28 µs per loop  <-- Anton vBR list comprehension
10000 loops, best of 3: 98.4 µs per loop   <-- Revos Regex
1000 loops, best of 3: 256 µs per loop     <-- Anton vBR numpy
1000 loops, best of 3: 425 µs per loop     <-- Tzot Regex
1000 loops, best of 3: 513 µs per loop     <-- Jean-François Fabre Regex 
1000 loops, best of 3: 782 µs per loop     <-- liliscent 
1000 loops, best of 3: 794 µs per loop     <-- Anton vBR pandas

答案 2 :(得分:0)

如果你想要 float 值而不是它们的字符串表示:

import re

list(
    filter(
        None,
        map(
            float,
            re.findall(r"\S+(?=\s0\.0+e)", my_string)
)))
  • re.findall(r"\S+(?=\s0\.0+e)", my_string)
    在白色空间和0.00000 ... e
  • 之前查找非空白字符序列的所有出现
  • map(float, ^ )
    假设所有上述匹配都可以转换为float
  • filter(None, ^ )
    过滤掉所有零浮点数
  • list( ^ )
    将上面的内容放入一个列表中(Python 2中的无操作,将生成器转换为Python 3中的列表)

结果:

>>> list(filter(None, map(float, re.findall(r"\S+(?=\s0\.0+e)", my_string))))
[1.4401016530699866e+26, 8.07936288357622e+25]

但是,如果你仍然想要字符串值,请告诉我;在这种情况下,map&amp;需要修改filter个子表达式。