python - Python中有 `string.split()`的生成器版本吗?

标签 python string generator

string.split()返回一个 list 实例。是否有返回 generator 的版本?反而?有没有理由反对使用生成器版本?

最佳答案

很有可能re.finditer使用相当少的内存开销。

def split_iter(string):
    return (x.group(0) for x in re.finditer(r"[A-Za-z']+", string))

演示:

>>> list( split_iter("A programmer's RegEx test.") )
['A', "programmer's", 'RegEx', 'test']

编辑:我刚刚确认这在 python 3.2.1 中占用了常量内存,假设我的测试方法是正确的。我创建了一个非常大的字符串(1GB 左右),然后使用 for 循环遍历可迭代对象(不是列表理解,它会产生额外的内存)。这并没有导致显着的内存增长(也就是说,如果内存有增长,则远远小于 1GB 字符串)。

更通用的版本:

在回复评论“我看不到与 str.split 的连接”时,这里有一个更通用的版本:

def splitStr(string, sep="\s+"):
    # warning: does not yet work if sep is a lookahead like `(?=b)`
    if sep=='':
        return (c for c in string)
    else:
        return (_.group(1) for _ in re.finditer(f'(?:^|{sep})((?:(?!{sep}).)*)', string))
    # alternatively, more verbosely:
    regex = f'(?:^|{sep})((?:(?!{sep}).)*)'
    for match in re.finditer(regex, string):
        fragment = match.group(1)
        yield fragment

这个想法是 ((?!pat).)* '否定'一个组,确保它贪婪地匹配直到模式开始匹配(前瞻不消耗正则表达式中的字符串有限状态机)。在伪代码中:尽可能地重复使用 (begin-of-string xor {sep}) + ,直到我们能够再次开始(或命中结束)字符串)

演示:

>>> splitStr('.......A...b...c....', sep='...')
<generator object splitStr.<locals>.<genexpr> at 0x7fe8530fb5e8>

>>> list(splitStr('A,b,c.', sep=','))
['A', 'b', 'c.']

>>> list(splitStr(',,A,b,c.,', sep=','))
['', '', 'A', 'b', 'c.', '']

>>> list(splitStr('.......A...b...c....', '\.\.\.'))
['', '', '.A', 'b', 'c', '.']

>>> list(splitStr('   A  b  c. '))
['', 'A', 'b', 'c.', '']

(应该注意 str.split 有一个丑陋的行为:它在特殊情况下将 sep=None 作为第一个执行 str.strip 以删除前导和尾随空格. 上面故意不这样做;请参阅最后一个示例,其中 sep="\s+".)

(在尝试实现此功能时,我遇到了各种错误(包括内部 re.error)... 负后视会将您限制为固定长度的定界符,因此我们不使用它。除了上述正则表达式之外,几乎所有东西似乎导致字符串开头和字符串结尾边缘情况的错误(例如 r'(.*?)($|,)' on ',,, a,,b,c' 返回 ['', '', '', 'a', '', 'b', 'c', '']最后的空字符串;人们可以查看另一个看似正确但实际上有细微错误的正则表达式的编辑历史。)

(如果您想自己实现它以获得更高的性能(尽管它们是重量级的,正则表达式最重要的是在 C 中运行),您会编写一些代码(使用 ctypes?不确定如何让生成器使用它?),使用以下固定长度定界符的伪代码:散列长度为 L 的定界符。在使用运行散列算法扫描字符串时,保持长度为 L 的运行散列,O(1) 更新时间。每当散列可能等于你的定界符时,手动检查过去的几个字符是否是分隔符;如果是,则从上次 yield 开始产生子字符串。字符串开头和结尾的特殊情况。这将是教科书算法的生成器版本,用于执行 O(N) 文本搜索。多处理版本也是可能的。它们可能看起来有点矫枉过正,但问题意味着一个人正在处理非常大的字符串......此时你可能会考虑一些疯狂的事情,比如缓存字节偏移量(如果它们很少的话),或者使用一些磁盘从磁盘工作-支持的字节串 View 对象,bu需要更多的内存等)

关于python - Python中有 `string.split()`的生成器版本吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3862010/

相关文章:

c++ - 如何将字符串 vector 传递给 execv

python - 如何从python中的协程获取返回值

c++ - 用 C++ 实现异步惰性生成器

python - 如何使用生成器避免 python 中的最大递归深度?

python - Pandas 组合两列

python - 将单词从行条目提取到 pandas DataFrame 中的列

c - 函数中的操作数组 - c - 段错误

python - 如何在Python中转换为UTC后完全删除tzinfo?

python - TensorFlow 数据验证和 BigQuery

c - 为什么 scanf ("%s") 与 char* 行为异常?