Khi tách một chuỗi trống trong Python, tại sao split () trả về một danh sách trống trong khi split ('\ n') trả về ['']?


154

Tôi đang sử dụng split('\n')để có được các dòng trong một chuỗi và thấy rằng ''.split()trả về một danh sách trống [], trong khi ''.split('\n')trả về ['']. Có bất kỳ lý do cụ thể cho một sự khác biệt như vậy?

Và có cách nào thuận tiện hơn để đếm các dòng trong một chuỗi không?


Câu trả lời:


247

Câu hỏi: Tôi đang sử dụng split ('\ n') để nhận các dòng trong một chuỗi và thấy rằng '' .split () trả về danh sách trống [], trong khi '' .split ('\ n') trả về [''] .

Phương thức str.split () có hai thuật toán. Nếu không có đối số nào được đưa ra, nó sẽ phân tách trên các khoảng trắng lặp đi lặp lại. Tuy nhiên, nếu một đối số được đưa ra, nó được coi là một dấu phân cách duy nhất không có lần chạy lặp lại.

Trong trường hợp tách một chuỗi rỗng, chế độ đầu tiên (không có đối số) sẽ trả về một danh sách trống vì khoảng trắng được ăn và không có giá trị nào được đưa vào danh sách kết quả.

Ngược lại, chế độ thứ hai (với một đối số như \n) sẽ tạo ra trường trống đầu tiên. Hãy xem xét nếu bạn đã viết '\n'.split('\n'), bạn sẽ nhận được hai trường (một phần tách, cho bạn hai nửa).

Câu hỏi: Có bất kỳ lý do cụ thể cho sự khác biệt như vậy?

Chế độ đầu tiên này hữu ích khi dữ liệu được căn chỉnh trong các cột với số lượng khoảng trắng khác nhau. Ví dụ:

>>> data = '''\
Shasta      California     14,200
McKinley    Alaska         20,300
Fuji        Japan          12,400
'''
>>> for line in data.splitlines():
        print line.split()

['Shasta', 'California', '14,200']
['McKinley', 'Alaska', '20,300']
['Fuji', 'Japan', '12,400']

Chế độ thứ hai hữu ích cho dữ liệu được phân tách, chẳng hạn như CSV trong đó dấu phẩy lặp lại biểu thị các trường trống. Ví dụ:

>>> data = '''\
Guido,BDFL,,Amsterdam
Barry,FLUFL,,USA
Tim,,,USA
'''
>>> for line in data.splitlines():
        print line.split(',')

['Guido', 'BDFL', '', 'Amsterdam']
['Barry', 'FLUFL', '', 'USA']
['Tim', '', '', 'USA']

Lưu ý, số lượng trường kết quả là một lớn hơn số lượng dấu phân cách. Hãy nghĩ đến việc cắt một sợi dây. Nếu bạn không cắt giảm, bạn có một mảnh. Làm một vết cắt, cho hai mảnh. Làm hai vết cắt, cho ba mảnh. Và đó là phương thức str.split (delimiter) của Python :

>>> ''.split(',')       # No cuts
['']
>>> ','.split(',')      # One cut
['', '']
>>> ',,'.split(',')     # Two cuts
['', '', '']

Câu hỏi: Và có cách nào thuận tiện hơn để đếm các dòng trong một chuỗi không?

Vâng, có một vài cách dễ dàng. Một cái sử dụng str.count () và cái kia sử dụng str.splitlines () . Cả hai cách sẽ cho cùng một câu trả lời trừ khi dòng cuối cùng bị thiếu \n. Nếu dòng mới cuối cùng bị thiếu, phương pháp str.splitlines sẽ đưa ra câu trả lời chính xác. Một kỹ thuật nhanh hơn cũng chính xác sử dụng phương pháp đếm nhưng sau đó sửa nó cho dòng mới cuối cùng:

>>> data = '''\
Line 1
Line 2
Line 3
Line 4'''

>>> data.count('\n')                               # Inaccurate
3
>>> len(data.splitlines())                         # Accurate, but slow
4
>>> data.count('\n') + (not data.endswith('\n'))   # Accurate and fast
4    

Câu hỏi từ @Kaz: Tại sao quái vật là hai thuật toán rất khác nhau được cắm vào một chức năng duy nhất?

Chữ ký cho str.split khoảng 20 năm và một số API từ thời đó là hoàn toàn thực dụng. Mặc dù không hoàn hảo, chữ ký phương thức cũng không "khủng". Đối với hầu hết các phần, các lựa chọn thiết kế API của Guido đã vượt qua thử thách của thời gian.

API hiện tại không phải là không có lợi thế. Hãy xem xét các chuỗi như:

ps_aux_header  = "USER               PID  %CPU %MEM      VSZ"
patient_header = "name,age,height,weight"

Khi được yêu cầu chia các chuỗi này thành các trường, mọi người có xu hướng mô tả cả hai bằng cách sử dụng cùng một từ tiếng Anh, "split". Khi được yêu cầu đọc mã như fields = line.split() hoặc fields = line.split(','), người ta có xu hướng để giải thích một cách chính xác những điều khoản là "chia tách một dòng vào các lĩnh vực".

Công cụ chuyển văn bản thành cột của Microsoft Excel đã đưa ra lựa chọn API tương tự và kết hợp cả hai thuật toán phân tách trong cùng một công cụ. Mọi người dường như mô hình hóa phân tách trường như một khái niệm duy nhất mặc dù có nhiều hơn một thuật toán có liên quan.


28

Nó dường như chỉ đơn giản là cách nó hoạt động, theo tài liệu :

Tách một chuỗi rỗng với một dấu phân cách được trả về [''].

Nếu sep không được chỉ định hoặc là Không có, thuật toán phân tách khác sẽ được áp dụng: các lần chạy của khoảng trắng liên tiếp được coi là một dấu tách duy nhất và kết quả sẽ không chứa các chuỗi trống ở đầu hoặc cuối nếu chuỗi có khoảng trắng ở đầu hoặc cuối. Do đó, việc tách một chuỗi rỗng hoặc một chuỗi chỉ bao gồm khoảng trắng với dấu phân cách Không trả về [].

Vì vậy, để làm cho nó rõ ràng hơn, split()hàm thực hiện hai thuật toán phân tách khác nhau và sử dụng sự hiện diện của một đối số để quyết định nên chạy cái nào. Điều này có thể là vì nó cho phép tối ưu hóa đối số không có đối số nhiều hơn đối số có đối số; Tôi không biết.


4

.split()không có thông số cố gắng để được thông minh. Nó phân tách trên bất kỳ khoảng trắng, tab, dấu cách, nguồn cấp dữ liệu, v.v. và nó cũng bỏ qua tất cả các chuỗi trống do kết quả của việc này.

>>> "  fii    fbar \n bopp ".split()
['fii', 'fbar', 'bopp']

Về cơ bản, .split()không có tham số được sử dụng để trích xuất các từ từ một chuỗi, trái ngược .split()với các tham số chỉ lấy một chuỗi và tách nó.

Đó là lý do cho sự khác biệt.

Và vâng, đếm các dòng bằng cách tách không phải là một cách hiệu quả. Đếm số lượng nguồn cấp dữ liệu và thêm một nguồn nếu chuỗi không kết thúc bằng nguồn cấp dữ liệu.


2

Sử dụng count():

s = "Line 1\nLine2\nLine3"
n_lines = s.count('\n') + 1

4
Chỉ nên thực hiện + 1 nếu văn bản không kết thúc bằng '\ n'.
Lennart Regebro

8
Chà, nếu nó kết thúc bằng "\ n" thì dòng cuối cùng là một dòng trống. Mặc dù vô dụng, nó vẫn được tính là dòng, không?
Jakub M.

2
Không. Khi tôi viết 3 dòng văn bản vào một tệp và kết thúc mỗi dòng bằng một nguồn cấp dữ liệu, thì tôi muốn nói rằng tệp chứa 3 dòng. trên unix, cách tốt nhất là để tệp văn bản luôn kết thúc bằng nguồn cấp dữ liệu. mặt khác cắt cat filexén dòng lệnh của bạn và lật đổ khiếu nại. vi luôn nối thêm một.
dùng829755

2
>>> print str.split.__doc__
S.split([sep [,maxsplit]]) -> list of strings

Return a list of the words in the string S, using sep as the
delimiter string.  If maxsplit is given, at most maxsplit
splits are done. If sep is not specified or is None, any
whitespace string is a separator and empty strings are removed
from the result.

Lưu ý câu cuối cùng.

Để đếm số dòng, bạn có thể chỉ cần đếm có bao nhiêu dòng \n:

line_count = some_string.count('\n') + some_string[-1] != '\n'

Phần cuối cùng sẽ đưa vào tài khoản các dòng cuối cùng mà không kết thúc với \n, mặc dù phương tiện này Hello, World!Hello, World!\ncó các tính cùng một dòng (mà đối với tôi là hợp lý), nếu không bạn có thể dễ dàng thêm 1vào đếm \n.


0

Để đếm số dòng, bạn có thể đếm số lần ngắt dòng:

n_lines = sum(1 for s in the_string if s == "\n") + 1 # add 1 for last line

Chỉnh sửa :

Câu trả lời khác với tích counthợp là thực sự phù hợp hơn


3
Ngoài việc chỉ sử dụng count, bools còn có thể thêm được (trên thực tế, chúng là lớp con int), do đó, genEx có thể được viết là sum(s == "\n" for s in the_string).
lvc

Ngay bây giờ bạn chỉ đang đếm các dòng trống?
Thijs van Điền

Có, tôi không loại bỏ bất kỳ dòng trống nào
Jakub M.
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.