เหตุใด 3 แบ็กสแลชจึงเท่ากับ 4 ในสตริง Python


90

บอกได้ไหมว่าทำไมถึง'?\\\?'=='?\\\\?'ให้True? นั่นทำให้ฉันแทบคลั่งและหาคำตอบที่สมเหตุสมผลไม่ได้ ...

>>> list('?\\\?')
['?', '\\', '\\', '?']
>>> list('?\\\\?')
['?', '\\', '\\', '?']

8
หลังไม่ได้หนีอะไรเลยดังนั้นมันจึงกลายเป็นหนีไปเอง
Padraic Cunningham

1
ไม่จำเป็นต้องรวมlist()แม้แต่:>>> '?\\\?' '?\\\\?'
daboross

@PadraicCunningham มันไม่ได้ "จบลงด้วยการหนีตัวเอง" นั่นหมายความว่าอย่างไร?
user253751

น่าขบขันเหตุผลก็คือทั้งคู่เท่ากับแบ็กสแลชสองตัว :-)
RemcoGerlich

@immibis นั่นคือสิ่งที่เกิดขึ้น คุณรู้ความแตกต่างระหว่าง repr และ str หรือไม่? ลองพิมพ์ทั้งสองอย่างโดยใช้แบ็กสแลชหนึ่งตัวในสตริงและมันอาจจะชัดเจน
Padraic Cunningham

คำตอบ:


84

โดยทั่วไปเนื่องจาก python ผ่อนปรนเล็กน้อยในการประมวลผลแบ็กสแลช อ้างจากhttps://docs.python.org/2.0/ref/strings.html :

ซึ่งแตกต่างจากมาตรฐาน C ทั้งหมดลำดับหนีไม่รู้จักที่เหลืออยู่ในสายที่ไม่เปลี่ยนแปลงคือเครื่องหมายที่เหลืออยู่ในสตริง

(เน้นในต้นฉบับ)

ดังนั้นใน python ไม่ใช่ว่าแบ็กสแลชสามตัวจะมีค่าเท่ากับสี่ แต่เมื่อคุณติดตามแบ็กสแลชด้วยอักขระที่เหมือน?กันทั้งสองจะมารวมกันเป็นสองอักขระเนื่องจาก\?ไม่ใช่ลำดับการหลีกเลี่ยงที่รู้จัก


6
ตรงข้ามกับผ่อนปรน Lenient เป็นพฤติกรรมของคนอื่น ๆส่วนใหญ่ที่ว่า "ถ้าคุณแบ็กสแลชอักขระที่ไม่ต้องการแบ็กสแลชจะไม่ทำอะไรเลย" ร่วมกับอนุสัญญาอื่น ๆ (การใส่ตัวอักษรและตัวเลขแบบย้อนกลับอาจทำให้เป็นแบบพิเศษ แต่เครื่องหมายวรรคตอนแบ็กสแลชจะทำให้ไม่พิเศษเสมอ) คุณจะได้รับคุณสมบัติที่ดีมากที่คุณสามารถทำให้สตริงเสียค่าได้อย่างปลอดภัยโดยการแบ็กสแลตเครื่องหมายวรรคตอนทั้งหมดโดยไม่ต้องรู้ว่าอักขระใดเป็นพิเศษ interpeted - คุณสมบัติที่ Python ขาด
hobbs

24
ไม่ตรงกันข้ามกับการผ่อนปรนคือการเพิ่มข้อผิดพลาดเมื่อคุณใช้การหลีกเลี่ยงแบ็กสแลชที่ไม่รู้จัก (เหมือนภาษาคอมไพล์เกือบทุกภาษาจำไว้ว่าการประมวลผลสตริงของ Python นั้นโดยพื้นฐานแล้ว "เหมือน C ยกเว้นว่าเราจะไม่ระเบิดเมื่อส่งแบ็กสแลชที่ไม่ถูกต้องหนี") นอกจากนี้ในสตริงไม่ว่าจะเป็นภาษาใดก็ตามมีเพียงสองอักขระที่ต้องหลบหนี - สิ่งที่คุณใช้เป็นตัวคั่นและแบ็กสแลชเอง ฉันไม่เข้าใจข้อโต้แย้งที่ว่ามันยากที่จะจำทั้งคู่
Daniel Martin

@DanielMartin มีบางภาษาที่ตัวคั่นทำหน้าที่เป็นอักขระหลีกของตัวเอง (เช่น'escape''d') คุณไม่จำเป็นต้องจำตัวละครอื่น ๆ ที่นั่น!
SztupY

1
เดี๋ยวก่อนฉันเดาว่าภาษาปาสคาลมาตรฐานก็ใช้ระบบนั้นเช่นกัน - ดูที่nyx.net/~gthompso/self_pasc.txt
Daniel Martin

1
@DanielMartin SQL ด้วย
Random832

30

เนื่องจากแบ็กสแลชทำหน้าที่เป็นอักขระหลีกสำหรับอักขระที่ตามหลังทันทีหากชุดค่าผสมแสดงลำดับการหลีกเลี่ยงที่ถูกต้อง โหลหรือดังนั้นลำดับหนีจะถูกแสดงไว้ที่นี่ พวกเขารวมถึงคนที่เห็นได้ชัดเช่นการขึ้นบรรทัดใหม่\nแท็บแนวนอน\tกลับสายการบิน\rและคนที่คลุมเครือมากขึ้นเช่นอักขระ Unicode ตั้งชื่อโดยใช้\N{...}เช่นซึ่งหมายถึงอักขระ\N{WAVY DASH} Unicode \u3030ประเด็นสำคัญคือถ้าไม่ทราบลำดับการหลีกเลี่ยงลำดับอักขระจะเหลืออยู่ในสตริงตามที่เป็นอยู่

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

>>> '?\\\?'
'?\\\\?'
>>> print('?\\\?')
?\\?
>>> '?\\\?' == '?\\?'    # I don't know why you think this is True???
False
>>> '?\\\?' == r'?\\?'   # but if you use a raw string for '?\\?'
True
>>> '?\\\\?' == '?\\\?'  # this is the same string... see below
True

สำหรับตัวอย่างเฉพาะของคุณในกรณีแรก'?\\\?'ตัวแรกจะหลีกเลี่ยง\แบ็กสแลชที่สองโดยเหลือแบ็กสแลชเดียว แต่แบ็กสแลชที่สามยังคงเป็นแบ็กสแลชเนื่องจาก\?ไม่ใช่ลำดับการหลีกที่ถูกต้อง ?\\?ดังนั้นสตริงที่เกิดคือ

สำหรับกรณีที่สองที่ทับขวาเป็นครั้งแรกที่สองหนีออกมาและทับขวาที่สามหนีออกมาที่สี่ซึ่งจะส่งผลในสตริง'?\\\\?'?\\?

นั่นคือสาเหตุที่แบ็กสแลชสามตัวเหมือนกันกับสี่:

>>> '?\\\?' == '?\\\\?'
True

หากคุณต้องการสร้างสตริงที่มีแบ็กสแลช 3 ตัวคุณสามารถหลีกเลี่ยงแบ็กสแลชแต่ละตัวได้:

>>> '?\\\\\\?'
'?\\\\\\?'
>>> print('?\\\\\\?')
?\\\?

หรือคุณอาจพบว่าสตริง "ดิบ" เข้าใจได้ง่ายขึ้น:

>>> r'?\\\?'
'?\\\\\\?'
>>> print(r'?\\\?')
?\\\?

การเปลี่ยนการประมวลผลลำดับ Escape สำหรับสตริงลิเทอรัล ดูString Literalsสำหรับรายละเอียดเพิ่มเติม


คุณขวา'?\\\?'=='?\\?'ให้Falseผมพิมพ์ผิด ควรจะ'?\\\?'=='?\\\\?'เป็นตามที่คำถามระบุฉันได้แก้ไขแล้ว
kozooh

13

เพราะ\xในสายอักขระเมื่อxไม่ได้เป็นหนึ่งในตัวละครที่ backslashable พิเศษเช่นn, r, t, 0ฯลฯ xประเมินสตริงที่มีเครื่องหมายและจากนั้น

>>> '\?'
'\\?'

7

จากหน้าการวิเคราะห์คำศัพท์ python ใต้ตัวอักษรสตริงที่: https://docs.python.org/2/reference/lexical_analysis.html

มีตารางที่แสดงรายการลำดับการหลีกเลี่ยงที่รู้จักทั้งหมด

\\ คือลำดับการหลีกเลี่ยงที่ === \

\? ไม่ใช่ลำดับการหลีกเลี่ยงและเป็น === \?

ดังนั้น '\\\\' คือ '\\' ตามด้วย '\\' ซึ่งก็คือ '\\' (สอง Escape \)

และ '\\\' คือ '\\' ตามด้วย '\' ซึ่งก็คือ '\\' เช่นกัน (หนึ่ง Escape \ และอีกหนึ่งดิบ \)

นอกจากนี้ควรสังเกตว่า python ไม่ได้แยกความแตกต่างระหว่างเครื่องหมายคำพูดเดี่ยวและคู่รอบตัวอักษรสตริงซึ่งแตกต่างจากภาษาอื่น ๆ

ดังนั้น "String" และ "String" จึงเหมือนกันทุกประการใน python ไม่มีผลต่อการตีความลำดับการหลีกเลี่ยง


1

คำตอบของ mhawke ค่อนข้างครอบคลุมฉันแค่อยากจะอธิบายใหม่ในรูปแบบที่กระชับและมีตัวอย่างเล็กน้อยที่แสดงพฤติกรรมนี้

ฉันเดาว่าสิ่งหนึ่งที่ต้องเพิ่มก็คือการหลีกเลี่ยงการประมวลผลการเคลื่อนที่จากซ้ายไปขวาเพื่อให้\nค้นหาแบ็กสแลชก่อนจากนั้นมองหาตัวละครที่จะหลบหนีจากนั้นค้นหาnและหลบหนี \\nค้นหาแบ็กสแลชแรกค้นหาวินาทีและหลบหนีจากนั้นค้นหาnและมองว่ามันเป็นตัวอักษร n; \?ค้นหาแบ็กสแลชและมองหาอักขระเพื่อหลบหนีพบว่า?สิ่งที่ไม่สามารถหลีกหนีได้และถือว่า\เป็นแบ็กสแลชตามตัวอักษร

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

>>> print('\?') # \? is not a valid escape code so backslash is left as-is
\?
>>> print('\\?') # \\ is a valid escape code, resulting in a single backslash
'\?'

>>> '\?' # same as first example except that interactive interpreter escapes the backslash
\\?
>>> '\\?' # same as second example, backslash is again escaped
\\?
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.