【发布时间】:2013-05-14 17:58:51
【问题描述】:
我正在使用split('\n') 获取一个字符串中的行,发现''.split() 返回一个空列表[],而''.split('\n') 返回['']。这种差异有什么具体原因吗?
还有没有更方便的方法来计算字符串中的行数?
【问题讨论】:
标签: python string algorithm parsing split
我正在使用split('\n') 获取一个字符串中的行,发现''.split() 返回一个空列表[],而''.split('\n') 返回['']。这种差异有什么具体原因吗?
还有没有更方便的方法来计算字符串中的行数?
【问题讨论】:
标签: python string algorithm parsing 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'
当被要求将这些字符串分解为字段时,人们倾向于使用同一个英文单词“split”来描述两者。当被要求阅读诸如 fields = line.split() 或 fields = line.split(',') 之类的代码时,人们倾向于将这些语句正确解释为“将一行拆分为多个字段”。
Microsoft Excel 的 text-to-columns tool 做出了类似的 API 选择并 在同一个工具中结合了两种分割算法。尽管涉及多个算法,但人们似乎将场分裂建模为一个单一的概念。
【讨论】:
根据the documentation,这似乎只是它应该工作的方式:
用指定的分隔符分割空字符串返回
['']。如果 sep 未指定或为 None,则应用不同的分割算法:连续的空格被视为单个分隔符,如果字符串有前导或尾随,结果将在开头或结尾不包含空字符串空白。因此,使用 None 分隔符拆分空字符串或仅包含空格的字符串将返回 []。
因此,为了更清楚起见,split() 函数实现了两种不同的拆分算法,并使用参数的存在来决定运行哪一个。这可能是因为它允许优化没有参数的参数而不是有参数的参数;我不知道。
【讨论】:
>>> 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 的计数中。
【讨论】:
要计算行数,可以计算换行数:
n_lines = sum(1 for s in the_string if s == "\n") + 1 # add 1 for last line
编辑:
The other answer内置count更合适,其实
【讨论】:
count之外,布尔值是可添加的(实际上,它们是int的子类),所以genexp可以写成sum(s == "\n" for s in the_string)。
.split() 不带参数试图变得聪明。它会在任何空格、制表符、空格、换行符等处拆分,并因此跳过所有空字符串。
>>> " fii fbar \n bopp ".split()
['fii', 'fbar', 'bopp']
本质上,不带参数的.split() 用于从字符串中提取单词,而带参数的.split() 只接受一个字符串并将其拆分。
这就是差异的原因。
是的,通过拆分来计算行数并不是一种有效的方法。计算换行的数量,如果字符串不以换行结尾,则添加一个。
【讨论】:
使用count():
s = "Line 1\nLine2\nLine3"
n_lines = s.count('\n') + 1
【讨论】:
cat file 会使您的命令行出现乱码并且颠覆会抱怨。 vi 总是附加一个。