在 Python 中拆分空字符串时,为什么 split ()返回空列表,而 split (’n’)返回[”] ?

我使用 split('\n')获取一个字符串中的行,发现 ''.split()返回一个空列表 [],而 ''.split('\n')返回 ['']。这种差异有什么具体原因吗?

还有更方便的方法来计算字符串中的行数吗?

97845 次浏览

根据 文件的说法,这似乎只是它应该工作的方式:

使用指定的分隔符拆分空字符串将返回 ['']

如果没有指定 sep 或者 sep 为 Nothing,则应用一种不同的分割算法: 连续空格的运行被视为一个单独的分隔符,如果字符串具有前导空格或尾随空格,则结果在开始或结束处将不包含空字符串。因此,分割一个空字符串或一个只包含空格的字符串,使用一个 Nothing 分隔符返回[]。

因此,为了更清楚地说明这一点,split()函数实现了两种不同的分割算法,并使用参数的存在来决定运行哪一个。这可能是因为它允许优化一个没有参数比一个有参数; 我不知道。

要计算行数,可以计算换行符的数量:

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

编辑 :

实际上,内置 count的另一个答案 更合适

使用 count():

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

没有参数的 .split()试图变得更聪明。它在任何空格、制表符、空格、换行符等上进行分割,而且由于这个原因,它还会跳过所有的空字符串。

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

从本质上讲,不带参数的 .split()用于从字符串中提取单词,而带参数的 .split()只是获取一个字符串并对其进行分割。

这就是不同的原因。

是的,通过分裂来计算直线并不是一种有效的方法。计算换行的数量,如果字符串没有以换行结束,则添加一个换行。

问: 我使用 split('\n')获取一个字符串中的行,发现 ''.split()返回一个空列表 [],而 ''.split('\n')返回 ['']

str.split()方法有两种算法。如果没有给出参数,它会在重复运行的空格上分裂。但是,如果给定一个参数,则将其视为一个没有重复运行的单一分隔符。

在拆分空字符串的情况下,第一种模式(没有参数)将返回一个空列表,因为空格被吃掉了,而且结果列表中没有要放入的值。

相反,第二种模式(带有 \n这样的参数)将生成第一个空字段。考虑一下,如果您编写了 '\n'.split('\n'),您将得到两个字段(一个分割,给出两个对半)。

问: 这种差异有什么具体原因吗?

当数据在具有可变空格数量的列中对齐时,第一种模式非常有用。例如:

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

第二种模式适用于分隔数据,例如 CSV,其中重复的逗号表示空字段。例如:

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

注意,结果字段的数目大于分隔符的数目。想象一下剪断一根绳子。如果你不裁员,你就只剩一块了。一刀两块。切两刀,分成三块。Python 的 str.split(delimiter)方法也是如此:

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

问: 有没有更方便的方法来计算字符串中的行数?

是的,有几个简单的方法。一个使用 str.count(),另一个使用 str.splitlines()。两种方式都会给出相同的答案,除非最后一行缺少 \n。如果最后的换行缺失,str.splitlines方法将给出准确的答案。一种更快、也更准确的方法是使用 count 方法,然后对最后的换行符进行修正:

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

来自@Kaz 的问题: 为什么两种截然不同的算法被硬塞到一个函数中?

str.split的签名已经有20年的历史了,那个时代的许多 API 都是非常实用的。虽然不是完美的,但是方法签名也不是“糟糕的”。在大多数情况下,Guido 的 API 设计选择经受住了时间的考验。

当前的 API 并非没有优点。考虑以下字符串:

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

当被要求将这些字符串划分为字段时,人们倾向于用同一个英语单词来描述它们,即“分离”。当被要求阅读诸如 fields = line.split()fields = line.split(',')之类的代码时,人们倾向于正确地将语句解释为“将一行分割成字段”。

MicrosoftExcel 的 文本到列工具也做了类似的 API 选择 在同一个工具中结合了两种分裂算法。人们似乎在心理上把分割场作为一个单一的概念,即使涉及到不止一种算法。

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

注意最后一句。

要计算行数,你可以简单地计算有多少个 \n:

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

最后一部分考虑到最后一行没有以 \n结束,即使这意味着 Hello, World!Hello, World!\n具有相同的行计数(对我来说这是合理的) ,否则您可以简单地将 1添加到 \n的计数中。

对于那些实际上希望避免在对空字符串调用 split时返回 ['']这种默认行为的人,以下是两种可能的一行解决方案:

list_ = s.split(*list(sep if s.count(sep) else []))
# Or this
list_ = s.split(sep) if s != "" else []

下面是如何在一个实际的例子中使用第一个:

import os


sep = os.linesep  # Split char


s_empty = ""
empty = s_empty.split(*list(sep if s_empty.count(sep) else []))


s_nonempty = f"a{sep}b"
nonempty = s_nonempty.split(*list(sep if s_nonempty.count(sep) else []))


print(f"Empty string: {empty}, non-empty string: {nonempty}")