从字符串列表中删除空字符串

Posted

技术标签:

【中文标题】从字符串列表中删除空字符串【英文标题】:Remove empty strings from a list of strings 【发布时间】:2011-04-20 05:07:45 【问题描述】:

我想从 python 中的字符串列表中删除所有空字符串。

我的想法是这样的:

while '' in str_list:
    str_list.remove('')

有没有更多的pythonic方法来做到这一点?

【问题讨论】:

@Ivo,这些说法都不是真的。您永远不应该使用for x in list 修改您迭代的列表如果您使用的是while loop,那很好。演示的循环将删除空字符串,直到没有更多的空字符串然后停止。实际上我什至没有看过这个问题(只是标题),但我回答的可能是完全相同的循环!如果您不想为了记忆而使用推导式或过滤器,这是一个非常 Pythonic 的解决方案。 永远不要更改您正在迭代的列表仍然是一个非常有效的观点:) @EduardLuca 如果迭代列表的目的是更改它,那么这与您应该做的相反。您只需要小心,您知道这样做不会导致意外行为。 @EduardLuca, @JFA :关键是他没有遍历任何列表。如果他以for var in list: 的形式写了一些东西,他会这样做,但在这里,他写的是while const in list:。这不是迭代任何东西。它只是重复相同的代码,直到条件为假。 您可以使用过滤器删除空字符串。代码应该看起来像这样...data = list(filter(None, str_list)) 【参考方案1】:

我会使用filter:

str_list = filter(None, str_list)
str_list = filter(bool, str_list)
str_list = filter(len, str_list)
str_list = filter(lambda item: item, str_list)

Python 3 从filter 返回一个迭代器,因此应该包含在对list() 的调用中

str_list = list(filter(None, str_list))

【讨论】:

如果您那么追求性能,itertool's ifilter 会更快—>>> timeit('filter(None, str_list)', 'str_list=["a"]*1000', number=100000) 2.3468542098999023; >>> timeit('itertools.ifilter(None, str_list)', 'str_list=["a"]*1000', number=100000)0.04442191123962402. @cpburnz 非常正确。然而,ifilter 的结果是懒惰地评估的,而不是一次性的——我认为大多数情况下ifilter 更好。有趣的是,使用filter 仍然比将ifilter 包装在list 中要快。 如果您对数字列表执行此操作,请注意零也会被删除(注意:我只使用了前 3 种方法),因此您需要另一种方法。 这仅关注速度,而不是解决方案的pythonic(被问到的问题)。 List Comprehensions 是 Pythonic 解决方案,只有在分析证明 listcomp 是瓶颈时才应使用过滤器。 @whoever-mentions-about-or-imply-Python-3,请编辑和更新答案。当被问到这个问题时,我们只是在讨论 Python 2,即使 Python 3 发布了将近 2 年。但请更新 Python 2 和 3 结果。【参考方案2】:

使用list comprehension 是最 Pythonic 的方式:

>>> strings = ["first", "", "second"]
>>> [x for x in strings if x]
['first', 'second']

如果必须就地修改列表,因为有其他引用必须看到更新的数据,则使用切片分配:

strings[:] = [x for x in strings if x]

【讨论】:

我喜欢这个解决方案,因为它很容易适应。如果我不仅需要删除空字符串,还需要删除只是空格的字符串,例如:[x for x in strings if x.strip()]. [x for x in strings if x] 这工作正常,但请解释一下这个循环是如何工作的?? @AmarKumar 在 Python 中,当在布尔上下文中宣布时,空白字符串的计算结果为 false,例如在 if x 中。括号、for 循环和if 子句组合起来读取“如果x 实际上包含某些内容,则为strings 中的每个元素生成一个由x 组成的列表。” @ Ib33x 绝对很棒的工作。这个答案肯定是最 Pythonic 的。【参考方案3】:

filter 实际上有一个特殊的选项:

filter(None, sequence)

它将过滤掉所有评估为 False 的元素。此处无需使用实际的可调用对象,例如 bool、len 等。

它和 map(bool, ...) 一样快

【讨论】:

这其实是一个python成语。这也是我唯一一次仍然使用 filter(),列表推导已经接管了其他任何地方。 与列表比较相比,我发现这更容易看出代码的意图【参考方案4】:
>>> lstr = ['hello', '', ' ', 'world', ' ']
>>> lstr
['hello', '', ' ', 'world', ' ']

>>> ' '.join(lstr).split()
['hello', 'world']

>>> filter(None, lstr)
['hello', ' ', 'world', ' ']

比较时间

>>> from timeit import timeit
>>> timeit('" ".join(lstr).split()', "lstr=['hello', '', ' ', 'world', ' ']", number=10000000)
4.226747989654541
>>> timeit('filter(None, lstr)', "lstr=['hello', '', ' ', 'world', ' ']", number=10000000)
3.0278358459472656

注意filter(None, lstr) 不会删除带有空格' ' 的空字符串,它只会删除''' '.join(lstr).split() 会删除两者。

要使用 filter() 并删除空格字符串,需要更多时间:

>>> timeit('filter(None, [l.replace(" ", "") for l in lstr])', "lstr=['hello', '', ' ', 'world', ' ']", number=10000000)
18.101892948150635

【讨论】:

如果单词的字符串中有空格,它将不起作用。例如: ['hello world', ' ', 'hello', ' '] 。 >> ['helloworld', ' ', 'hello', ' '] 你有没有其他解决方案可以在列表中的项目中保留空格但删除其他项目? 注意filter(None, lstr) 不会删除带有空格' ' 的空字符串 是的,因为那不是空字符串。 救星? !!【参考方案5】:

@Ib33X 的回复很棒。如果要删除每个空字符串,请在剥离后。您也需要使用剥离方法。否则,如果它有空格,它也会返回空字符串。就像,“”对于那个答案也是有效的。所以,可以通过。

strings = ["first", "", "second ", " "]
[x.strip() for x in strings if x.strip()]

答案是["first", "second"]。 如果你想改用filter 方法,你可以这样做 list(filter(lambda item: item.strip(), strings))。这给出了相同的结果。

【讨论】:

【参考方案6】:

我会使用 if X != '' 而不是 if x,以便仅消除空字符串。像这样:

str_list = [x for x in str_list if x != '']

这将在您的列表中保留 None 数据类型。此外,如果您的列表中有整数并且 0 是其中之一,它也会被保留。

例如,

str_list = [None, '', 0, "Hi", '', "Hello"]
[x for x in str_list if x != '']
[None, 0, "Hi", "Hello"]

【讨论】:

如果您的列表有不同的类型(None 除外),您可能会遇到更大的问题。 什么类型?我尝试使用 int 和其他数字类型、字符串、列表、元组、集合和 None 并且没有问题。我可以看到,如果有任何不支持 str 方法的用户定义类型可能会出现问题。我应该担心其他任何事情吗? 如果您有str_list = [None, '', 0, "Hi", '', "Hello"],则表明应用程序设计不佳。您不应该在同一个列表中拥有多个接口(类型)和 None。 从数据库中检索数据?进行自动化测试时函数的参数列表? 这些通常是元组。【参考方案7】:

总结最佳答案:

1。在不剥离的情况下消除 emtpties:

即保留全空格字符串:

slist = list(filter(None, slist))

专业人士:

最简单的; 最快(请参阅下面的基准)。

2。为了消除剥离后的空...

2.a ...当字符串在单词之间不包含空格时:

slist = ' '.join(slist).split()

专业人士:

小代码 快 (但由于内存的原因,大数据集的速度不是最快的,这与 @paolo-melchiorre 的结果相反)

2.b ...当字符串在单词之间包含空格时?

slist = list(filter(str.strip, slist))

专业人士:

最快; 代码的可理解性。

2018 机器上的基准测试:

## Build test-data
#
import random, string
nwords = 10000
maxlen = 30
null_ratio = 0.1
rnd = random.Random(0)                  # deterministic results
words = [' ' * rnd.randint(0, maxlen)
         if rnd.random() > (1 - null_ratio)
         else
         ''.join(random.choices(string.ascii_letters, k=rnd.randint(0, maxlen)))
         for _i in range(nwords)
        ]

## Test functions
#
def nostrip_filter(slist):
    return list(filter(None, slist))

def nostrip_comprehension(slist):
    return [s for s in slist if s]

def strip_filter(slist):
    return list(filter(str.strip, slist))

def strip_filter_map(slist): 
    return list(filter(None, map(str.strip, slist))) 

def strip_filter_comprehension(slist):  # waste memory
    return list(filter(None, [s.strip() for s in slist]))

def strip_filter_generator(slist):
    return list(filter(None, (s.strip() for s in slist)))

def strip_join_split(slist):  # words without(!) spaces
    return ' '.join(slist).split()

## Benchmarks
#
%timeit nostrip_filter(words)
142 µs ± 16.8 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

%timeit nostrip_comprehension(words)
263 µs ± 19.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit strip_filter(words)
653 µs ± 37.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit strip_filter_map(words)
642 µs ± 36 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit strip_filter_comprehension(words)
693 µs ± 42.2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit strip_filter_generator(words)
750 µs ± 28.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit strip_join_split(words)
796 µs ± 103 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

【讨论】:

s and s.strip() 可以简化为 s.strip() s and s.strip() 如果我们想完全复制filter(None, words) 是必需的,这是公认的答案。我更正了上面的 x2 个示例函数并删除了 x2 个坏的。【参考方案8】:

根据列表的大小,使用 list.remove() 而不是创建新列表可能最有效:

l = ["1", "", "3", ""]

while True:
  try:
    l.remove("")
  except ValueError:
    break

这样做的好处是不创建新列表,但缺点是每次都必须从头开始搜索,虽然不像上面建议的使用while '' in l,它只需要在每次出现''时搜索一次(有无疑是一种保持两种方法最好的方法,但它更复杂)。

【讨论】:

您可以通过ary[:] = [e for e in ary if e] 编辑列表。更简洁,不使用控制流异常。 嗯,这不是真的“到位” - 我很确定这会创建一个新列表并将其分配给旧列表。 这表现很差,因为数据的尾部在每次删除时都会在内存中打乱。最好一次性删除所有内容。【参考方案9】:

正如Aziz Alto 所报告的,filter(None, lstr) 不会删除带有空格的空字符串 ' ' 但如果您确定 lstr 仅包含字符串,则可以使用 filter(str.strip, lstr)

>>> lstr = ['hello', '', ' ', 'world', ' ']
>>> lstr
['hello', '', ' ', 'world', ' ']
>>> ' '.join(lstr).split()
['hello', 'world']
>>> filter(str.strip, lstr)
['hello', 'world']

在我的电脑上比较时间

>>> from timeit import timeit
>>> timeit('" ".join(lstr).split()', "lstr=['hello', '', ' ', 'world', ' ']", number=10000000)
3.356455087661743
>>> timeit('filter(str.strip, lstr)', "lstr=['hello', '', ' ', 'world', ' ']", number=10000000)
5.276503801345825

删除'' 和带有空格' ' 的空字符串的最快解决方案仍然是' '.join(lstr).split()

如评论中所述,如果您的字符串包含空格,情况会有所不同。

>>> lstr = ['hello', '', ' ', 'world', '    ', 'see you']
>>> lstr
['hello', '', ' ', 'world', '    ', 'see you']
>>> ' '.join(lstr).split()
['hello', 'world', 'see', 'you']
>>> filter(str.strip, lstr)
['hello', 'world', 'see you']

您可以看到filter(str.strip, lstr) 保留带有空格的字符串,但' '.join(lstr).split() 会拆分这些字符串。

【讨论】:

这仅在您的字符串不包含空格时才有效。否则,您也会拆分这些字符串。 @BenPolinsky 正如您所报告的join 解决方案将使用空格分割字符串,但过滤器不会。感谢您的评论我改进了我的答案。【参考方案10】:

请记住,如果您想在字符串中保留空格,您可能会使用某些方法无意中删除它们。 如果你有这份清单

['你好世界','','','你好'] 你可能想要什么 ['hello world','hello']

首先修剪列表以将任何类型的空白转换为空字符串:

space_to_empty = [x.strip() for x in _text_list]

然后从它们列表中删除空字符串

space_clean_list = [x for x in space_to_empty if x]

【讨论】:

如果你想保留字符串中的空格,你可能会使用一些方法无意中将它们删除。喜欢这种方法,然后呢? 谢谢老兄,它对我有用,只需稍作改动。即space_clean_list = [x.strip() for x in y if x.strip()]【参考方案11】:

使用filter:

newlist=filter(lambda x: len(x)>0, oldlist) 

正如所指出的,使用过滤器的缺点是它比替代品慢;此外,lambda 通常成本很高。

或者你可以选择最简单和最迭代的:

# I am assuming listtext is the original list containing (possibly) empty items
for item in listtext:
    if item:
        newlist.append(str(item))
# You can remove str() based on the content of your original list

这是最直观的方法,而且可以在适当的时候完成。

【讨论】:

欢迎来到 SO。你没有被忽视。您没有受到任何反对投票者的攻击。您已收到反馈。放大:您为过滤器提出的第一个参数比lambda x: len(x) 差,后者比lambda x : x 差,这是所选答案中4 个解决方案中最差的。正确的功能是首选,但还不够。将光标悬停在否决按钮上:它显示“这个答案没有用”。【参考方案12】:

你可以使用这样的东西

test_list = [i for i in test_list if i]

其中 test_list 是您要从中删除空元素的列表。

【讨论】:

【参考方案13】:

使用正则表达式和过滤器匹配

lstr = ['hello', '', ' ', 'world', ' ']
r=re.compile('^[A-Za-z0-9]+')
results=list(filter(r.match,lstr))
print(results)

【讨论】:

以上是关于从字符串列表中删除空字符串的主要内容,如果未能解决你的问题,请参考以下文章

从列表中删除空字符串(Spark Dataframe)[重复]

如何删除python列表列表中的''(空字符串)?

从 spark RDD 中删除空字符串

从数组中删除空字符串或空白字符串 - Javascript

从数组中删除空字符串,同时保持记录没有循环?

concat_ws 从 spark 数据帧的输出中删除空字符串