Split Strings into words with multiple word boundary delimiters
我认为我想做的是一个相当常见的任务,但我在网上没有找到任何参考资料。我有带标点的文本,我想要一个单词列表。
1 | "Hey, you - what are you doing here!?" |
应该是
1 | ['hey', 'you', 'what', 'are', 'you', 'doing', 'here'] |
但是python的
拆分()
re.split(pattern, string[, maxsplit=0])
Split string by the occurrences of pattern. If capturing parentheses are used in pattern, then the text of all groups in the pattern are also returned as part of the resulting list. If maxsplit is nonzero, at most maxsplit splits occur, and the remainder of the string is returned as the final element of the list. (Incompatibility note: in the original Python 1.5 release, maxsplit was ignored. This has been fixed in later releases.)
1 2 3 4 5 6 | >>> re.split('\W+', 'Words, words, words.') ['Words', 'words', 'words', ''] >>> re.split('(\W+)', 'Words, words, words.') ['Words', ', ', 'words', ', ', 'words', '.', ''] >>> re.split('\W+', 'Words, words, words.', 1) ['Words', 'words, words.'] |
正则表达式有理的一种情况:
1 2 3 4 | import re DATA ="Hey, you - what are you doing here!?" print re.findall(r"[\w']+", DATA) # Prints ['Hey', 'you', 'what', 'are', 'you', 'doing', 'here'] |
另一种不使用regexp的快速方法是先替换字符,如下所示:
1 2 | >>> 'a;bcd,ef g'.replace(';',' ').replace(',',' ').split() ['a', 'bcd', 'ef', 'g'] |
如此多的答案,但我找不到任何有效地解决问题标题所要求的问题的解决方案(相反,在多个可能的分隔符上拆分,许多答案删除任何不是一个单词的东西,这是不同的)。因此,这里是对标题中的问题的回答,它依赖于Python的标准和高效的
1 2 3 | >>> import re # Will be splitting on: , <space> - ! ? : >>> filter(None, re.split("[, \-!?:]+","Hey, you - what are you doing here!?")) ['Hey', 'you', 'what', 'are', 'you', 'doing', 'here'] |
在哪里?
[…] 与里面列出的一个分隔符相匹配,- 正则表达式中的
\- 是为了防止将- 特殊解释为字符范围指示符(如A-Z )。 + 跳过一个或多个定界符(由于filter() 可以省略定界符,但这将不必要地在匹配的分隔符之间产生空字符串),以及filter(None, …) 删除可能由前导和尾随分隔符创建的空字符串(因为空字符串的布尔值为假)。
正如问题标题中所要求的那样,这个
此外,该解决方案还不受某些其他解决方案(请参见GhostDog74答案的第一条注释)中的非ASCII字符问题的影响。
与"手工"执行python循环和测试相比,
另一种方式,不使用regex
1 2 3 4 5 | import string punc = string.punctuation thestring ="Hey, you - what are you doing here!?" s = list(thestring) ''.join([o for o in s if not o in punc]).split() |
pro提示:使用
一些证明…
首先,慢方法(抱歉,pprzemek):
1 2 3 4 5 6 7 8 9 10 11 12 | >>> import timeit >>> S = 'Hey, you - what are you doing here!?' >>> def my_split(s, seps): ... res = [s] ... for sep in seps: ... s, res = res, [] ... for seq in s: ... res += seq.split(sep) ... return res ... >>> timeit.Timer('my_split(S, punctuation)', 'from __main__ import S,my_split; from string import punctuation').timeit() 54.65477919578552 |
接下来,我们使用
1 2 | >>> timeit.Timer('findall(r"\w+", S)', 'from __main__ import S; from re import findall').timeit() 4.194725036621094 |
最后,我们使用
1 2 3 4 | >>> from string import translate,maketrans,punctuation >>> T = maketrans(punctuation, ' '*len(punctuation)) >>> timeit.Timer('translate(S, T).split()', 'from __main__ import S,T,translate').timeit() 1.2835021018981934 |
说明:
不过,这有点尴尬,因为它需要一个翻译表来实现这个魔力。您可以使用
接下来,我们使用好的旧
回答得有点晚。),但我也有类似的困境,不想使用"re"模块。
1 2 3 4 5 6 7 8 9 10 | def my_split(s, seps): res = [s] for sep in seps: s, res = res, [] for seq in s: res += seq.split(sep) return res print my_split('1111 2222 3333;4444,5555;6666', [' ', ';', ',']) ['1111', '', '2222', '3333', '4444', '5555', '6666'] |
1 2 3 | join = lambda x: sum(x,[]) # a.k.a. flatten1([[1],[2,3],[4]]) -> [1,2,3,4] # ...alternatively... join = lambda lists: [x for l in lists for x in l] |
然后这就变成了一个三行程序:
1 2 3 | fragments = [text] for token in tokens: fragments = join(f.split(token) for f in fragments) |
解释
这就是哈斯克尔所说的单子列表。单子背后的想法是,一旦"在单子里"你"留在单子里",直到有东西把你带走。例如,在haskell中,假设您将python
您可以将其抽象为一个函数,默认情况下使用
这种方法的优点:
- 这种方法(与基于原始regex的方法不同)可以使用任意长度的标记(regex也可以使用更高级的语法)。
- 您不仅仅局限于标记;您可以用任意逻辑代替每个标记,例如,"标记"中的一个可以是一个根据嵌套括号的方式拆分的函数。
首先,我想同意其他人的观点,即基于regex或
我的主要目标是将其他一些答案中的想法归纳为一个解决方案,该解决方案可以适用于包含不仅仅是regex单词的字符串(即,将标点字符的显式子集与白名单字符的显式子集进行黑名单)。
注意,在任何方法中,也可以考虑使用
我惊讶地发现到目前为止还没有答案使用re.sub(…)。我发现这是解决这个问题的一种简单而自然的方法。
1 2 3 4 5 | import re my_str ="Hey, you - what are you doing here!?" words = re.split(r'\s+', re.sub(r'[,\-!?]', ' ', my_str).strip()) |
在这个解决方案中,我在
这是另外几行,但是它的好处是可以扩展,而不必检查是否需要在regex中转义某个字符。
1 2 3 4 5 6 7 | my_str ="Hey, you - what are you doing here!?" replacements = (',', '-', '!', '?') for r in replacements: my_str = my_str.replace(r, ' ') words = my_str.split() |
如果能够将str.replace映射到字符串,那就太好了,但是我不认为可以用不可变的字符串来完成,虽然根据字符列表进行映射可以工作,但是对每个字符执行每个替换听起来太过分了。(编辑:有关函数示例,请参见下一选项。)
选项3-Functools.Reduce(在python 2中,
1 2 3 4 5 6 7 | import functools my_str ="Hey, you - what are you doing here!?" replacements = (',', '-', '!', '?') my_str = functools.reduce(lambda s, sep: s.replace(sep, ' '), replacements, my_str) words = my_str.split() |
试试这个:
1 2 3 4 5 | import re phrase ="Hey, you - what are you doing here!?" matches = re.findall('\w+', phrase) print matches |
这将打印
使用两次替换:
1 2 | a = '11223FROM33344INTO33222FROM3344' a.replace('FROM', ',,,').replace('INTO', ',,,').split(',,,') |
结果:
1 | ['11223', '33344', '33222', '3344'] |
我喜欢Re,但我的解决方案是:
1 2 3 4 | from itertools import groupby sep = ' ,-!?' s ="Hey, you - what are you doing here!?" print [''.join(g) for k, g in groupby(s, sep.__contains__) if not k] |
sep."包含"是"in"运算符使用的方法。基本上和
1 | lambda ch: ch in sep |
但是这里更方便。
groupby获取字符串和函数。它使用该函数将字符串分组:每当函数值更改时,都会生成一个新组。所以,九月。"包含"正是我们需要的。
groupby返回一个成对序列,其中pair[0]是函数的结果,pair[1]是一个组。使用"如果不是k",我们用分隔符筛选出组(因为sep的结果"包含"在分隔符上是真的)。好吧,就这些了-现在我们有了一系列的组,其中每个组都是一个单词(组实际上是一个iterable,所以我们使用join将其转换为字符串)。
这个解决方案非常一般,因为它使用一个函数来分隔字符串(您可以根据需要的任何条件进行拆分)。另外,它不会创建中间字符串/列表(您可以删除join,表达式将变为lazy,因为每个组都是迭代器)
不使用re-module函数re.split,您可以使用pandas的series.str.split方法获得相同的结果。
首先,使用上面的字符串创建一个序列,然后将该方法应用于该序列。
thestring.str.split(pat = ',|-')
参数pat接受分隔符并将拆分字符串作为数组返回。这里使用(或运算符)传递两个分隔符。输出如下:
我已经熟悉了python,需要同样的东西。芬德尔的解决方案可能更好,但我想到了:
1 | tokens = [x.strip() for x in data.split(',')] |
在python 3中,您可以为每个人使用py4e-python中的方法。
We can solve both these problems by using the string methods
lower ,punctuation , andtranslate . Thetranslate is the most subtle of the methods. Here is the documentation fortranslate :
Replace the characters in
fromstr with the character in the same position intostr and delete all characters that are indeletestr . Thefromstr andtostr can be empty strings and thedeletestr parameter can be omitted.
您可以看到"标点符号":
1 2 3 4 | In [10]: import string In [11]: string.punctuation Out[11]: '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~' |
例如:
1 2 3 4 5 6 7 8 9 10 | In [12]: your_str ="Hey, you - what are you doing here!?" In [13]: line = your_str.translate(your_str.maketrans('', '', string.punctuation)) In [14]: line = line.lower() In [15]: words = line.split() In [16]: print(words) ['hey', 'you', 'what', 'are', 'you', 'doing', 'here'] |
有关更多信息,请参阅:
- PY4E-人人都有Python
- STR翻译
- 大跨越
- python string maketrans()方法
使用maketrans和translate可以轻松、整洁地完成它
1 2 3 4 5 | import string specials = ',.!?:;"()<>[]#$=-/' trans = string.maketrans(specials, ' '*len(specials)) body = body.translate(trans) words = body.strip().split() |
首先,我不认为您的意图是实际使用标点作为拆分函数中的分隔符。您的描述表明您只需要从结果字符串中消除标点符号。
我经常遇到这种情况,我通常的解决方案不需要重复。
一个线性lambda函数,具有列表理解功能:(要求
1 2 3 4 5 6 | split_without_punc = lambda text : [word.strip(string.punctuation) for word in text.split() if word.strip(string.punctuation) != ''] # Call function split_without_punc("Hey, you -- what are you doing?!") # returns ['Hey', 'you', 'what', 'are', 'you', 'doing'] |
功能(传统)
作为传统的功能,这仍然只是两行列表理解(除
1 2 3 4 5 6 7 8 9 10 | def split_without_punctuation2(text): # Split by whitespace words = text.split() # Strip punctuation from each word return [word.strip(ignore) for word in words if word.strip(ignore) != ''] split_without_punctuation2("Hey, you -- what are you doing?!") # returns ['Hey', 'you', 'what', 'are', 'you', 'doing'] |
它也会自然地保留收缩和连字符单词的完整性。在拆分之前,可以始终使用
对于更一般的解决方案(可以指定要消除的字符),如果不理解列表,则可以得到:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | def split_without(text: str, ignore: str) -> list: # Split by whitespace split_string = text.split() # Strip any characters in the ignore string, and ignore empty strings words = [] for word in split_string: word = word.strip(ignore) if word != '': words.append(word) return words # Situation-specific call to general function import string final_text = split_without("Hey, you - what are you doing?!", string.punctuation) # returns ['Hey', 'you', 'what', 'are', 'you', 'doing'] |
当然,也可以将lambda函数归纳为任何指定的字符串。
首先,在循环中执行任何regex操作之前,务必使用re.compile(),因为它的工作速度比正常操作快。
因此,对于您的问题,首先编译模式,然后对其执行操作。
1 2 3 4 | import re DATA ="Hey, you - what are you doing here!?" reg_tok = re.compile("[\w']+") print reg_tok.findall(DATA) |
这是答案和一些解释。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | st ="Hey, you - what are you doing here!?" # replace all the non alpha-numeric with space and then join. new_string = ''.join([x.replace(x, ' ') if not x.isalnum() else x for x in st]) # output of new_string 'Hey you what are you doing here ' # str.split() will remove all the empty string if separator is not provided new_list = new_string.split() # output of new_list ['Hey', 'you', 'what', 'are', 'you', 'doing', 'here'] # we can join it to get a complete string without any non alpha-numeric character ' '.join(new_list) # output 'Hey you what are you doing' |
或者在一行中,我们可以这样做:
1 2 3 4 | (''.join([x.replace(x, ' ') if not x.isalnum() else x for x in st])).split() # output ['Hey', 'you', 'what', 'are', 'you', 'doing', 'here'] |
更新答案
创建一个函数,将两个字符串(要拆分的源字符串和分隔符的split list字符串)作为输入,并输出拆分单词列表:
1 2 3 4 5 6 7 8 9 10 11 12 13 | def split_string(source, splitlist): output = [] # output list of cleaned words atsplit = True for char in source: if char in splitlist: atsplit = True else: if atsplit: output.append(char) # append new word after split atsplit = False else: output[-1] = output[-1] + char # continue copying characters until next split return output |
实现这一点的另一种方法是使用自然语言工具包(NLTK)。
1 2 3 4 | import nltk data="Hey, you - what are you doing here!?" word_tokens = nltk.tokenize.regexp_tokenize(data, r'\w+') print word_tokens |
这张照片是:
这种方法最大的缺点是需要安装NLTK包。
好处是,一旦你得到了代币,你可以用剩余的NLTK包做很多有趣的事情。
1 2 3 4 5 6 7 8 9 10 11 12 13 | def get_words(s): l = [] w = '' for c in s.lower(): if c in '-!?,. ': if w != '': l.append(w) w = '' else: w = w + c if w != '': l.append(w) return l |
用法如下:
1 2 3 | >>> s ="Hey, you - what are you doing here!?" >>> print get_words(s) ['hey', 'you', 'what', 'are', 'you', 'doing', 'here'] |
我最喜欢
1 2 3 4 | def split_string(text, splitlist): for sep in splitlist: text = text.replace(sep, splitlist[0]) return filter(None, text.split(splitlist[0])) if splitlist else [text] |
我认为以下是满足您需求的最佳答案:
1 | filter(None, re.compile('[ |,|\-|!|?]').split("Hey, you - what are you doing here!?") |
以下是我与多个除沫器的分离:
1 2 3 4 5 6 7 8 9 10 11 | def msplit( str, delims ): w = '' for z in str: if z not in delims: w += z else: if len(w) > 0 : yield w w = '' if len(w) > 0 : yield w |
和@ooboo有同样的问题,找到这个主题@Ghostdog74启发了我,也许有人发现我的解决方案有用
1 2 3 | str1='adj:sg:nom:m1.m2.m3:pos' splitat=':.' ''.join([ s if s not in splitat else ' ' for s in str1]).split() |
在空格处输入内容,如果不想在空格处拆分,请使用相同的字符进行拆分。
如果要执行可逆操作(保留分隔符),可以使用以下函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | def tokenizeSentence_Reversible(sentence): setOfDelimiters = ['.', ' ', ',', '*', ';', '!'] listOfTokens = [sentence] for delimiter in setOfDelimiters: newListOfTokens = [] for ind, token in enumerate(listOfTokens): ll = [([delimiter, w] if ind > 0 else [w]) for ind, w in enumerate(token.split(delimiter))] listOfTokens = [item for sublist in ll for item in sublist] # flattens. listOfTokens = filter(None, listOfTokens) # Removes empty tokens: '' newListOfTokens.extend(listOfTokens) listOfTokens = newListOfTokens return listOfTokens |
这是我的看法……
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | def split_string(source,splitlist): splits = frozenset(splitlist) l = [] s1 ="" for c in source: if c in splits: if s1: l.append(s1) s1 ="" else: print s1 s1 = s1 + c if s1: l.append(s1) return l >>>out = split_string("First Name,Last Name,Street Address,City,State,Zip Code",",") >>>print out >>>['First Name', 'Last Name', 'Street Address', 'City', 'State', 'Zip Code'] |
您需要python的regex模块的
http://www.regular-expressions.info/python.html网站
例子