Dlaczego 3 ukośniki odwrotne są równe 4 w ciągu znaków Pythona?

90

Czy możesz mi powiedzieć, dlaczego '?\\\?'=='?\\\\?'daje True? To doprowadza mnie do szału i nie mogę znaleźć rozsądnej odpowiedzi ...

>>> list('?\\\?')
['?', '\\', '\\', '?']
>>> list('?\\\\?')
['?', '\\', '\\', '?']
kozooh
źródło
8
Ten ostatni nie ucieka przed niczym, więc kończy się ucieczką
Padraic Cunningham,
1
Nie trzeba uwzględniać list()nawet:>>> '?\\\?' '?\\\\?'
daboross
@PadraicCunningham To nie „kończy się ucieczką samego siebie”. Co to w ogóle znaczy?
user253751
Co zabawne, powodem jest to, że oba są równe dwóm odwrotnym
ukośnikom
@immibis, właśnie to się dzieje. Czy znasz różnicę między repr i str? Spróbuj wydrukować oba z jednym ukośnikiem odwrotnym w ciągu, a może stać się jasne
Padraic Cunningham,

Odpowiedzi:

84

Zasadniczo dlatego, że Python jest nieco łagodniejszy w przetwarzaniu odwrotnego ukośnika. Cytowanie z https://docs.python.org/2.0/ref/strings.html :

W przeciwieństwie do standardu C, wszystkie nierozpoznane sekwencje specjalne są pozostawione w ciągu niezmienionym, tj . Odwrotny ukośnik jest pozostawiony w ciągu .

(Podkreślenie w oryginale)

Dlatego w Pythonie nie chodzi o to, że trzy odwrotne ukośniki są równe czterem, ale o to, że po odwróconym ukośniku z takim znakiem ?, te dwa razem przechodzą jako dwa znaki, ponieważ \?nie jest to rozpoznawana sekwencja ucieczki.

Daniel Martin
źródło
6
To przeciwieństwo wyrozumiałości. Łagodne to zachowanie większości innych osób polegające na tym, że „jeśli odwrócisz ukośnik znaku, który tego nie potrzebuje, odwrotny ukośnik nic nie robi”. Wraz z inną konwencją (że znaki alfanumeryczne z odwrotnym ukośnikiem mogą uczynić je wyjątkowymi, ale znaki interpunkcyjne odwrotnym ukośnikiem zawsze sprawiają, że nie są one wyjątkowe), otrzymujesz bardzo fajną właściwość, że możesz bezpiecznie rozbić ciąg, odwracając wszystkie znaki interpunkcyjne, bez konieczności wiedzieć, które znaki są specjalnie interpeted - właściwość, której brakuje Pythonowi.
hobbs
24
Nie, przeciwieństwem pobłażania byłoby wywołanie błędu, gdy używasz nierozpoznanego znaku ucieczki odwrotnym ukośnikiem. (Jak prawie każdy język kompilowany. Pamiętaj, że przetwarzanie napisów w Pythonie jest w zasadzie „jak C, z wyjątkiem tego, że nie nadymamy, gdy podaje się nieprawidłowe znaki z odwrotnym ukośnikiem”). - cokolwiek używasz jako separatora, i sam ukośnik odwrotny. Nie rozumiem argumentu, że trudno zapamiętać jedno i drugie.
Daniel Martin
@DanielMartin jest kilka języków, w których separator działa jako własny znak ucieczki (np 'escape''d'.). Nie musisz nawet pamiętać innych postaci!
SztupY
1
Och, czekaj, domyślam się, że standardowy pascal również używał tego systemu - patrz nyx.net/~gthompso/self_pasc.txt
Daniel Martin
1
@DanielMartin SQL też.
Random832
30

Dzieje się tak, ponieważ ukośnik odwrotny działa jako znak zmiany znaczenia dla znaku (ów) bezpośrednio po nim, jeśli kombinacja reprezentuje prawidłową sekwencję ucieczki. Kilkunastu sekwencje są tutaj wymienione . Obejmują one oczywiste, takie jak znak nowej linii \n, tabulator poziomy \t, powrót karetki \ri bardziej niejasne, takie jak nazwane znaki unicode \N{...}, np. \N{WAVY DASH}Które reprezentują znak unicode \u3030. Kluczową kwestią jest jednak to, że jeśli sekwencja ucieczki nie jest znana, sekwencja znaków pozostaje w ciągu bez zmian.

Częścią problemu może być również to, że dane wyjściowe interpretera Pythona wprowadzają Cię w błąd. Dzieje się tak, ponieważ odwrotne ukośniki są usuwane podczas wyświetlania. Jeśli jednak wydrukujesz te ciągi, zobaczysz, że dodatkowe ukośniki odwrotne znikną.

>>> '?\\\?'
'?\\\\?'
>>> 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

Dla twoich konkretnych przykładów, w pierwszym przypadku '?\\\?', pierwszy \znak ucieka przed drugim ukośnikiem odwrotnym, pozostawiając pojedynczy ukośnik odwrotny, ale trzeci ukośnik odwrotny pozostaje ukośnikiem odwrotnym, ponieważ \?nie jest prawidłową sekwencją ucieczki. Stąd wynikowy ciąg to ?\\?.

W drugim przypadku '?\\\\?'pierwszy lewy ukośnik wymyka się drugiemu, a trzeci odwrotny ukośnik wymyka się czwartemu, co daje w wyniku ciąg ?\\?.

Dlatego trzy ukośniki odwrotne to to samo, co cztery:

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

Jeśli chcesz utworzyć ciąg z 3 odwrotnymi ukośnikami, możesz pominąć każdy lewy ukośnik:

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

lub może się okazać, że „surowe” ciągi znaków są bardziej zrozumiałe:

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

To włącza przetwarzanie sekwencji ucieczki dla literału ciągu. Aby uzyskać więcej informacji, zobacz Literały ciągów .

mhawke
źródło
Masz rację '?\\\?'=='?\\?'daje False, pomyliłem się. Powinno być '?\\\?'=='?\\\\?'tak, jak wskazuje pytanie, poprawiłem to.
kozooh
13

Ponieważ \xw ciągu znaków, gdy xnie jest jednym ze specjalnych znaków backslashable jak n, r, t, 0, itp Zwraca ciąg znaków z odwrotnym ukośnikiem a potem x.

>>> '\?'
'\\?'
Paweł
źródło
7

Ze strony analizy leksykalnej Pythona pod literałami ciągów pod adresem : https://docs.python.org/2/reference/lexical_analysis.html

Istnieje tabela zawierająca wszystkie rozpoznane sekwencje specjalne.

\\ to sekwencja ucieczki, która jest === \

\? nie jest sekwencją ucieczki i jest === \?

więc „\\\\” to „\\”, po którym następuje „\\”, czyli „\\” (dwa znaki ucieczki \)

a „\\\” to „\\”, po którym następuje „\”, czyli również „\\” (jeden znak ze znakiem ucieczki \ i jeden surowy \)

należy również zauważyć, że w przeciwieństwie do niektórych innych języków python nie rozróżnia pojedynczych i podwójnych cudzysłowów otaczających literał łańcuchowy.

Zatem „String” i „String” to dokładnie to samo w Pythonie, nie wpływają one na interpretację sekwencji ucieczki.

rkh
źródło
1

odpowiedź mhawke prawie to pokrywa, chcę tylko powtórzyć to w bardziej zwięzłej formie i z minimalnymi przykładami, które ilustrują to zachowanie.

Myślę, że jedną rzeczą do dodania jest to, że przetwarzanie ucieczki przesuwa się od lewej do prawej, tak że \nnajpierw znajduje ukośnik odwrotny, a następnie szuka znaku do ucieczki, a następnie znajduje go ni ucieka; \\nznajduje pierwszy ukośnik odwrotny, znajduje drugi ukośnik i pomija go, a następnie znajduje ni widzi literę n; \?znajduje odwrotny ukośnik i szuka znaku do ucieczki, wyszukuje, ?których nie można uciec, i dlatego traktuje \jako dosłowny odwrotny ukośnik.

Jak zauważył mhawke, kluczem jest tutaj to, że interaktywny interpreter wymyka się ukośnikiem odwrotnym podczas wyświetlania ciągu. Zgaduję, że powodem tego jest upewnienie się, że ciągi tekstowe skopiowane z interpretera do edytora kodu są prawidłowymi ciągami w języku Python. Jednak w tym przypadku ta ulga dla wygody powoduje zamieszanie.

>>> 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
\\?
Deszczowy
źródło