什么是Pythonic方法在给定字符出现之前拆分字符串的方式?
例如,我想在任何大写字母(可能除了第一个)出现的地方拆分
'TheLongAndWindingRoad'
并获得
['The', 'Long', 'And', 'Winding', 'Road']
。
编辑:它还应该拆分单个出现,即从'ABC'
中,我想获得
['A','B','C']
。
很遗憾,在Python中不可能按零宽度匹配分割。但是你可以使用re.findall
代替:
>>> import re
>>> re.findall('[A-Z][^A-Z]*', 'TheLongAndWindingRoad')
['The', 'Long', 'And', 'Winding', 'Road']
>>> re.findall('[A-Z][^A-Z]*', 'ABC')
['A', 'B', 'C']
这里有一个替代正则表达式的解决方案。问题可以重新描述为“在执行拆分之前,在每个大写字母前面插入一个空格”的方法:
>>> s = "TheLongAndWindingRoad ABC A123B45"
>>> re.sub( r"([A-Z])", r" \1", s).split()
['The', 'Long', 'And', 'Winding', 'Road', 'A', 'B', 'C', 'A123', 'B45']
这种方法的优点是保留了所有非空白字符,而大多数其他解决方案则没有这样做。
使用前后断言:
在Python 3.7中,您可以这样做:
re.split('(?<=.)(?=[A-Z])', 'TheLongAndWindingRoad')
然后它产生:
['The', 'Long', 'And', 'Winding', 'Road']
你需要使用回顾表达式来避免在开头出现空字符串。>>> import re
>>> re.findall('[A-Z][a-z]*', 'TheLongAndWindingRoad')
['The', 'Long', 'And', 'Winding', 'Road']
>>> re.findall('[A-Z][a-z]*', 'SplitAString')
['Split', 'A', 'String']
>>> re.findall('[A-Z][a-z]*', 'ABC')
['A', 'B', 'C']
如果你想将"It'sATest"
拆分为["It's", 'A', 'Test']
,则需要将正则表达式更改为"[A-Z][a-z']*"
。- ChristopheDre.findall('[A-Z][a-z]*', "It's about 70% of the Economy") -----> ['It', 'Economy']
@ChristopheD的解决方案的一种变化
s = 'TheLongAndWindingRoad'
pos = [i for i,e in enumerate(s+'A') if e.isupper()]
parts = [s[pos[j]:pos[j+1]] for j in xrange(len(pos)-1)]
print parts
range
而不是xrange
。 - raspiduinoxrange
还可以,@raspiduino,不是因为我在Python 2中测试过它,而是因为它在Python 3中与range
一起工作,而range
与xrange
显然是一样的。Python 3中没有xrange
:https://stackoverflow.com/questions/94935/what-is-the-difference-between-range-and-xrange-functions-in-python-2-x - undefined我认为一个更好的答案可能是将字符串分割成不以大写字母结尾的单词。这样就可以处理字符串不以大写字母开头的情况。
re.findall('.[^A-Z]*', 'aboutTheLongAndWindingRoad')
例子:
>>> import re
>>> re.findall('.[^A-Z]*', 'aboutTheLongAndWindingRoadABC')
['about', 'The', 'Long', 'And', 'Winding', 'Road', 'A', 'B', 'C']
import re
filter(None, re.split("([A-Z][^A-Z]*)", "TheLongAndWindingRoad"))
或者
[s for s in re.split("([A-Z][^A-Z]*)", "TheLongAndWindingRoad") if s]
[s for s in re.compile(r"([A-Z][^A-Z]*)").split( "TheLongAndWindingRoad") if s]
可以得到 ['The', 'Long', 'And', 'Winding', 'Road']
。 - smcifilter
的用法和带条件的列表推导式是一样的。你对此有什么意见吗? - Gabefilter(lambdaconditionfunc, ...)
更清晰。
b)在Python 3中,filter()
返回一个迭代器。所以它们不完全等价。
c)我认为filter()
也更慢。 - smciPythonic方式可能是:
"".join([(" "+i if i.isupper() else i) for i in 'TheLongAndWindingRoad']).strip().split()
['The', 'Long', 'And', 'Winding', 'Road']
适用于Unicode,避免使用re/re2。
"".join([(" "+i if i.isupper() else i) for i in 'СуперМаркетыПродажаКлиент']).strip().split()
['Супер', 'Маркеты', 'Продажа', 'Клиент']
"".join(" " + c if c.isupper() else c for c in s).strip()
我没有看到任何违规之处,@rearThing。我很想知道你对这个方法有何不满意之处。毕竟它使用了生成器表达式:https://peps.python.org/pep-0289/当你去掉不必要的括号后,它看起来更好。 - undefined这是一种不需要使用正则表达式的方法,同时如果需要的话可以保留连续的大写字母。
def split_on_uppercase(s, keep_contiguous=False):
"""
Args:
s (str): string
keep_contiguous (bool): flag to indicate we want to
keep contiguous uppercase chars together
Returns:
"""
string_length = len(s)
is_lower_around = (lambda: s[i-1].islower() or
string_length > (i + 1) and s[i + 1].islower())
start = 0
parts = []
for i in range(1, string_length):
if s[i].isupper() and (not keep_contiguous or is_lower_around()):
parts.append(s[start: i])
start = i
parts.append(s[start:])
return parts
>>> split_on_uppercase('theLongWindingRoad')
['the', 'Long', 'Winding', 'Road']
>>> split_on_uppercase('TheLongWindingRoad')
['The', 'Long', 'Winding', 'Road']
>>> split_on_uppercase('TheLongWINDINGRoadT', True)
['The', 'Long', 'WINDING', 'Road', 'T']
>>> split_on_uppercase('ABC')
['A', 'B', 'C']
>>> split_on_uppercase('ABCD', True)
['ABCD']
>>> split_on_uppercase('')
['']
>>> split_on_uppercase('hello world')
['hello world']
src = 'TheLongAndWindingRoad'
glue = ' '
result = ''.join(glue + x if x.isupper() else x for x in src).strip(glue).split(glue)
'[a-zA-Z][^A-Z]*'
作为正则表达式即可。 - knubprint(re.findall('^[a-z]+|[A-Z][^A-Z]*', 'theLongAndWindingRoad'))
。 - Ulysses