【问题标题】:String manipulation in PythonPython中的字符串操作
【发布时间】:2010-10-18 01:02:44
【问题描述】:

我正在将一些代码从另一种语言转换为 python。该代码将一个相当大的文件读入一个字符串,然后通过数组索引来操作它,例如:

str[i] = 'e'

由于字符串是不可变的,这不能直接在 python 中工作。在 python 中这样做的首选方法是什么?

我见过string.replace() 函数,但它返回的字符串副本听起来不是很理想,因为在这种情况下,字符串是一个完整的文件。

【问题讨论】:

  • 你总是替换同一列,还是搜索和替换?
  • 替换的内容取决于文件的内容

标签: python string replace


【解决方案1】:

其他人已经回答了您问题的字符串操作部分,但我认为您应该考虑解析文件并修改文本表示的数据结构而不是直接操作文本是否更好。

【讨论】:

    【解决方案2】:

    假设您没有使用 UTF-8 等可变长度文本编码,您可以使用array.array

    >>> import array
    >>> a = array.array('c', 'foo')
    >>> a[1] = 'e'
    >>> a
    array('c', 'feo')
    >>> a.tostring()
    'feo'
    

    但由于您正在处理文件的内容,mmap 应该更有效:

    >>> f = open('foo', 'r+')
    >>> import mmap
    >>> m = mmap.mmap(f.fileno(), 0)
    >>> m[:]
    'foo\n'
    >>> m[1] = 'e'
    >>> m[:]
    'feo\n'
    >>> exit()
    % cat foo
    feo
    

    这是一个快速基准测试脚本(对于非 Unix 操作系统,您需要将 dd 替换为其他内容):

    import os, time, array, mmap
    
    def modify(s):
        for i in xrange(len(s)):
            s[i] = 'q'
    
    def measure(func):
        start = time.time()
        func(open('foo', 'r+'))
        print func.func_name, time.time() - start
    
    def do_split(f):
        l = list(f.read())
        modify(l)
        return ''.join(l)
    
    def do_array(f):
        a = array.array('c', f.read())
        modify(a)
        return a.tostring()
    
    def do_mmap(f):
        m = mmap.mmap(f.fileno(), 0)
        modify(m)
    
    os.system('dd if=/dev/random of=foo bs=1m count=5')
    
    measure(do_mmap)
    measure(do_array)
    measure(do_split)
    

    我用几年前的笔记本电脑得到的输出符合我的直觉:

    5+0 records in
    5+0 records out
    5242880 bytes transferred in 0.710966 secs (7374304 bytes/sec)
    do_mmap 1.00865888596
    do_array 1.09792494774
    do_split 1.20163106918
    

    所以 mmap 会稍微快一些,但建议的解决方案都没有特别不同。如果您发现有很大的不同,请尝试使用 cProfile 来查看花费时间的原因。

    【讨论】:

    • 我似乎记得 mmap 仅适用于 linux,因此您可能会遇到可移植性问题。
    • 不,它适用于 Unix 和 Windows (docs.python.org/library/mmap.html)。有一些小的 API 差异,但没有影响这个用例。实际上在 Windows 上有一个更大的区别:do_mmap 0.65700006485; do_array 1.0150001049; do_split 0.827999830246.
    • 感谢有关 cProfile 的提示,它为我指出了问题所在。 for 循环使用 range() 导致大量开销。我切换到while循环,现在性能很好。
    • 酷!很高兴你明白了。
    【解决方案3】:

    试试:

    sl = list(s)
    sl[i] = 'e'
    s = ''.join(sl)
    

    【讨论】:

      【解决方案4】:
      l = list(str)
      l[i] = 'e'
      str = ''.join(l)
      

      【讨论】:

      • @theycallmemorty:它消耗的内存是 C 的两倍,但除此之外,我看不出它不应该工作的任何原因。
      • 事实上,如果要进行大量此类操作,最好将字符串保留为字符列表。
      • 这行得通,而且似乎比另一个答案的数组方法要快一些。但是这两种方法都比我以前的代码慢很多;目前 ~7 秒 vs 0.4 秒
      • @liw.fi:正确。完成所有基于字符的修改后,''.join(l) 行应该是用户。
      • @Zitrax:你之前的代码是什么? Python 或原始语言 (C?)。另外,请参阅我对 liw.fi 评论的回复。
      猜你喜欢
      • 2013-07-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多