簡體   English   中英

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

[英]Fastest way to remove first and last lines from a Python string

我有一個 python 腳本,由於各種原因,它有一個相當大的字符串變量,比如 10mb 長。 此字符串包含多行。

刪除此字符串的第一行和最后一行的最快方法是什么? 由於字符串的大小,操作越快越好; 強調速度。 該程序返回一個略小的字符串,沒有第一行和最后一行。

'\n'.join(string_variable[-1].split('\n')[1:-1])是最簡單的方法,但速度非常慢,因為 split() function 復制了 memory 中的 object ,然后 join() 再次復制它。

示例字符串:

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

額外功勞:如果中間沒有數據,讓這個程序不會卡住; 這是可選的,因為對於我的情況,不應該有一個中間沒有數據的字符串。

在第一次分裂'\\n'一次,然后檢查是否在最后一個索引字符串中包含'\\n' ,如果是str.rsplit'\\n'一次,並挑選在第0個指標的項目,否則返回一個空字符串:

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

考慮一個字符串s,它是這樣的:

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

以下代碼......

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

...產生輸出:

'line2\nline3\nline4'

因此,是刪除字符串的第一行和最后一行的最短代碼。 我認為.find和.rfind方法除了搜索給定的字符串之外什么都不做。 試試速度吧!

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

如果您計划按順序訪問字符串中的行,則可以構建一個生成器,該生成器跳過第一行和最后一行,同時生成每行所消耗的行,而不是構建所有行的新副本集。

避免第一行和最后一行的特殊方法是迭代字符串而不生成不必要的副本是通過跟蹤三個后續行並僅返回第二行,這樣迭代將在到達最后一行之前結束而不需要知道最后一次換行的位置。

以下函數應該為您提供所需的輸出:

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

請注意,這種方法的最大優點是,當時只會創建一個新行,並且幾乎沒有時間生成第一行輸出(而不是等待所有行在找到之前再繼續)但是,再次,根據您的使用情況,這可能有用或不有用。

另一種方法是將數據拆分為換行符,然后重新加入除第一行和最后一行之外的所有內容:

>>> 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])
''

你可以在分割后減去第一個和最后一個。 簡單,pythonic。

mydata = '''
data
data
data
'''

for data in mydata.split('\n')[1:-1]:
    print(data)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM