การเปลี่ยนหนึ่งอักขระในสตริงใน 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'

สายไพ ธ อนไม่เปลี่ยนรูป (เช่นพวกเขาไม่สามารถแก้ไขได้) มีจำนวนมากของเหตุผลนี้ ใช้รายการจนกว่าคุณจะไม่มีทางเลือกจากนั้นเปลี่ยนเป็นสตริง


4
ผู้ที่กำลังมองหาความเร็ว / ประสิทธิภาพอ่านนี้
AneesAhmed777

4
"อย่าแก้ไขสตริง" ทำไม
hacksoi

2
"Create-> modified-> serialize-> assign-> free" มีประสิทธิภาพมากกว่า s [6] = 'W' หรือไม่ อืม ... ทำไมภาษาอื่น ๆ ถึงยอมแม้ว่าเหตุผล "ลอต" นั้น? ที่น่าสนใจว่าสามารถปกป้องการออกแบบที่แปลก (สำหรับความรักฉันคิดว่า) ทำไมไม่แนะนำให้เพิ่มฟังก์ชั่น MID (strVar, index, newChar) ไปยัง Python core ที่เข้าถึงตำแหน่งหน่วยความจำถ่านโดยตรงแทนที่จะเป็นไบต์สับแบบไม่ จำกัด ด้วยสตริงทั้งหมด?
ออสการ์

@hacksoi, @oscar เหตุผลค่อนข้างง่าย: ไม่ต้อง refcount เมื่อส่งพอยน์เตอร์ไปรอบ ๆ เพื่อใช้ copy-on-modified หรือคัดลอกสตริงทั้งหมดในกรณีที่มีคนต้องการแก้ไขสตริงนั้น - ซึ่งนำไปสู่การเพิ่มความเร็วในทั่วไป ใช้. ไม่จำเป็นต้องมีสิ่งต่าง ๆ เช่นMIDชิ้น:s[:index] + c + s[index+1:]
MultiSkill

1
@ Oscar ด้วยภาษาที่โง่ฉันหมายถึงพวกเขาไม่ได้จัดการกับยูนิโค้ดเว้นแต่คุณจะบอกพวกเขาอย่างชัดเจน แน่นอนว่าคุณสามารถเขียนแอปพลิเคชันที่มีความสามารถ Unicode ใน C แต่คุณต้องใส่ใจกับมันตลอดเวลาและต้องทดสอบอย่างชัดเจนเพื่อหลีกเลี่ยงปัญหา ทุกอย่างเป็นเครื่องจักร ฉันทำงานกับ PHP ก่อนเรียน Python และภาษานั้นยุ่งเหยิงไปหมด เกี่ยวกับบันทึกย่อของคุณเกี่ยวกับซีพียูที่รวดเร็ว แต่ส่วนหนึ่งของปัญหานั้นคือการไม่อนุมัติยอดนิยมของการปรับให้เหมาะสมก่อนกำหนดซึ่งนำไปสู่การล่ามและไลบรารีช้าลงโดยการรั่วไหลของซีพียูมากมาย
Bachsau

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
คงจะน่าสนใจที่จะดูว่าค่าโดยสารเทียบกับวิธีบายเรย์
gaborous

1
คำแนะนำที่ดี วิธี bytearray ก็ช้าลงเช่นกัน: ช้ากว่าtimeit.timeit("text = 'abcdefg'; s = bytearray(text); s[1] = 'Z'; str(s)", number=1000000)สองเท่า
Mehdi Nellen

2
ชื่นชมการทดสอบซึ่งทำให้ฉันคิดใหม่ว่าฉันควรจัดการกับสตริง Python อย่างไร
Spectral

1
ดี โปรดแก้ไขคำตอบเพื่อรวมวิธีที่ 3 ด้วย (bytearray)
AneesAhmed777

1
ควรสังเกตว่าใช้เวลาส่วนใหญ่ในการแปลง ... (สตริง -> อาร์เรย์ไบต์) หากคุณมีการแก้ไขมากมายที่จะทำให้สตริงวิธีการอาร์เรย์ไบต์จะเร็วขึ้น
Ian Sudbery


37

สายไพ ธ อนไม่เปลี่ยนรูปคุณเปลี่ยนมันด้วยการทำสำเนา
วิธีที่ง่ายที่สุดในการทำสิ่งที่คุณต้องการน่าจะเป็น:

text = "Z" + text[1:]

text[1:]ผลตอบแทนสตริงในtextจากตำแหน่งที่ 1 ไปยังจุดสิ้นสุดตำแหน่งนับจาก 0 ดังนั้น '1' เป็นตัวละครที่สอง

แก้ไข: คุณสามารถใช้เทคนิคการแบ่งสตริงเดียวกันสำหรับส่วนใด ๆ ของสตริง

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

หรือถ้าตัวอักษรปรากฏขึ้นเมื่อคุณสามารถใช้การค้นหาและแทนที่เทคนิคที่แนะนำด้านล่าง


ฉันพูดถึงตัวละครที่สอง IE ตัวละครที่สถานที่หมายเลข 1 (ตามที่อักขระตัวที่ 1 หมายเลข 0)
kostia

ข้อความ [0] + "Z" + ข้อความ [2:]
wbg

13

เริ่มต้นด้วย python 2.6 และ python 3 คุณสามารถใช้ bytearrays ซึ่งไม่แน่นอน (สามารถเปลี่ยนแปลงองค์ประกอบที่ไม่เหมือนกับสตริง):

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

แก้ไข: เปลี่ยน str เป็น s

แก้ไข 2: ตามที่นักเล่นแร่แปรธาตุ Two-Bit กล่าวถึงในความคิดเห็นรหัสนี้ไม่สามารถทำงานกับยูนิโค้ด


คำตอบนี้ไม่ถูกต้อง สำหรับสิ่งหนึ่งที่มันควรจะเป็นไม่ได้bytearray(s) bytearray(str)อีกสิ่งนี้จะผลิต: TypeError: string argument without an encoding. TypeError: an integer is requiredหากคุณระบุการเข้ารหัสแล้วคุณจะได้รับ นั่นคือด้วย Unicode ของ Python 3 หรือ Python 2 หากคุณทำสิ่งนี้ใน Python 2 (ด้วยบรรทัดที่สองที่แก้ไข) มันจะไม่ทำงานสำหรับอักขระที่ไม่ใช่ ASCII เพราะอาจไม่ใช่เพียงหนึ่งไบต์ ลองกับและคุณจะได้รับs = 'Héllo' 'He\xa9llo'
นักเล่นแร่แปรธาตุ Two-Bit

ฉันลองอีกครั้งใน Python 2.7.9 ฉันไม่สามารถสร้างข้อผิดพลาดที่คุณพูดถึงใหม่ได้ (TypeError: อาร์กิวเมนต์สตริงโดยไม่มีการเข้ารหัส)
มาห์มุด

ข้อผิดพลาดนั้นจะใช้เฉพาะเมื่อคุณใช้ยูนิโค้ด ลองs = u'abcdefg'ดู
นักเล่นแร่แปรธาตุ Two-Bit

4
อย่าทำอย่างนี้. เมธอดนี้ละเว้นแนวคิดทั้งหมดของการเข้ารหัสสตริงซึ่งหมายความว่าจะทำงานกับอักขระ ASCII เท่านั้น ในวันและอายุนี้คุณไม่สามารถถือ ASCII ได้แม้ว่าคุณจะเป็นผู้พูดภาษาอังกฤษในประเทศที่ใช้ภาษาอังกฤษ ความเข้ากันได้ย้อนหลังที่ใหญ่ที่สุดของ Python3 และในความคิดของฉันที่สำคัญที่สุดคือการแก้ไขไบต์ทั้งหมด = สตริงที่เทียบเท่าเท็จ อย่านำมันกลับมา
อดัม

5

เช่นเดียวกับคนอื่น ๆ ที่บอกว่าโดยทั่วไปแล้วสตริงของ Python นั้นควรจะไม่เปลี่ยนรูป

อย่างไรก็ตามหากคุณใช้ CPython การใช้งานที่ python.org เป็นไปได้ที่จะใช้ ctypes เพื่อปรับเปลี่ยนโครงสร้างสตริงในหน่วยความจำ

นี่คือตัวอย่างที่ฉันใช้เทคนิคเพื่อล้างสตริง

ทำเครื่องหมายข้อมูลว่าละเอียดอ่อนในไพ ธ อน

ฉันพูดถึงเรื่องนี้เพื่อความสมบูรณ์และนี่ควรจะเป็นทางเลือกสุดท้ายของคุณเพราะมันแฮ็ค


6
สุดท้ายไหม หากคุณเคยทำเช่นนี้คุณจะถูกตราหน้าว่าชั่วร้าย!
Chris Morgan

@ChrisMorgan หากสตริงของคุณมีรหัสผ่านล้างด้วย s = '' ไม่เพียงพอเพราะรหัสผ่านยังคงเขียนอยู่ในหน่วยความจำ การล้างมันผ่าน ctypes เป็นวิธีเดียว
Cabu

1
@Cabu ฉันจะไม่เคยอยู่ภายใต้การใด ๆสถานการณ์ยอมรับรหัสที่ไม่ว่า หากข้อมูลของคุณอ่อนไหวและคุณให้ความสำคัญกับความปลอดภัยเช่นนี้strไม่ใช่ประเภทที่เหมาะสมสำหรับคุณ อย่าใช้มัน ใช้สิ่งที่ชอบbytearrayแทน (ยังดีกว่าห่อในสิ่งที่ช่วยให้คุณปฏิบัติมากกว่าหรือน้อยกว่าเป็นข้อมูลทึบเพื่อให้คุณไม่สามารถเรียกคืนstrจากมันเพื่อปกป้องคุณจากอุบัติเหตุจริง ๆ อาจมีห้องสมุดสำหรับที่ไม่มีความคิด)
Chris Morgan

4

รหัสนี้ไม่ใช่ของฉัน ฉันจำคืนฟอร์มของเว็บไซต์ไม่ได้ ที่น่าสนใจคุณสามารถใช้สิ่งนี้เพื่อแทนที่หนึ่งตัวละครหรือมากกว่าด้วยหนึ่งหรือมากกว่า charectors แม้ว่าการตอบกลับนี้จะช้ามากสามเณรอย่างฉัน (ทุกเวลา) อาจพบว่ามีประโยชน์

ฟังก์ชั่นเปลี่ยนข้อความ

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

11
สิ่งนี้ไม่ตอบคำถาม มันไม่ใช่สิ่งที่ต้องการเลย
Chris Morgan

2
รหัสนี้จะไม่ดีถ้าคุณต้องการที่จะเปลี่ยนเพียงlครั้งแรก mytext = mytext.replace('l', 'W')->HeWWo Zorld
Ooker

หากคุณกำลังมองหาที่จะผ่าตัดแทนเพียง 1 ตัวอักษร (ซึ่งฉัน) นี้เหมาะกับใบเสร็จอย่างสมบูรณ์ ขอบคุณ!
ProfVersaggi

@ProfVersaggi นั่นเป็นเท็จอย่างแน่นอน ดูความคิดเห็นของ Ooker ด้านบน
นักเล่นแร่แปรธาตุสองบิต

3
@Ooker หากคุณต้องการแทนที่เฉพาะอักขระตัวแรกที่คุณสามารถmytext = mytext.replace('l', 'W',1)ใช้ได้ ลิงก์ไปยัง doc
Alex

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
นี่จะช้ามากเพราะการต่อข้อมูลทุกครั้งจะต้องสร้างวัตถุสตริงใหม่เนื่องจากมันไม่เปลี่ยนรูปซึ่งเป็นสิ่งที่คำถามนี้เกี่ยวกับ
นักเล่นแร่แปรธาตุ Two-Bit

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

เมื่อเทียบกับการเปลี่ยนสตริงให้เป็นรายการและแทนที่ค่า ith จากนั้นจึงเข้าร่วมอีกครั้ง

รายการวิธีการ

>>> 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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.