【问题标题】:What is an efficient way to erase substrings?什么是擦除子字符串的有效方法?
【发布时间】:2011-12-29 07:07:58
【问题描述】:

我有一个长字符串和一个[end-index, string] 列表,如下所示:

long_sentence = "This is a long long long long sentence"
indices = [[6, "is"], [8, "is a"], [18, "long"], [23, "long"]]

元素6, "is" 表示6 是字符串中单词"is" 的结束索引。我想最后得到以下字符串:

>> print long_sentence
This .... long ......... long sentence"

我试过这样的方法:

temp = long_sentence
for i in indices:
    temp = temp[:int(i[0]) - len(i[1])] + '.'*(len(i[1])+1) + temp[i[0]+1:]

虽然这似乎有效,但它需要的时间非常长(300 MB 文件中的 5000 个字符串超过 6 小时)。有没有办法加快速度?

【问题讨论】:

  • 旁注:您不需要int() 部分,因为i[0] 是一个整数。

标签: python string text


【解决方案1】:

每次执行temp = temp... 赋值时,Python 都必须创建一个新字符串(因为 Python 字符串是不可变的)。

您可能想要做的是将字符串转换为字符列表,然后对字符列表进行操作,然后再次将该列表重新组合成字符串。

long_list = list(long_sentence)
for end, repstr in indices:
    long_list[end-len(repstr):end] = ['.'] * len(repstr)
new_sentence = ''.join(long_list)

【讨论】:

  • 正要发布这个。你需要一个 StringBuilder 对象的 Python 等价物。
  • @user978122 即''.join()
  • @user978122:StringBuilder 对象有什么作用?
  • @EOL - 这是一个用于累积字符串片段然后生成最终字符串的类,而不会在此过程中创建更小的不完整字符串。 (''.join() 也是这样做的。)
  • 是的,如果您尝试创建问题中指定的约 300,000,000 个元素列表,我想我的盒子会窒息……答案:我的系统上有 2.3GB;我相信现在每个人都有 64 位操作系统?我仍然持怀疑态度;)
【解决方案2】:

我通常会首先专注于编写最干净、可读的简洁代码,然后再进行优化;这正是你采取的方法,太棒了! 6 小时似乎站不住脚,是时候优化了。您已经清楚地将创建替换字符串的时间与首先生成索引列表的时间分开了吗?

Benchmarking 显示 list comprehensionsjoinsfake files 对于字符串连接来说是最快的。那是一篇相当老的文章 - 您可能想自己运行基准测试以确认结果 - 尽管它可能仍然有效。

【讨论】:

    【解决方案3】:

    您可以通过使用集合进行成员资格测试和 str.join 组合结果来避免 O(n) 行为:

    >>> redacts = set()
    >>> indices = [[6, "is"], [8, "is a"], [18, "long"], [23, "long"]]
    >>> for end, substr in indices:
            redacts.update(range(end-len(substr)+1, end+1))
    >>> ''.join([('.' if i in redacts else c) for i, c in enumerate(long_sentence)])
    'This .... long .... .... long sentence'
    

    或者,您可以使用 bytearray 让您就地改变“字符串”:

    >>> arr = bytearray(long_sentence)
    >>> for end, substr in indices:
            arr[end-len(substr)+1: end+1] = '.' * len(substr)
    >>> str(arr)
    'This .... long .... .... long sentence'
    

    后一种技术仅适用于非 unicode 字符串。

    【讨论】:

    • 这个答案很有趣,因为它很有创意。 :) 虽然它看起来很快,但我很好奇它与我的回答中的 array 方法相比有多快。
    【解决方案4】:

    您可以使用 可变标准 array 类型进行字符替换:

    >>> import array
    
    >>> long_sentence = "This is a long long long long sentence"
    >>> indices = [[6, "is"], [8, "is a"], [18, "long"], [23, "long"]]
    
    >>> temp = array.array('c', long_sentence)  # Could replace long_sentence too
    >>> for end, substr in indices:
    ...     temp[end-len(substr)+1:end+1] = array.array('c', '.'*len(substr))
    ...     
    >>> temp
    array('c', 'This .... long .... .... long sentence')
    

    可以将新字符串写入输出文件:

    temp.tofile(your_file)
    

    (字符串本身由temp.tostring()返回。)

    这种方法的优点是通过切片防止创建太多新字符串,这需要时间。另一个优点是它节省内存:字符串更新在原地完成(这由在temp.buffer_info() 中找到的地址显示,该地址保持不变)。一个副作用是,这种内存效率可能会让您的计算机避免交换,从而进一步加快速度。

    您还可以通过使用自定义__getitem__ 方法的特殊类DotString缓存 '.'*len(substr) 字符串来加快速度,其中DotString[4] 返回'....' 等。

    PS:大多数优化尝试首先受益于分析。您可以通过以下方式分析您的程序:

    python -m cProfile -o stats.prof <Python program name and arguments>
    

    然后您可以使用以下方法分析时间安排:

    python -m pstats stats.prof
    

    您通常会运行的第一个命令是 sort time(按严格在函数代码内花费的时间对函数进行排序),然后是 stats 10(前 10 个最长的函数执行)。

    您可以在输入文件的截断版本上执行此操作,这样运行时间不会太长。这将告诉您哪些功能花费的时间最多,应该是优化的重点。

    PPS:上面示例中使用的'c' 类型用于字节字符串(通常是ASCII 编码)。字符串(又名 unicode 字符串)可以用'u' 处理。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-11-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-21
      • 2011-07-25
      • 2010-11-21
      • 1970-01-01
      相关资源
      最近更新 更多