使用多个字边界分隔符将字符串拆分成单词

2024-11-22 08:47:00
admin
原创
26
摘要:问题描述:我认为我想做的是一个相当常见的任务,但我在网上找不到任何参考资料。我有带标点符号的文本,我想要一个单词列表。"Hey, you - what are you doing here!?" 应该是['hey', 'you', 'what', 'are', 'you', 'doing'...

问题描述:

我认为我想做的是一个相当常见的任务,但我在网上找不到任何参考资料。我有带标点符号的文本,我想要一个单词列表。

"Hey, you - what are you doing here!?"

应该是

['hey', 'you', 'what', 'are', 'you', 'doing', 'here']

但是 Pythonstr.split()只适用于一个参数,因此我用空格分隔后,所有单词都带有标点符号。有什么想法吗?


解决方案 1:

重新分割()

re.split(模式,字符串[,maxsplit=0])

根据模式的出现次数拆分字符串。如果在模式中使用捕获括号,则模式中所有组的文本也将作为结果列表的一部分返回。如果 maxsplit 非零,则最多发生 maxsplit 次拆分,并且字符串的其余部分将作为列表的最后一个元素返回。(不兼容性说明:在最初的 Python 1.5 版本中,maxsplit 被忽略。这已在后续版本中得到修复。)

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

解决方案 2:

正则表达式合理的情况:

import re
DATA = "Hey, you - what are you doing here!?"
print re.findall(r"[w']+", DATA)
# Prints ['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']

解决方案 3:

另一种不使用正则表达式的快速方法是先替换字符,如下所示:

>>> 'a;bcd,ef g'.replace(';',' ').replace(',',' ').split()
['a', 'bcd', 'ef', 'g']

解决方案 4:

答案如此之多,但我找不到任何解决方案能够有效地完成问题标题所要求的字面要求(根据多个可能的分隔符进行拆分 - 相反,许多答案根据任何非单词的内容进行拆分,这是不同的)。 因此,这里是对标题中问题的回答,它依赖于 Python 的标准和高效re模块:

>>> import re  # Will be splitting on: , <space> - ! ? :
>>> filter(None, re.split(r"[, -!?:]+", "Hey, you-what are you doing here!?"))
['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']

在哪里:

  • […]与列出的分隔符​​之一匹配,

  • 正则表达式中的-是为了防止 被特殊解释-为字符范围指示符(如A-Z),

  • 跳过+一个或多个分隔符(可以使用 来省略filter(),但这会在匹配的单字符分隔符之间不必要地产生空字符串),

  • 使用原始字符串r"…"可以明确地表示``字符串将保持原样(并且不会引入特殊字符)—这对于 Python 3.12+ 很有用—,并且

  • filter(None, …)删除可能由前导和尾随分隔符创建的空字符串(因为空字符串具有 false 布尔值)。

re.split()正如问题标题所要求的那样,这正是“用多个分隔符拆分”。

此外,该解决方案还可以避免其他一些解决方案中发现的单词中非 ASCII 字符的问题(参见ghostdog74 的回答的第一条评论)。

re模块比“手动”执行 Python 循环和测试效率更高(速度和简洁性)!

解决方案 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()

解决方案 6:

专业提示:使用string.translatePython 最快的字符串操作。

一些证据...

首先,慢速的方法(对不起pprzemek):

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

接下来,我们使用re.findall()(如建议的答案所示)。速度快得多:

>>> timeit.Timer('findall(r"w+", S)', 'from __main__ import S; from re import findall').timeit()
4.194725036621094

最后,我们使用translate

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

解释:

string.translate是用 C 实现的,与 Python 中的许多字符串处理函数不同,它string.translate 不会生成新字符串。因此,它是字符串替换速度最快的函数。

不过,这有点尴尬,因为它需要一个转换表才能实现这个魔法。您可以使用maketrans()便利函数制作一个转换表。这里的目标是将所有不需要的字符转换为空格。一对一替换。同样,不会产生新数据。所以这很快

接下来,我们使用老办法split(). split(),默认情况下它将对所有空格字符进行操作,并将它们分组进行拆分。结果将是您想要的单词列表。这种方法比 快近 4 倍re.findall()

解决方案 7:

我遇到了类似的困境并且不想使用“re”模块。

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

解决方案 8:

首先,我想同意其他人的观点,即基于正则表达式或的str.translate(...)解决方案是最高效的。对于我的用例来说,这个函数的性能并不重要,所以我想添加一些我根据该标准考虑过的想法。

我的主要目标是将其他一些答案中的想法概括为一个解决方案,该解决方案可以适用于包含不仅仅是正则表达式单词的字符串(即将标点符号的明确子集列入黑名单,将单词字符列入白名单)。

请注意,无论采用哪种方法,人们都可能考虑使用string.punctuation手动定义的列表来代替。

选项 1 - re.sub

我很惊讶到目前为止没有答案使用re.sub(...)。我发现这是解决这个问题的一个简单而自然的方法。

import re

my_str = "Hey, you - what are you doing here!?"

words = re.split(r's+', re.sub(r'[,-!?]', ' ', my_str).strip())

re.sub(...)在这个解决方案中,我嵌套了对内部的调用re.split(...)- 但如果性能至关重要,那么在外部编译正则表达式可能会有益 - 对于我的用例,差异并不显著,所以我更喜欢简单性和可读性。

选项 2 - str.replace

这是多了几行,但它的好处是可以扩展,而不必检查是否需要在正则表达式中转义某个字符。

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 中,reduce可以在全局命名空间中使用,无需从 functools 导入它。)

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

解决方案 9:

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]

然后这变成了三行:

fragments = [text]
for token in tokens:
    fragments = join(f.split(token) for f in fragments)

解释

这就是 Haskell 中所谓的 List monad。monad 背后的想法是,一旦“进入 monad”,您就会“留在 monad 中”,直到有东西将您带出。例如,在 Haskell 中,假设您将 Pythonrange(n) -> [1,2,...,n]函数映射到 List 上。如果结果是一个 List,它将就地附加到 List 中,因此您将得到类似 的内容map(range, [3,4,1]) -> [0,1,2,0,1,2,3,0]。这称为 map-append(或 mappend,或类似的东西)。这里的想法是,您已经获得了要应用的此操作(在标记上拆分),并且每当您执行此操作时,您都会将结果连接到列表中。

您可以将其抽象为一个函数并tokens=string.punctuation默认使用。

这种方法的优点:

  • 这种方法(与简单的基于正则表达式的方法不同)可以处理任意长度的标记(正则表达式也可以使用更高级的语法)。

  • 您并不局限于单纯的标记;您可以用任意逻辑代替每个标记,例如,其中一个“标记”可以是一个根据括号的嵌套方式进行拆分的函数。

解决方案 10:

我喜欢re,但是这是我不使用 re 的解决方案:

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.__contains__是 'in' 运算符使用的方法。基本上它与

lambda ch: ch in sep

但这里更方便。

groupby获取我们的字符串和函数。它使用该函数将字符串拆分成组:每当函数的值发生变化时 - 就会生成一个新组。因此,sep.__contains__正是我们所需要的。

groupby返回一个成对的序列,其中 pair[0] 是我们函数的结果,而 pair[1] 是一个组。使用“if not k”,我们过滤掉带有分隔符的组(因为sep.__contains__的结果在分隔符上为 True)。好了,就这些了 - 现在我们有了一个组序列,每个组都是一个单词(group 实际上是可迭代的,因此我们使用join将其转换为字符串)。

这个解决方案非常通用,因为它使用函数来分离字符串(您可以根据需要的任何条件进行拆分)。此外,它不会创建中间字符串/列表(您可以删除连接,表达式将变得懒惰,因为每个组都是一个迭代器)

解决方案 11:

使用替换两次:

a = '11223FROM33344INTO33222FROM3344'
a.replace('FROM', ',,,').replace('INTO', ',,,').split(',,,')

结果:

['11223', '33344', '33222', '3344']

解决方案 12:

试试这个:

import re

phrase = "Hey, you - what are you doing here!?"
matches = re.findall('w+', phrase)
print matches

这将打印['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']

解决方案 13:

在 Python 3 中,您可以使用PY4E - Python for Everybody中的方法。

我们可以通过使用字符串方法lowerpunctuation和 来解决这两个问题translatetranslate是最精妙的方法。 以下是 的文档translate

your_string.translate(your_string.maketrans(fromstr, tostr, deletestr))

将 中的字符替换fromstr为 中相同位置的字符tostr,并删除 中的所有字符deletestrfromstrtostr可以为空字符串,deletestr参数可以省略。

您可以看到“标点符号”:

In [10]: import string

In [11]: string.punctuation
Out[11]: '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'  

例如:

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

  • 字符串翻译

  • 字符串

  • Python 字符串 maketrans() 方法

解决方案 14:

您不需要使用 re 模块函数 re.split,而是可以使用 pandas 的 series.str.split 方法来实现相同的结果。

首先,用上述字符串创建一个系列,然后将该方法应用于该系列。

`thestring = pd.Series("Hey, you - what are you doing here!?")
thestring.str.split(pat = ',|-')`

参数pat接受分隔符并以数组形式返回分割后的字符串。这里使用 |(或运算符)传递两个分隔符。输出如下:

[Hey, you , what are you doing here!?]

解决方案 15:

我正在重新熟悉 Python,并且需要同样的东西。 findall 解决方案可能更好,但我想到了这个:

tokens = [x.strip() for x in data.split(',')]

解决方案 16:

使用 maketrans 和 Translation,你可以轻松而整洁地完成它

import string
specials = ',.!?:;"()<>[]#$=-/'
trans = string.maketrans(specials, ' '*len(specials))
body = body.translate(trans)
words = body.strip().split()

解决方案 17:

实现此目的的另一种方法是使用自然语言工具包(nltk)。

import nltk
data= "Hey, you - what are you doing here!?"
word_tokens = nltk.tokenize.regexp_tokenize(data, r'w+')
print word_tokens

这将打印:['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']

这种方法最大的缺点是需要安装nltk包。

好处是,一旦你获得了令牌,你就可以使用 nltk 包的其余部分做很多有趣的事情。

解决方案 18:

首先,我不认为您的意图是真正使用标点符号作为拆分函数中的分隔符。您的描述表明您只是想从结果字符串中删除标点符号。

我经常遇到这种情况,而我的通常解决方案不需要重新。

具有列表理解的单行 lambda 函数:

(需要import string):

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

功能(传统)

作为一个传统的函数,这仍然只有两行带有列表推导的代码(除了import string):

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

它还会自然地保留缩写和带连字符的单词。text.replace("-", " ")在拆分之前,您始终可以使用将连字符转换为空格。

不带 Lambda 或列表推导的通用函数

对于更通用的解决方案(您可以在其中指定要消除的字符),并且不需要列表理解,您会得到:

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 函数推广到任何指定的字符串。

解决方案 19:

遇到了与@ooboo 相同的问题,发现这个主题@ghostdog74 启发了我,也许有人会觉得我的解决方案有用

str1='adj:sg:nom:m1.m2.m3:pos'
splitat=':.'
''.join([ s if s not in splitat else ' ' for s in str1]).split()

如果不想在空格处分割,请在空格处输入一些内容,然后使用相同的字符进行分割。

解决方案 20:

首先,在循环中执行任何 RegEx 操作之前始终使用 re.compile(),因为它比正常操作更快。

因此,对于您的问题,首先编译模式,然后对其执行操作。

import re
DATA = "Hey, you - what are you doing here!?"
reg_tok = re.compile("[w']+")
print reg_tok.findall(DATA)

解决方案 21:

以下是答案及其一些解释。

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'

或者在一行中,我们可以这样做:

(''.join([x.replace(x, ' ') if not x.isalnum() else x for x in st])).split()

# output
['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']

更新答案

解决方案 22:

创建一个函数,该函数以两个字符串(要拆分的源字符串和分隔符的拆分列表字符串)作为输入,并输出拆分词的列表:

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

解决方案 23:

我喜欢 pprzemek 的解决方案,因为它不假设分隔符是单个字符,并且它不会尝试利用正则表达式(如果分隔符的数量非常长,则效果会不佳)。

为了清楚起见,下面是上述解决方案的更易读的版本:

def split_string_on_multiple_separators(input_string, separators):
    buffer = [input_string]
    for sep in separators:
        strings = buffer
        buffer = []  # reset the buffer
        for s in strings:
            buffer = buffer + s.split(sep)

    return buffer

解决方案 24:

我必须想出自己的解决方案,因为到目前为止我测试过的所有东西都在某些时候失败了。

>>> import re
>>> def split_words(text):
...     rgx = re.compile(r"((?:(?<!'|w)(?:w-?'?)+(?<!-))|(?:(?<='|w)(?:w-?'?)+(?=')))")
...     return rgx.findall(text)

它似乎运行良好,至少对于下面的例子来说。

>>> split_words("The hill-tops gleam in morning's spring.")
['The', 'hill-tops', 'gleam', 'in', "morning's", 'spring']
>>> split_words("I'd say it's James' 'time'.")
["I'd", 'say', "it's", "James'", 'time']
>>> split_words("tic-tac-toe's tic-tac-toe'll tic-tac'tic-tac we'll--if tic-tac")
["tic-tac-toe's", "tic-tac-toe'll", "tic-tac'tic-tac", "we'll", 'if', 'tic-tac']
>>> split_words("google.com email@google.com split_words")
['google', 'com', 'email', 'google', 'com', 'split_words']
>>> split_words("Kurt Friedrich Gödel (/ˈɡɜːrdəl/;[2] German: [ˈkʊɐ̯t ˈɡøːdl̩] (listen);")
['Kurt', 'Friedrich', 'Gödel', 'ˈɡɜːrdəl', '2', 'German', 'ˈkʊɐ', 't', 'ˈɡøːdl', 'listen']
>>> split_words("April 28, 1906 – January 14, 1978) was an Austro-Hungarian-born Austrian...")
['April', '28', '1906', 'January', '14', '1978', 'was', 'an', 'Austro-Hungarian-born', 'Austrian']

解决方案 25:

以下是我使用多个分隔符进行拆分的示例:

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

解决方案 26:

我认为以下是最能满足您的需求的答案:

W+可能适合这种情况,但可能不适合其他情况。

filter(None, re.compile('[ |,|-|!|?]').split( "Hey, you - what are you doing here!?")

解决方案 27:

这是我的看法....

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

解决方案 28:

replace()最喜欢这种方式。下面的过程将字符串中定义的所有分隔符更改splitlist为第一个分隔符splitlist,然后根据该分隔符拆分文本。它还考虑了是否splitlist恰好是空字符串。它返回一个单词列表,其中没有空字符串。

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]

解决方案 29:

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

用法如下:

>>> s = "Hey, you - what are you doing here!?"
>>> print get_words(s)
['hey', 'you', 'what', 'are', 'you', 'doing', 'here']

解决方案 30:

如果您想要可逆操作(保留分隔符),您可以使用此功能:

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
相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   609  
  在现代项目管理中,资源的有效利用是确保项目成功的关键因素之一。随着技术的不断进步,越来越多的工具和软件被开发出来,以帮助项目经理和团队更高效地管理资源。本文将介绍10款工具,这些工具可以帮助项目团队提升资源利用效率,从而实现项目目标。禅道项目管理软件禅道项目管理软件是一款开源的项目管理工具,广泛应用于软件开发和其他行业...
项目管理系统   3  
  在项目管理领域,软件工具的不断升级和创新是推动效率和协作的关键。2024年,众多项目管理软件将迎来一系列令人期待的升级功能,这些新特性不仅将提升团队的工作效率,还将增强用户体验和数据分析能力。本文将详细介绍10款项目管理软件的最新升级功能,帮助项目经理和团队成员更好地规划和执行项目。禅道项目管理软件禅道项目管理软件一直...
开源项目管理工具   2  
  信创国产系统的10个关键厂商及其技术生态随着全球信息技术格局的不断演变,信创(信息技术应用创新)产业作为国产化替代的重要阶段,正逐步成为推动我国信息技术自主可控、安全可靠的核心力量。信创产业不仅关乎国家信息安全,也是数字经济高质量发展的关键支撑。本文将深入探讨信创国产系统中的10个关键厂商及其技术生态,分析它们在信创浪...
项目管理流程   0  
  在探讨项目管理的广阔领域中,成功并非偶然,而是精心策划、高效执行与持续优化的结果。项目管理的成功之道,可以从明确的目标设定与规划、高效的团队协作与沟通、以及灵活的风险管理与适应变化这三个核心方面进行深入解析。每个方面都是项目成功的基石,它们相互交织,共同支撑起项目的顺利推进与最终成就。明确的目标设定与规划项目管理的首要...
建筑工程项目管理规范   0  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用