从Python字符串中删除第一行和最后一行的最快方法

时间:2015-01-25 07:45:52

标签: python string performance

我有一个python脚本,由于各种原因,它有一个相当大的字符串变量,比如10mb长。该字符串包含多行。

删除此字符串的第一行和最后一行的最快方法是什么?由于弦的大小,操作越快越好;强调速度。程序返回一个稍小的字符串,没有第一行和最后一行。

'\n'.join(string_variable[-1].split('\n')[1:-1])是最简单的方法,但它非常慢,因为split()函数会将对象复制到内存中,而join()会再次复制它。

示例字符串:

*** START OF DATA ***
data
data
data
*** END OF DATA ***

额外信用:如果中间没有数据,此程序不会阻塞;这是可选的,因为对于我的情况,不应该是一个没有数据的字符串。

4 个答案:

答案 0 :(得分:9)

首先在'\n'分割一次然后检查最后一个索引的字符串是否包含'\n',如果str.rsplit'\n',则检查该项是否为def solve(s): s = s.split('\n', 1)[-1] if s.find('\n') == -1: return '' return s.rsplit('\n', 1)[0] ... >>> s = '''*** START OF DATA *** data data data *** END OF DATA ***''' >>> solve(s) 'data\ndata\ndata' >>> s = '''*** START OF DATA *** *** END OF DATA ***''' >>> solve(s) '' >>> s = '\n'.join(['a'*100]*10**5) >>> %timeit solve(s) 100 loops, best of 3: 4.49 ms per loop 否则返回一个空字符串:

'\n'

或者根本不分裂,从任一端找到>>> def solve_fast(s): ind1 = s.find('\n') ind2 = s.rfind('\n') return s[ind1+1:ind2] ... >>> s = '''*** START OF DATA *** data data data *** END OF DATA ***''' >>> solve_fast(s) 'data\ndata\ndata' >>> s = '''*** START OF DATA *** *** END OF DATA ***''' >>> solve_fast(s) '' >>> s = '\n'.join(['a'*100]*10**5) >>> %timeit solve_fast(s) 100 loops, best of 3: 2.65 ms per loop 的索引并切割字符串:

{{1}}

答案 1 :(得分:7)

考虑一个类似这样的字符串:

s = "line1\nline2\nline3\nline4\nline5"

以下代码......

s[s.find('\n')+1:s.rfind('\n')]

...产生输出:

'line2\nline3\nline4'

因此,是删除字符串的第一行和最后一行的最短代码。我认为.find和.rfind方法除了搜索给定的字符串之外什么都不做。试试速度!

答案 2 :(得分:0)

根据您的用例使用字符串的方式,删除它的更快方法可能是不删除它。

如果您计划按顺序访问字符串中的行,则可以构建一个生成器,该生成器跳过第一行和最后一行,同时生成每行所消耗的行,而不是构建所有行的新副本集。

避免第一行和最后一行的特殊方法是迭代字符串而不生成不必要的副本是通过跟踪三个后续行并仅返回第二行,这样迭代将在到达最后一行之前结束线,无需知道最后一个换行符的位置。

以下函数应该为您提供所需的输出:

def split_generator(s):
  # Keep track of start/end positions for three lines
  start_prev = end_prev = 0
  start = end = 0
  start_next = end_next = 0

  nr_lines = 0

  for idx, c in enumerate(s):
    if c == '\n':
      nr_lines += 1

      start_prev = start
      end_prev = end
      start = start_next
      end = end_next
      start_next = end_next
      end_next = idx

      if nr_lines >= 3:
        yield s[(start + 1) : end]

  # Handle the case when input string does not finish on "\n"
  if s[-1] != '\n' and nr_lines >= 2:
    yield s[(start_next+1):end_next]

你不能用以下方法测试它:

print("1st example")
for filtered_strs in split_generator('first\nsecond\nthird'):
  print(filtered_strs)

print("2nd example")
for filtered_strs in split_generator('first\nsecond\nthird\n'):
  print(filtered_strs)

print("3rd example")
for filtered_strs in split_generator('first\nsecond\nthird\nfourth'):
  print(filtered_strs)

print("4th example")
for filtered_strs in split_generator('first\nsecond\nthird\nfourth\n'):
  print(filtered_strs)

print("5th example")
for filtered_strs in split_generator('first\nsecond\nthird\nfourth\nfifth'):
  print(filtered_strs)

将生成输出:

1st example
second
2nd example
second
3rd example
second
third
4th example
second
third
5th example
second
third
fourth

请注意,这种方法的最大优点是,当时只会创建一个新行,并且几乎没有时间生成第一行输出(而不是等待所有行都找到,然后再继续)但是,根据您的使用情况,这可能有用或不可用。

答案 3 :(得分:0)

另一种方法是将数据拆分为换行符,然后重新加入除第一行和最后一行之外的所有内容:

>>> s = '*** START OF DATA *** \n\
... data\n\
... data\n\
... data\n\
... *** END OF DATA ***'
>>> '\n'.join(s.split('\n')[1:-1])
'data\ndata\ndata'

没有数据,这样可以正常工作:

>>> s = '*** START OF DATA *** \n\
... *** END OF DATA ***'
>>> '\n'.join(s.split('\n')[1:-1])
''