Изменение одного символа в строке в Python

385

Какой самый простой способ в Python заменить символ в строке?

Например:

text = "abcdefg";
text[1] = "Z";
           ^
Kostia
источник

Ответы:

535

Не изменяйте строки.

Работать с ними как списками; превращайте их в струны только при необходимости.

>>> s = list("Hello zorld")
>>> s
['H', 'e', 'l', 'l', 'o', ' ', 'z', 'o', 'r', 'l', 'd']
>>> s[6] = 'W'
>>> s
['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd']
>>> "".join(s)
'Hello World'

Строки Python являются неизменяемыми (то есть они не могут быть изменены). Есть много причин для этого. Используйте списки, пока у вас нет выбора, только затем превратите их в строки.

scvalex
источник
4
Те, кто ищет скорость / эффективность, читают это
AneesAhmed777
4
«Не изменяйте строки». почему
hacksoi
2
«Создать-> изменить-> сериализовать-> назначить-> освободить» более эффективно, чем s [6] = 'W'? Хм ... Почему другие языки позволяют это, несмотря на это "много" причин? Интересно, как можно защитить странный дизайн (полагаю, ради любви). Почему бы не предложить добавить в ядро ​​Python функцию MID (strVar, index, newChar), которая напрямую обращается к позиции памяти, вместо ненужного перемешивания байтов со всей строкой?
Оскар
@hacksoi, @oscar, причина довольно проста: нет необходимости пересчитывать при передаче указателей для реализации копирования при модификации или прямого копирования всей строки в случае, если кто-то захочет изменить эту строку - это приводит к увеличению скорости в общем использовать. Нет необходимости в таких вещах, как MIDиз-за кусочков:s[:index] + c + s[index+1:]
MultiSkill
1
@oscar Под тупыми языками я подразумеваю, что они не имеют дело с юникодом, если вы явно не скажете им. Конечно, вы можете писать приложения, поддерживающие юникод, на C. Но вы должны постоянно о них заботиться и должны явно тестировать, чтобы избежать проблем. Все ориентировано на машины. Я работал с PHP до изучения Python, и этот язык - полный беспорядок. Что касается вашей заметки о быстрых процессорах, я полностью с вами. Но частью этой проблемы является популярное несогласие с преждевременной оптимизацией, что приводит к медленным интерпретаторам и библиотекам из-за утечки большого количества циклов ЦП.
Бахсау
202

Самый быстрый способ?

Есть три способа. Для ищущих скорость я рекомендую «Метод 2»

Способ 1

Данный ответ

text = 'abcdefg'
new = list(text)
new[6] = 'W'
''.join(new)

Что довольно медленно по сравнению с «Методом 2»

timeit.timeit("text = 'abcdefg'; s = list(text); s[6] = 'W'; ''.join(s)", number=1000000)
1.0411581993103027

Метод 2 (БЫСТРЫЙ МЕТОД)

Данный ответ

text = 'abcdefg'
text = text[:1] + 'Z' + text[2:]

Который намного быстрее:

timeit.timeit("text = 'abcdefg'; text = text[:1] + 'Z' + text[2:]", number=1000000)
0.34651994705200195

Способ 3:

Байтовый массив:

timeit.timeit("text = 'abcdefg'; s = bytearray(text); s[1] = 'Z'; str(s)", number=1000000)
1.0387420654296875
Мехди Неллен
источник
1
Было бы интересно посмотреть, как это происходит с методом bytearray.
Габорист
1
Хорошее предложение. Метод bytearray также медленнее: в timeit.timeit("text = 'abcdefg'; s = bytearray(text); s[1] = 'Z'; str(s)", number=1000000)два раза медленнее, чем самый быстрый.
Мехди Неллен
2
Цените тесты, которые заставляют меня переосмыслить, как я должен манипулировать строками Python.
Spectral
1
Ницца. Пожалуйста, измените ответ, чтобы включить метод 3 тоже (bytearray).
AneesAhmed777
1
Следует отметить, что большую часть времени здесь проводят в преобразованиях ... (строка -> байтовый массив). Если вам нужно внести много изменений в строку, метод байтового массива будет быстрее.
Ян Садбери
37

Строки Python неизменны, вы меняете их, делая копию.
Вероятно, самый простой способ сделать то, что вы хотите, это:

text = "Z" + text[1:]

В text[1:]возвращает строку в textот позиции 1 до конца, позиция считать от 0 , так «1» второго символ.

редактировать: вы можете использовать ту же технику нарезки строк для любой части строки

text = text[:1] + "Z" + text[2:]

Или, если буква появляется только один раз, вы можете использовать метод поиска и замены, предложенный ниже

Мартин Беккет
источник
Я имею в виду второго персонажа, т.е. персонаж на месте № 1 (в сравнении с 1-м
знаком
текст [0] + "Z" + текст [2:]
wbg
13

Начиная с python 2.6 и python 3 вы можете использовать байт-массивы, которые являются изменяемыми (могут изменяться поэлементно в отличие от строк):

s = "abcdefg"
b_s = bytearray(s)
b_s[1] = "Z"
s = str(b_s)
print s
aZcdefg

редактировать: изменен str на s

edit2: Как отметил в комментариях Two-Bit Alchemist, этот код не работает с юникодом.

Махмуд
источник
Этот ответ неверен. С одной стороны, это должно быть bytearray(s), а не bytearray(str). С другой стороны , это будет производить: TypeError: string argument without an encoding. Если вы укажете кодировку, то вы получите TypeError: an integer is required. Это с Python 3 или Unicode Python 2. Если вы сделаете это в Python 2 (с исправленной второй строкой), это не сработает для не-ASCII символов, потому что они могут быть не одним байтом. Попробуйте, s = 'Héllo'и вы получите 'He\xa9llo'.
Двухразрядный алхимик
Я попробовал это снова на Python 2.7.9. Я не смог воспроизвести ошибку, о которой вы упомянули (TypeError: строковый аргумент без кодировки).
Махмуд
Эта ошибка применяется, только если вы используете Unicode. Попробуй s = u'abcdefg'.
Двухразрядный алхимик
4
НЕ ДЕЛАЙТЕ ЭТОГО. Этот метод игнорирует всю концепцию кодирования строк, что означает, что он работает только с символами ASCII. В наши дни вы не можете принимать ASCII, даже если вы говорите по-английски в англоязычной стране. Самая большая обратная несовместимость Python3, и, на мой взгляд, самая важная, это исправление всей этой ложной эквивалентности byte = string. Не возвращай это.
Адам
5

Как говорили другие люди, обычно строки Python должны быть неизменяемыми.

Однако, если вы используете CPython, реализацию на python.org, можно использовать ctypes для изменения строковой структуры в памяти.

Вот пример, где я использую технику, чтобы очистить строку.

Пометить данные как чувствительные в Python

Я упоминаю это для полноты картины, и это должно быть вашим последним средством, так как оно хакерское.

неизвестный
источник
6
Крайнее средство? Если ты когда-нибудь сделаешь это, тебя вдруг заклеймят злом!
Крис Морган
@ChrisMorgan, если ваша строка содержит пароль, очистки его с помощью s = 'недостаточно, потому что пароль все еще записан где-то в памяти. Очистка через ctypes - единственный способ.
Cabu
1
@ Cabu Я бы ни при каких обстоятельствах не принял код, который это сделал. Если ваши данные конфиденциальны, и вы заботитесь о такой безопасности, strэто не тот тип, который вам подходит. Просто не используйте это. Используйте что-то вроде bytearrayэтого. (Еще лучше, оберните его во что-то, что позволит вам более или менее обрабатывать его как непрозрачные данные, чтобы вы действительно не могли извлечь strиз него данные, чтобы защитить вас от несчастных случаев. Для этого может быть библиотека. Не знаю.)
Крис Морган
4

Этот код не мой. Я не мог вспомнить форму сайта, где я ее взял. Интересно, что вы можете использовать это, чтобы заменить одного или нескольких персонажей одним или несколькими персонажами. Хотя этот ответ очень запоздал, новички вроде меня (в любое время) могут найти его полезным.

Функция изменения текста.

mytext = 'Hello Zorld'
mytext = mytext.replace('Z', 'W')
print mytext,
K.Vee.Shanker.
источник
11
Это не отвечает на вопрос. Это не то, что было желательно.
Крис Морган
2
Этот код плох, если вы хотите заменить только первый l. mytext = mytext.replace('l', 'W')->HeWWo Zorld
Ooker
Если вы хотите хирургическим путем заменить только 1 символ (которым я являюсь), это идеально подходит для вас. Спасибо!
ProfVersaggi
@ProfVersaggi Это абсолютно неверно. Смотрите комментарий Оокера выше.
Двухразрядный алхимик
3
@Ooker Если вы хотите заменить только первый символ, который вы можете использовать mytext = mytext.replace('l', 'W',1). Ссылка на док
Алекс
2

На самом деле, со строками вы можете сделать что-то вроде этого:

oldStr = 'Hello World!'    
newStr = ''

for i in oldStr:  
    if 'a' < i < 'z':    
        newStr += chr(ord(i)-32)     
    else:      
        newStr += i
print(newStr)

'HELLO WORLD!'

По сути, я «добавляю» + «строки» вместе в новую строку :).

user5587487
источник
4
Это будет очень медленно, потому что каждая конкатенация должна создавать новый строковый объект, так как они неизменяемы, о чем этот вопрос.
Двухразрядный алхимик
0

если ваш мир на 100% ascii/utf-8(в эту коробку помещается множество вариантов использования):

b = bytearray(s, 'utf-8')
# process - e.g., lowercasing: 
#    b[0] = b[i+1] - 32
s = str(b, 'utf-8')

Python 3.7.3

Пол Натан
источник
0

Я хотел бы добавить еще один способ изменения символа в строке.

>>> text = '~~~~~~~~~~~'
>>> text = text[:1] + (text[1:].replace(text[0], '+', 1))
'~+~~~~~~~~~'

Насколько быстрее это по сравнению с превращением строки в список и заменой i-го значения с последующим присоединением снова?

Список подход

>>> timeit.timeit("text = '~~~~~~~~~~~'; s = list(text); s[1] = '+'; ''.join(s)", number=1000000)
0.8268570480013295

Мое решение

>>> timeit.timeit("text = '~~~~~~~~~~~'; text=text[:1] + (text[1:].replace(text[0], '+', 1))", number=1000000)
0.588400217000526
Мохаммед Вазим
источник