【问题标题】:Get data between 1st and 2nd pipe in python在python中获取第一个和第二个管道之间的数据
【发布时间】:2015-11-23 04:15:08
【问题描述】:

这是我的示例数据

78|Indonesia|Pamela|Reid|preid25@gravatar.com|147.3.67.193

我想得到结果为

Indonesia

目前我正在对字符串使用拆分并访问该值。但我想使用正则表达式。

需要注意的一些条件: 数据可能为空 数据将不包含管道 (|)

我想使用正则表达式而不是拆分,因为我认为正则表达式更有效。我希望它尽可能高效的原因是因为源文件是 70gb

编辑:

这是我将在其中使用它的整个代码

def main(argv):
    mylist = set();
    input_file = open("test.txt", 'r')

    for row in input_file:
        rowsplit = row.split("|");

        if rowsplit[1] !='':
            if rowsplit[1] in mylist:
                filename= "bby_"+rowsplit[1]+".dat";
                existingFile=open(filename,'a')
                existingFile.write(row);
                existingFile.close()
            else:
                mylist.add(rowsplit[1])
                filename= "bby_"+rowsplit[1]+".dat";
                newFile = open(filename,'a')
                newFile.write(row);
                newFile.close();
        else:
            print "Empty"
    print mylist

我只是对现在应该使用哪个答案感到困惑:(

我只是想让这段代码快一点。就是这样。

【问题讨论】:

  • 你可以逐行解析它,然后根据知识没关系
  • 如果有一个管道,总会有多个管道,或者管道可以出现在任何地方吗?
  • @PadraicCunningham 我不明白你在说什么
  • 我的意思是有管道字符的行总是看起来像你的输入示例
  • Regex 非常灵活,但这种灵活性是有代价的。正如 Yaroslav Admin 的回答所示,使用正则表达式执行此任务比使用 str.split 方法慢得多

标签: python regex


【解决方案1】:

拆分和检查长度可能仍然比正则表达式更快:

for line in f:
    spl = line.split("|",2)
    if len(spl) > 2:
        print(spl[1])
       ....

匹配和不匹配行的一些时间安排:

In [24]: s = "78|Indonesia|Pamela|Reid|preid25@gravatar.com|147.3.67.193"

In [25]: %%timeit                                                        
    spl = s.split("|",2)
    if len(spl) > 2:
        pass
   ....: 
1000000 loops, best of 3: 413 ns per loop

In [26]: r = re.compile(r'(?<=\|)[^|]*')

In [27]: timeit r.search(s)                                            
1000000 loops, best of 3: 452 ns per loop

In [28]: s = "78 Indonesia Pamela Reid preid25@gravatar.com 147.3.67.193"

In [29]: timeit r.search(s)
1000000 loops, best of 3: 1.66 µs per loop

In [30]: %%timeit                       
    spl = s.split("|",2)
    if len(spl) > 2:
        pass
   ....: 
1000000 loops, best of 3: 342 ns per loop

您可以通过创建对 str.split 的本地引用来节省更多:

_spl = str.split
for line in f:
    spl = _spl(s,"|",2)
    if len(spl) > 2:
      .....

因为每行总是有相同数量的管道:

def main(argv):
    seen = set() # only use if you actually need  a set of all names
    with open("test.txt", 'r') as infile:
        r = csv.reader(infile, delimiter="|")
        for row in r:
            v = row[1]
            if v:
                filename = "bby_" + v + ".dat"
                existingFile = open(filename, 'a')
                existingFile.write(row)
                existingFile.close()
                seen.add(v)
            else:
                print "Empty"

if/else 似乎是多余的,因为无论如何您都在附加到文件中,如果您出于另一个原因想要保留一组行 [1],您可以每次都添加到该集合中,除非您真的想要一个我将从代码中删除它的所有名称的集合。

应用相同的逻辑进行拆分:

def main(argv):
    seen = set()
    with open("test.txt", 'r') as infile:
        _spl = str.split
        for row in infile:
            v = _spl(row,"|",2)[1]
            if v:
                filename = "bby_" + v + ".dat"
                existingFile = open(filename, 'a')
                existingFile.write(row)
                existingFile.close()
                seen.add(v)
            else:
                print "Empty"

会导致大量开销的是不断打开和写入,但除非您可以将所有行存储在内存中,否则没有简单的方法可以解决它。

就阅读而言,在包含一千万行的文件上,只需拆分两次就优于 csv 阅读器:

In [15]: with open("in.txt") as f:
   ....:     print(sum(1 for _ in f))
   ....: 
10000000

In [16]: paste
def main(argv):
    with open(argv, 'r') as infile:
        for row in infile:
            v = row.split("|", 2)[1]
            if v:
                pass
## -- End pasted text --

In [17]: paste
def main_r(argv):
    with open(argv, 'r') as infile:
        r = csv.reader(infile, delimiter="|")
        for row in r:
            if row[1]:
                pass

## -- End pasted text --

In [18]: timeit main("in.txt")
1 loops, best of 3: 3.85 s per loop

In [19]: timeit main_r("in.txt")
1 loops, best of 3: 6.62 s per loop

【讨论】:

  • @ViChU,管道的数量总是相同吗?这将是最佳方法的一个重要因素
  • 是的.. 相同数量的管道。
  • 那么只检查if v 的拆分将是最快的方法,您知道将出现在第二列中的潜在国家吗?如果是这样,我们可能会加快 io 速度
  • @ViChU,没有人第一次就做对了 ;) 保留列表或 row[1]'s 的目的是什么?
  • 唯一的问题是使用with,其他应该在python2.4上工作的吧
【解决方案2】:

以下是 Python 3.4.3 上有意义答案的表现:

In [4]: timeit.timeit('s.split("|", 2)[1]', 's = "78|Indonesia|Pamela|Reid|preid25@gravatar.com|147.3.67.193"')
Out[4]: 0.43930888699833304

In [10]: timeit.timeit('re.search(r"^[^a-zA-Z]*([a-zA-Z]+)", s).group(1)', 's = "78|Indonesia|Pamela|Reid|preid25@gravatar.com|147.3.67.193"; import re')
Out[10]: 1.234878903022036

In [16]: timeit.timeit('re.search("^\d*\|(\w+?)?\|", s).group(1)', 's = "78|Indonesia|Pamela|Reid|preid25@gravatar.com|147.3.67.193"; import re')
Out[16]: 1.8305770770530216

如果没有管道:

In [24]: timeit.timeit('s.split("|", 2)[1] if "|" in s else None', 's = "78|Indonesia|Pamela|Reid|preid25@gravatar.com|147.3.67.193"')
Out[24]: 0.494665392965544

In [25]: timeit.timeit('s.split("|", 2)[1] if "|" in s else None', 's =  ""')
Out[25]: 0.04492994397878647

【讨论】:

  • 没有管道怎么办?
  • @AvinashRaj:那么数据文件坏了,所以如果程序引发异常是可以接受的。根据 OP,他们只需要从第二列中提取数据,因此可以合理地假设总会有(至少)两列。
  • @AvinashRaj 查看添加的部分。
  • 输入中总会有管道。只有数据可能为空。
  • @ViChU:如果管道在那里但第二列是空的,Yaroslav 的代码可以正常工作:它只会返回一个空字符串。
【解决方案3】:

这个相当简单:如果你知道第一个子句总是一个数字(或空),你可以锚定到前面。

^\d*\|(\w+?)?\|

这将匹配行前的 0 个或多个数字,后跟文字 | 字符,然后是第 1 条,它将包含一个非贪婪匹配的单词(没有空格),或者如果它没有不存在,后跟文字 | 字符。

性能正则表达式是关于快速失败,非贪婪匹配和锚点至关重要。

【讨论】:

  • OP 说子句可能是空的,所以第一部分可能需要d*
  • 你是绝对正确的。我认为 OP 仅指第二个子句可能为空。固定。
【解决方案4】:

如果总是有相同的管道数,您可以使用正则表达式:

[^|]+(?=(?:\|[^|]+\|?){4}$)

DEMO

【讨论】:

    【解决方案5】:

    这将为您提供第一个和第二个管道之间的东西:

     re.search(r'(?<=\|)[^|]*',s).group()
    

    re.search(模式、字符串、标志=0) 扫描字符串寻找 正则表达式模式产生的第一个位置 匹配.....

    【讨论】:

      【解决方案6】:

      使用这个正则表达式抓住第一个单词。

      re.search(r'^[^a-zA-Z]*([a-zA-Z]+)', S).group(1)
      

      【讨论】:

      • 这也考虑第一个元素。
      • 打印第一组。
      【解决方案7】:

      我想您可以在以下位置找到答案:Python: Split string by list of separators

      def split(txt, seps):
          default_sep = seps[0]
      
          # we skip seps[0] because that's the default seperator
          for sep in seps[1:]:
              txt = txt.replace(sep, default_sep)
      
          return [i.strip() for i in txt.split(default_sep)]
      

      Joschua(回答上述问题的用户)进行了测试,它比正则表达式更有效。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2022-11-13
        • 2021-10-15
        • 2018-05-02
        • 2021-12-25
        • 1970-01-01
        • 1970-01-01
        • 2016-10-20
        相关资源
        最近更新 更多