2015-01-25 21 views
7

Tôi có một tập lệnh python, vì nhiều lý do, có một biến là một chuỗi khá lớn, dài 10mb. Chuỗi này chứa nhiều dòng.Cách nhanh nhất để xóa dòng đầu tiên và cuối cùng khỏi chuỗi Python

Cách nhanh nhất để xóa dòng đầu tiên và dòng cuối cùng của chuỗi này là gì? Do kích thước của chuỗi, hoạt động càng nhanh thì càng tốt; có sự nhấn mạnh về tốc độ. Chương trình trả về một chuỗi nhỏ hơn một chút, sans dòng đầu tiên và cuối cùng.

'\n'.join(string_variable[-1].split('\n')[1:-1]) là cách dễ nhất để làm điều này, nhưng nó cực kỳ chậm vì hàm split() sao chép đối tượng trong bộ nhớ và nối() sao chép lại nó.

Ví dụ chuỗi:

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

tín dụng phụ: Có chương trình này không bị nghẹt thở nếu không có dữ liệu ở giữa; đây là tùy chọn, vì trường hợp của tôi không nên là một chuỗi không có dữ liệu ở giữa.

+0

Bạn có thể kiểm soát như thế nào chuỗi vào chương trình của bạn, ví dụ: bạn đang làm 'my_string = file_obj.read()' để lấy chuỗi? Ngoài ra, bạn có cần tất cả các dòng có trong bộ nhớ tại một thời điểm, hoặc chỉ là một dòng tại một thời điểm được không? –

Trả lời

9

chia đầu tiên tại '\n' lần và sau đó kiểm tra xem chuỗi tại chỉ số cuối cùng chứa '\n', nếu có str.rsplit tại '\n' một lần và chọn mục tại index 0 khác trả về một chuỗi rỗng:

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 

Hoặc không phân chia, hãy tìm chỉ mục của '\n' từ một trong hai đầu và cắt chuỗi:

>>> 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 
0

Tùy thuộc vào cách mà trường hợp sử dụng của bạn sẽ tiêu thụ chuỗi, cách nhanh hơn để loại bỏ nó có thể là do không loại bỏ nó.

Nếu bạn có kế hoạch truy cập các dòng trong chuỗi tuần tự, bạn có thể tạo một trình phát bỏ qua dòng đầu tiên và cuối cùng trong khi sinh ra mỗi dòng đang được tiêu thụ thay vì xây dựng một bộ bản sao mới của tất cả các dòng.

Một cách đặc biệt để tránh dòng đầu tiên và cuối cùng là lặp qua chuỗi mà không tạo bản sao không cần thiết bằng cách theo dõi ba dòng tiếp theo và chỉ trả lại dòng thứ hai, cách lặp lại này sẽ kết thúc trước khi tiếp cận dòng cuối cùng mà không cần phải biết vị trí của ngắt dòng cuối cùng.

Chức năng sau đây sẽ cho bạn những kết quả mong muốn:

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] 

Bạn không thể thử nghiệm nó với:

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) 

Will tạo ra kết quả:

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

Lưu ý rằng lớn nhất lợi thế của phương pháp này là sẽ chỉ tạo ra một dòng mới vào thời điểm đó và sẽ hầu như không có thời gian để tạo ra dòng đầu tiên đầu ra (thay vì chờ tất cả các dòng được tìm thấy trước khi tiếp tục) nhưng, một lần nữa, điều đó có thể hữu ích hay không tùy thuộc vào trường hợp sử dụng của bạn.

6

Xem xét một chuỗi s có nghĩa là một cái gì đó như thế này:

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

Các mã sau đây ...

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

... tạo ra kết quả:

'line2\nline3\nline4' 

Và, do đó, là mã ngắn nhất để loại bỏ đầu tiên và dòng cuối cùng của một chuỗi. Tôi không nghĩ rằng các phương thức .find và .rfind làm bất cứ điều gì ngoài việc tìm kiếm một chuỗi đã cho. Hãy thử tốc độ!

0

phương pháp khác là để phân chia các dữ liệu ở dòng mới và sau đó gia nhập lại tất cả mọi thứ nhưng dòng đầu tiên và lần cuối:

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

này hoạt động tốt với không có dữ liệu:

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

Theo ghi nhận của OP, điều này sẽ rất chậm trên dữ liệu lớn. –

Các vấn đề liên quan