Python 如何从字符串的开头删除到第二个特定字符?
我有一组字符串,其形式如下:Python 如何从字符串的开头删除到第二个特定字符?,python,string,Python,String,我有一组字符串,其形式如下: 'foo.bar.baz.spam.spam.spam...etc' 它们很可能有三个或更多的多字母子串,由分隔。可能存在少于两个的格式错误的字符串,在这种情况下,我需要原始字符串 首先想到的是str.partition方法,如果我在第一个之后查找所有内容,我会使用它: 'foo.bar.baz.boink.a.b.c'.partition('.')[2] 返回 'bar.baz.boink.a.b.c' 这可以重复: def secondpartition(
'foo.bar.baz.spam.spam.spam...etc'
它们很可能有三个或更多的多字母子串,由
分隔。可能存在少于两个
的格式错误的字符串,在这种情况下,我需要原始字符串
首先想到的是str.partition方法,如果我在第一个
之后查找所有内容,我会使用它:
'foo.bar.baz.boink.a.b.c'.partition('.')[2]
返回
'bar.baz.boink.a.b.c'
这可以重复:
def secondpartition(s):
return s.partition('.')[2].partition('.')[2] or s
但这是否有效?两次调用一个方法并两次使用下标似乎并不有效。这当然不雅观。有更好的办法吗
主要问题是:
如何从
字符的开始到第二个实例删除所有内容,使'foo.bar.baz.spam.spam.spam'
变成'baz.spam.spam'
?这样做的最佳/最有效的方法是什么
摘要:这是最有效的方法(概括为n个字符): 但我展示了其他方法进行比较 使用字符串方法和正则表达式可以实现这一点。我会确保你能按照顺序剪切和粘贴所有内容,从而跟随翻译 首次进口:
import re
import timeit
from itertools import islice
不同的方法:字符串方法
问题中提到的方法是划分两次,但我不这么认为,因为这似乎很不雅观,而且不必要地重复:
def secondpartition(s):
return s.partition('.')[2].partition('.')[2] or s
第二种方法是在
上拆分,从第二个开始切片,然后加入
。这给我的印象是相当优雅,我认为这会相当有效
def splitslicejoin(s):
return '.'.join(s.split('.')[2:]) or s
dot = re.compile('\.')
def find2nddot(s):
for i, found_dot in enumerate(dot.finditer(s)):
if i == 1:
return s[found_dot.end():] or s
return s
但是切片创建了一个不必要的额外列表。但是,来自itertools模块的islice提供了一个不可用的iterable!所以我认为这会做得更好:
def splitislicejoin(s):
return '.'.join(islice(s.split('.'), 2, None)) or s
不同的方法:正则表达式
现在是正则表达式。正则表达式想到的第一种方法是找到一个空字符串并替换为第二个
dot2 = re.compile('.*?\..*?\.')
def redot2(s):
return dot2.sub('', s)
但我突然想到,最好使用非捕获组,并在最后返回匹配项:
dot2match = re.compile('(?:.*?\..*?\.)(.*)')
def redot2match(s):
match = dot2match.match(s)
if match is not None:
return match.group(1)
else:
return s
最后,我可以使用正则表达式搜索找到第二个
的结尾,然后使用该索引对字符串进行切片,这将使用更多的代码,但可能仍然是快速和高效的
def splitslicejoin(s):
return '.'.join(s.split('.')[2:]) or s
dot = re.compile('\.')
def find2nddot(s):
for i, found_dot in enumerate(dot.finditer(s)):
if i == 1:
return s[found_dot.end():] or s
return s
更新Falsetru建议使用str.split
的maxsplit参数,我完全忘记了它。我的想法是,这可能是最直接的方法,但作业和额外的检查可能会伤害它
def maxsplittwo(s):
parts = s.split('.', 2)
if len(parts) <= 2:
return s
return parts[-1]
这是完全合适的,因为没有足够的
s将是例外
测试
现在让我们测试一下我们的函数。首先,让我们断言它们确实有效(这不是生产代码中的最佳实践,应该使用unittests,但对于StackOverflow的快速验证非常有用):
断言不会引发断言错误,因此现在让我们对它们计时,看看它们的执行情况:
性能
setup = 'from __main__ import ' + ', '.join(functions)
perfs = {}
for func in functions:
perfs[func] = min(timeit.repeat(func + '("foo.bar.baz.a.b.c")', setup))
for func in sorted(perfs, key=lambda x: perfs[x]):
print('{0}: {1}'.format(func, perfs[func]))
结果
Update表现最好的是falsetru的maxsplittwo
,它略微削弱了secondpartition函数。祝贺falsetru。这是有道理的,因为这是一种非常直接的方法。而JonClements的修改甚至更好
maxsplittwoexcept: 1.01329493523
maxsplittwo: 1.08345508575
secondpartition: 1.1336209774
splitslicejoin: 1.49500417709
redot2match: 2.22423219681
splitislicejoin: 3.4605550766
find2nddot: 3.77172589302
redot2: 4.69134306908
旧版运行和分析,无falsetru的maxsplittwo和JonClements的maxsplittwo,除了:
secondpartition: 0.636116637553
splitslicejoin: 1.05499717616
redot2match: 1.10188927335
redot2: 1.6313087087
find2nddot: 1.65386564664
splitislicejoin: 3.13693511439
事实证明,最有效的方法是两次分区,尽管我的直觉不喜欢
此外,我对使用islice的直觉是错误的,在这种情况下,它的性能要差得多,因此,如果遇到类似的代码位,来自常规切片的额外列表可能是值得权衡的
在正则表达式中,我所需字符串的匹配方法在这里表现最好,几乎与
splitslicejoin
并列:这是性能最好的方法(概括为n个字符):
但我展示了其他方法进行比较
使用字符串方法和正则表达式可以实现这一点。我会确保你能按照顺序剪切和粘贴所有内容,从而跟随翻译
首次进口:
import re
import timeit
from itertools import islice
不同的方法:字符串方法
问题中提到的方法是划分两次,但我不这么认为,因为这似乎很不雅观,而且不必要地重复:
def secondpartition(s):
return s.partition('.')[2].partition('.')[2] or s
第二种方法是在
上拆分,从第二个开始切片,然后加入
。这给我的印象是相当优雅,我认为这会相当有效
def splitslicejoin(s):
return '.'.join(s.split('.')[2:]) or s
dot = re.compile('\.')
def find2nddot(s):
for i, found_dot in enumerate(dot.finditer(s)):
if i == 1:
return s[found_dot.end():] or s
return s
但是切片创建了一个不必要的额外列表。但是,来自itertools模块的islice提供了一个不可用的iterable!所以我认为这会做得更好:
def splitislicejoin(s):
return '.'.join(islice(s.split('.'), 2, None)) or s
不同的方法:正则表达式
现在是正则表达式。正则表达式想到的第一种方法是找到一个空字符串并替换为第二个
dot2 = re.compile('.*?\..*?\.')
def redot2(s):
return dot2.sub('', s)
但我突然想到,最好使用非捕获组,并在最后返回匹配项:
dot2match = re.compile('(?:.*?\..*?\.)(.*)')
def redot2match(s):
match = dot2match.match(s)
if match is not None:
return match.group(1)
else:
return s
最后,我可以使用正则表达式搜索找到第二个
的结尾,然后使用该索引对字符串进行切片,这将使用更多的代码,但可能仍然是快速和高效的
def splitslicejoin(s):
return '.'.join(s.split('.')[2:]) or s
dot = re.compile('\.')
def find2nddot(s):
for i, found_dot in enumerate(dot.finditer(s)):
if i == 1:
return s[found_dot.end():] or s
return s
更新Falsetru建议使用str.split
的maxsplit参数,我完全忘记了它。我的想法是,这可能是最直接的方法,但作业和额外的检查可能会伤害它
def maxsplittwo(s):
parts = s.split('.', 2)
if len(parts) <= 2:
return s
return parts[-1]
哪一个是t