Зміна одного символу в рядку в Python


385

Який найпростіший спосіб у Python замінити символ у рядку?

Наприклад:

text = "abcdefg";
text[1] = "Z";
           ^

Відповіді:


534

Не змінюйте рядки.

Робота з ними як списки; перетворюйте їх у рядки лише за потреби.

>>> 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 незмінні (тобто їх неможливо змінити). Є багато причин для цього. Використовуйте списки, поки у вас немає вибору, лише потім перетворіть їх у рядки.


4
Ті, хто шукає швидкості / ефективності, читають це
AneesAhmed777

4
"Не змінюйте рядки." чому
hacksoi

2
"Створити-> змінити-> серіалізувати-> призначити-> безкоштовно" більш ефективним, ніж s [6] = 'W'? Хм ... Чому інші мови дозволяють це, незважаючи на це "безліч" причин? Цікаво, як можна захищати дивний дизайн (для любові, мабуть). Чому б не запропонувати до ядра Python додати функцію MID (strVar, index, newChar), яка спрямовує доступ до позиції пам'яті char, замість того, щоб зайво переміщувати байт цілою рядком?
Оскар

@hacksoi, @oscar, причина досить проста: немає необхідності відмовлятися при переході покажчиків навколо, щоб здійснити копіювати-змінювати або прямо копіювати весь рядок у випадку, якщо хтось хоче змінити цей рядок - це призводить до збільшення швидкості в загальному використання. Немає необхідності в таких речах, як MIDчерез скибочки:s[:index] + c + s[index+1:]
MultiSkill

1
@oscar Під німими мовами я маю на увазі, що вони не мають справу з unicode, якщо ви прямо не скажете їм це зробити. Звичайно, ви можете писати програми, що підтримують unicode, в 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.
Спектрал

1
Приємно. Будь-ласка, відредагуйте відповідь, щоб також було включено метод 3 (байт-масив).
AneesAhmed777

1
Слід зазначити, що більша частина часу тут проводиться на перетвореннях ... (рядок -> масив байтів). Якщо вам доведеться внести багато змін у рядок, метод байтового масиву буде швидшим.
Ian Sudbery


37

Рядки Python незмінні, ви змінюєте їх, роблячи копію.
Напевно, найпростіший спосіб зробити те, що ви хочете:

text = "Z" + text[1:]

У text[1:]повертає рядок в textвід позиції 1 до кінця, позиція вважати від 0 , так «1» другого символ.

редагувати: Ви можете використовувати ту саму техніку нарізки рядків для будь-якої частини рядка

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

Або якщо лист з’являється лише один раз, ви можете використовувати запропоновану нижче техніку пошуку та заміни


Я наставляю 2-го персонажа, IE. персонаж у місці № 1 (призначений для 1-го символу, номер 0)
kostia

текст [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: Як зазначено у коментарях двобітний алхімік, цей код не працює з unicode.


Ця відповідь невірна. З одного боку, це має бути bytearray(s), ні bytearray(str). З іншого боку , це буде виробляти: TypeError: string argument without an encoding. Якщо ви вказали кодування, то отримаєте TypeError: an integer is required. Це з унікодом Python 3 або Python 2. Якщо ви зробите це в Python 2 (з виправленим другим рядком), він не працюватиме для символів, що не належать до ASCII, оскільки вони можуть бути не одним байтом. Спробуйте це, s = 'Héllo'і ви отримаєте 'He\xa9llo'.
Двобітовий алхімік

Я знову спробував це на Python 2.7.9. Я не міг відновити вказану вами помилку (TypeError: аргумент рядка без кодування).
Махмуд

Ця помилка застосовується, лише якщо ви використовуєте unicode. Спробуйте s = u'abcdefg'.
Двобітовий алхімік

4
НЕ РОБИ ЦЬОГО. Цей метод ігнорує всю концепцію кодування рядків, це означає, що він працює лише на символах ASCII. У цей день і вік ви не можете вважати ASCII, навіть якщо ви володієте англійською мовою в англомовній країні. Найбільша відсталість несумісності Python3, і, на мою думку, найважливіша, - це фіксація цілого байта = рядкової помилкової еквівалентності. Не повертай його назад.
Адам

5

Як і інші люди казали, як правило, струни Python мають бути незмінними.

Однак якщо ви використовуєте CPython, реалізацію на python.org, можна використовувати ctypes для зміни структури рядків у пам'яті.

Ось приклад, коли я використовую техніку для очищення рядка.

Позначте дані як чутливі в python

Я згадую це заради повноти, і це має бути вашим останнім засобом, оскільки це хакі.


6
Останній засіб? Якщо ви коли-небудь це робите, вас раптом називають злом!
Кріс Морган

@ChrisMorgan, якщо ваш рядок містить пароль, очищення його за допомогою s = '' недостатньо, оскільки пароль все ще записаний десь у пам'яті. Очистити його через типи - єдиний спосіб.
Кабу

1
@Cabu Я ніколи ні за яких обставин не приймав код, який це робив. Якщо ваші дані є конфіденційними і ви дбаєте про безпеку, як це, strце не правильний для вас тип. Просто не використовуйте його. Використовуйте bytearrayзамість цього щось подібне . (Ще краще, загортайте його в щось, що дозволяє вам ставитися до цього більш-менш як до непрозорих даних, щоб ви справді не могли отримати strз нього захист від нещасних випадків. Для цього може бути бібліотека. Немає ідеї.)
Кріс Морган

4

Цей код не мій. Я не міг згадати форму сайту, де я взяв її. Цікаво, що ви можете використовувати це для заміни одного або декількох символів одним або декількома символами. Хоча ця відповідь дуже пізня, такі послушники, як я (будь-коли) можуть вважати її корисною.

Змінити функцію тексту.

mytext = 'Hello Zorld'
mytext = mytext.replace('Z', 'W')
print mytext,

11
Це не відповідає на запитання. Це зовсім не те, що було бажано.
Кріс Морган

2
Цей код поганий, якщо ви хочете замінити лише перший l. mytext = mytext.replace('l', 'W')->HeWWo Zorld
Ooker

Якщо ви хочете хірургічно замінити лише 1 символ (який я є), це цілком відповідає законопроекту. Дякую!
ProfVersaggi

@ProfVersaggi Це абсолютно помилково. Дивіться коментар Оукера вище.
Двобітовий алхімік

3
@Ooker Якщо ви хочете замінити лише перший символ, який ви можете використовувати mytext = mytext.replace('l', 'W',1). Посилання на doc
Алекс

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!'

В основному я "додаю" + "рядки" разом у новий рядок :).


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')

пітон 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.