【问题标题】:'in-place' string modifications in PythonPython中的“就地”字符串修改
【发布时间】:2010-08-11 23:52:57
【问题描述】:

在 Python 中,字符串是不可变的。

逐个字符遍历字符串并对其进行修改的标准习语是什么?

我能想到的唯一方法是一些与加​​入结果字符串相关的真正臭名昭著的黑客攻击。

--

在 C 中:

for(int i = 0; i < strlen(s); i++)
{
   s[i] = F(s[i]);
}

这是超级表达力,并准确地说明了我在做什么。这就是我要找的。

【问题讨论】:

  • 我猜因为它们是不可变的,所以你不能“修改它”......你的意思是逐个字符地构造一个新字符串吗?
  • 这真的很少见。你能给出这样做的背景或用例吗?
  • 在消息中添加噪音以进行测试。
  • @Paul Nathan:“给消息添加噪音”?就像“随机替换字符”一样? replace 这个有什么问题?
  • @S.Lott:替换选择列表中的已知元素“mystring.replace("1", "i")`;而我想按索引进行迭代 - 我不知道列表中会包含哪些元素。 (为什么一定要这么痛苦?)

标签: python string


【解决方案1】:

不要使用字符串,使用可变的东西,比如 bytearray:

#!/usr/bin/python

s = bytearray("my dog has fleas")
for n in xrange(len(s)):
    s[n] = chr(s[n]).upper()
print s

结果:

MY DOG HAS FLEAS

编辑:

由于这是bytearray,因此您不必(必然)使用字符。您正在使用 bytes。所以这也有效:

s = bytearray("\x81\x82\x83")
for n in xrange(len(s)):
    s[n] = s[n] + 1
print repr(s)

给予:

bytearray(b'\x82\x83\x84')

如果您想修改 Unicode 字符串中的字符,您可能希望使用 memoryview,尽管它不直接支持 Unicode。

【讨论】:

  • 它们不是字符,但是,只是字节。仅适用于 ASCII,不适用于 Unicode。
  • 适用于有效的字节,而不仅仅是ASCII。
【解决方案2】:

C 的 Python 模拟:

for(int i = 0; i < strlen(s); i++)
{
   s[i] = F(s[i]);
}

应该是:

s = "".join(F(c) for c in s)

这也很有表现力。它准确地说明了正在发生的事情,但以功能风格而不是程序风格。

【讨论】:

  • 有趣;我有点喜欢它,除了空字符串。可能和我得到的一样好。
  • @Joe Koberg:Python 的一个重要部分是它更短更清晰。 “就地”是有限制的,因为你不能让字符串更长。
  • 我明白这一点,但 OP 似乎专注于它。并拒绝了许多标准的python字符串转换方法。唯一剩下的就是它没有在原地工作。
  • map() 和列表理解和加入不一样吗?
  • @JavierBadia: map() 通常性能也较差,除非您使用内置函数。
【解决方案3】:

您可以使用 UserString 模块:

 >>> import UserString
... s = UserString.MutableString('Python')
... print s
Python
>>> s[0] = 'c'
>>> print s
cython

【讨论】:

【解决方案4】:

我想说最 Pythonic 的方式是使用 map():

s = map(func, s) # func has been applied to every character in s

这相当于写作:

s = "".join(func(c) for c in s)

【讨论】:

  • 小修正/点:仍然需要重新加入返回的列表(Py3+ 中的 /iter/map-obj)。所以上面应该是:s = "".join(map(func, s))
  • 地图可能是最不Python的方式。
【解决方案5】:

问题首先说明字符串是不可变的,然后要求找到一种方法来更改它们。这有点矛盾。无论如何,当您搜索“python 字符串就地修改”时,这个问题会在列表顶部弹出,我正在添加真正的就地更改的答案。

当您查看字符串类的方法时,字符串似乎是不可变的。但是没有一种具有 C 接口的语言可以真正提供不可变的数据类型。唯一的问题是您是否必须编写 C 代码才能实现所需的修改。

这里 python ctypes 是你的朋友。由于它支持获取指针并包含类似 C 的内存复制函数,因此可以像这样修改 python 字符串

s = 16 * "."
print s
ctypes.memmove(ctypes.c_char_p(s), "Replacement", 11)
print s

结果:

................
Replacement.....

(当然,您可以在运行时通过将函数F 应用于原始字符串的每个字符来计算替换字符串。之前的答案中已经显示了不同的方法。)

请注意,我绝不鼓励这样做。但是,我必须为从 C++ 映射到 python 并包含一个方法的类编写一个替换:

int readData(char* data, int length)

(调用者应该为内存提供length 字节,然后该方法将可用数据(最多length)写入该内存,返回写入的字节数。)虽然这是一个完美的C/C++ 中的合理 API,它不应该作为 python 类的方法提供,或者至少应该让 API 的用户知道他们只能将可变字节数组作为参数传递。

如您所料,该方法的“常用用法”如我的示例所示(创建一个字符串并将其连同其长度作为参数一起传递)。由于我真的不想编写 C/C++ 扩展,所以我必须想出一个解决方案,以便仅使用 python 在我的替换类中实现行为。

【讨论】:

    【解决方案6】:

    string.translate 可能是最接近您所追求的功能。

    【讨论】:

    • 有趣的函数——然而,我正在寻找的是能够在给定索引处进行调整(或返回一个修改了给定索引的字符串)。
    • 不需要从字符串导入,str 对象有translate 方法。虽然使用 string 模块中的 maketrans 通常很方便
    【解决方案7】:

    字符串是可迭代的,可以像列表一样遍历。字符串还有许多基本方法,例如 .replace(),这可能是您正在寻找的。所有字符串方法都返回一个新字符串。因此,您可以简单地替换其现有值,而不是修改字符串。

    >>> mystring = 'robot drama'
    >>> mystring = mystring.replace('r', 'g')
    >>> mystring
    'gobot dgama'
    

    【讨论】:

    • 字符串是不可变的,不能由成员分配。
    • 正确。所有字符串操作都返回输入字符串的副本。但是,变量名称不是不可变的,因此通过将字符串操作重新分配给相同的变量名称,您实际上是在“改变”字符串。
    • "在不增加或减少长度的情况下,如果可以使用切片符号雾就地调整,它仍然很有用:myStr[0:3][:] = 'new'" .replace("g","r")
    【解决方案8】:

    将特定字符分配给字符串中的特定索引并不是特别常见的操作,因此如果您发现自己需要这样做,请考虑是否有更好的方法来完成任务。但是,如果您确实需要,可能最标准的方法是将字符串转换为列表,进行修改,然后将其转换回字符串。

    s = 'abcdefgh'
    l = list(s)
    l[3] = 'r'
    s2 = ''.join(l)
    

    编辑:正如 bstpierre 的回答中所发布的,bytearray 可能比 list 更适合此任务,只要您不使用 Unicode 字符串。

    s = 'abcdefgh'
    b = bytearray(s)
    b[3] = 'r'
    s2 = str(b)
    

    【讨论】:

    • 如果您使用的是多字节字符串,最好小心不要砍掉编码字符!
    【解决方案9】:
    >>> mystring = "Th1s 1s my str1ng"
    >>> mystring.replace("1", "i")
    'This is my string'
    

    如果你想存储这个 new 字符串,你必须mystring = mystring.replace("1", "i")。这是因为在 Python 中字符串是不可变的。

    【讨论】:

    • 1:不要使用“字符串”作为变量名。 2:这不会修改变量。
    • 那不是按索引选择和修改它。
    • @Paul Nathan:“按索引”的要求在哪里?
    【解决方案10】:

    如果我需要做类似的事情,我只需将其转换为可变列表

    例如...(虽然使用排序会更容易(参见第二个示例))

    >>> s = "abcdfe"
    >>> s = list(s)
    >>> s[4] = "e"
    >>> s[5] = "f"
    >>> s = ''.join(s)
    >>> print s
    abcdef
    >>>
    # second example
    >>> s.sort()
    >>> s = ''.join(s)
    

    【讨论】:

      【解决方案11】:

      这是一个使用 translate 将“-”切换为“.”的示例。和大写的“a”s

      >>> from string import maketrans
      >>> trans_table = maketrans(".-a","-.A")
      >>> "foo-bar.".translate(trans_table)
      'foo.bAr-'
      

      如果您只需要进行单个字符替换,这比翻转到字节数组并返回要高效得多

      【讨论】:

        【解决方案12】:
        def modifyIdx(s, idx, newchar):
            return s[:idx] + newchar + s[idx+1:]
        

        【讨论】:

          【解决方案13】:

          您可以使用StringIO 类来接收字符串的类似文件的可变接口。

          【讨论】:

          • 链接已损坏
          【解决方案14】:

          我是这样做的:

          import tempfile
          import shutil
          
          ...
          
          f_old = open(input_file, 'r')
          with tempfile.NamedTemporaryFile() as tmp:
              for line in f_old:
                  tmp.write(line.replace(old_string, new_string))
              f_old.close()
              tmp.flush()
              os.fsync(tmp)
              shutil.copy2(tmp.name, input_file)
              tmp.close()
          

          【讨论】:

            【解决方案15】:

            这是我的 就地 字符串反转的 Pythonic 解决方案。

            也考虑空格。

            注意:除了下划线('_')之外,如果包含在 input_string 中,它不会匹配任何特殊字符

            i/p - “Hello World” => o/p - “olleH dlroW”

            import re
            
            def inplace_reversal(input_string):
                
                list_of_strings = re.findall(r'\s|(\w+)',input_string)
                
                output_string= ''
                
                for string in list_of_strings:
                    
                    if string == '':
                        
                        output_string += ' '
                    
                    else:
                        
                        output_string += string[::-1]
                
                return output_string
            
            print(inplace_reversal('__Hello__ __World__         __Hello__       __World__ '))
            
            >>> __olleH__ __dlroW__         __olleH__       __dlroW__ 
            

            【讨论】:

              猜你喜欢
              • 2012-11-11
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2019-02-20
              • 2022-07-29
              • 2015-01-27
              • 1970-01-01
              相关资源
              最近更新 更多