Mengapa literal string mentah Python tidak bisa diakhiri dengan backslash tunggal?

178

Secara teknis, jumlah backslash ganjil, seperti yang dijelaskan dalam dokumentasi .

>>> r'\'
  File "<stdin>", line 1
    r'\'
       ^
SyntaxError: EOL while scanning string literal
>>> r'\\'
'\\\\'
>>> r'\\\'
  File "<stdin>", line 1
    r'\\\'
         ^
SyntaxError: EOL while scanning string literal

Sepertinya pengurai hanya dapat memperlakukan backslash dalam string mentah sebagai karakter biasa (bukankah itu yang dimaksud dengan string mentah?), Tapi saya mungkin kehilangan sesuatu yang jelas.

cdleary
sumber
8
Sepertinya ini sekarang faq . mungkin tidak ketika Anda mengajukan pertanyaan. Saya tahu dokumen yang Anda kutip mengatakan hal yang hampir sama, tetapi saya pikir saya akan menambahkan sumber dokumentasi lain.
oob

Jawaban:

124

Alasannya dijelaskan di bagian bagian yang saya soroti dengan huruf tebal:

Kutipan string bisa lolos dengan backslash, tetapi backslash tetap di string; misalnya, r"\""adalah string literal yang valid yang terdiri dari dua karakter: garis miring terbalik dan kutipan ganda; r"\"bukan string literal yang valid (bahkan string mentah tidak dapat berakhir dengan jumlah garis miring terbalik yang ganjil). Secara khusus, string mentah tidak dapat berakhir dengan backslash tunggal (karena backslash akan lolos dari karakter kutipan berikut). Perhatikan juga bahwa garis miring terbalik tunggal yang diikuti oleh baris baru ditafsirkan sebagai dua karakter sebagai bagian dari string, bukan sebagai kelanjutan garis.

Jadi string mentah tidak 100% mentah, masih ada beberapa pemrosesan backslash yang belum sempurna.

Oefe
sumber
20
Oh wow ... itu aneh. Tangkapan bagus. Masuk akal bahwa r '\' '== "\\'" tetapi masih aneh bahwa karakter pelarian memiliki efek tanpa menghilang.
cdleary
2
@ihightower ini mungkin berfungsi untuk jalur sistem file, tetapi ada kegunaan lain dari backslash. Dan untuk jalur sistem file, jangan hardcode pemisah. Gunakan 'os.path.sep', atau lebih baik fitur tingkat lebih tinggi dari 'os.path'. (Atau 'pathlib', bila tersedia)
oefe
5
Catatan: Solusinya adalah menggunakan concatentation literal yang berdekatan. r"foo\bar\baz" "\\"(bungkus parens jika ambigu) akan membuat literal tunggal pada waktu kompilasi, bagian pertama adalah mentah, dan hanya bit kecil terakhir yang tidak mentah, untuk memungkinkan backslash tertinggal.
ShadowRanger
2
IMO ini hanya menyatakan kembali pertanyaan (apa yang diizinkan / akan bekerja, dan apa yang tidak), tanpa mengatakan mengapa itu dirancang dengan cara ini. Ada entri FAQ yang menjelaskan mengapa (string mentah dirancang untuk tujuan tertentu, dan masuk akal dalam konteks tujuan itu).
ShreevatsaR
3
Apa gunanya string mentah itu? Sepertinya implementasi konsep yang teduh.
Matthew James Briggs
100

Seluruh kesalahpahaman tentang string mentah python adalah bahwa sebagian besar orang berpikir bahwa backslash (dalam string mentah) hanyalah karakter biasa seperti yang lainnya. Bukan itu. Kunci untuk memahami adalah urutan tutorial python ini:

Ketika awalan ' r ' atau ' R ' hadir, karakter mengikuti garis miring terbalik dimasukkan dalam string tanpa perubahan, dan semua garis miring terbalik dibiarkan dalam string

Jadi setiap karakter yang mengikuti garis miring terbalik adalah bagian dari string mentah. Setelah parser memasukkan string mentah (bukan Unicode) dan menemukan backslash, ia tahu ada 2 karakter (backslash dan char yang mengikutinya).

Cara ini:

r'abc \ d ' terdiri dari a, b, c, \, d

r'abc \ 'd' terdiri dari a, b, c, \, ', d

r'abc \ '' terdiri dari a, b, c, \, '

dan:

r'abc \ ' terdiri dari a, b, c, \,' tetapi tidak ada kutipan terminating sekarang.

Kasus terakhir menunjukkan bahwa menurut dokumentasi sekarang parser tidak dapat menemukan kutipan penutup karena kutipan terakhir yang Anda lihat di atas adalah bagian dari string yaitu backslash tidak dapat terakhir di sini karena akan 'melahap' string penutupan string char.

Artur
sumber
8
Ini sebenarnya lebih jelas daripada jawaban yang diterima. Kerusakan yang bagus.
Fisikawan Gila
4
saya juga menemukan ini jauh lebih jelas daripada jawaban yang diterima, dan saya juga kebetulan seorang fisikawan
xdavidliu
22

Begitulah adanya! Saya melihatnya sebagai salah satu cacat kecil di python!

Saya tidak berpikir ada alasan bagus untuk itu, tapi itu jelas tidak parsing; sangat mudah untuk mengurai string mentah dengan \ sebagai karakter terakhir.

Tangkapannya adalah, jika Anda membiarkan \ menjadi karakter terakhir dalam string mentah maka Anda tidak akan dapat menempatkan "di dalam string mentah. Tampaknya python pergi dengan membiarkan" alih-alih membiarkan \ sebagai karakter terakhir.

Namun, ini seharusnya tidak menimbulkan masalah.

Jika Anda khawatir tidak dapat dengan mudah menulis jalur folder Windows seperti c:\mypath\kemudian khawatir tidak, karena, Anda dapat mewakili mereka sebagai r"C:\mypath", dan, jika Anda perlu menambahkan nama subdirektori, jangan lakukan itu dengan rangkaian string, karena toh itu bukan cara yang tepat untuk melakukannya! menggunakanos.path.join

>>> import os
>>> os.path.join(r"C:\mypath", "subfolder")
'C:\\mypath\\subfolder'
Hasen
sumber
2
Bahan pendukung yang bagus. :-) Pendukung Iblis: kadang-kadang Anda ingin membedakan jalur file dari jalur direktori dengan menambahkan pemisah jalur. Hal yang menyenangkan tentang os.path.join adalah ia akan menciutkan mereka: menegaskan os.path.join ('/ home / cdleary /', 'foo /', 'bar /') == '/ home / cdleary / foo / bar / '
cdleary
Itu tidak membuat perbedaan (teknis)! os.path.isdir akan memberitahu Anda apakah jalur tertentu adalah direktori (folder)
Hasen
2
Yap, itu hanya untuk menunjukkan kepada seseorang yang membaca kode apakah Anda mengharapkan path menjadi direktori atau file.
cdleary
Konvensi windows adalah bahwa file memiliki ekstensi, selalu. sama sekali tidak mungkin (dalam keadaan normal) memiliki file teks dengan lintasan seperti c: \ path \ data
hasen
5
..atau Anda dapat mewakili mereka sebagai "c: / mypath" dan melupakan kesengsaraan backslash Anda sama sekali :-)
John Fouhy
14

Agar Anda dapat mengakhiri string mentah dengan garis miring, saya sarankan Anda dapat menggunakan trik ini:

>>> print r"c:\test"'\\'
test\
Charles Beattie
sumber
14

Trik lain adalah dengan menggunakan chr (92) saat mengevaluasi ke "\".

Saya baru-baru ini harus membersihkan serangkaian garis miring terbalik dan berikut ini caranya:

CleanString = DirtyString.replace(chr(92),'')

Saya menyadari bahwa ini tidak menangani "mengapa" tetapi utas menarik banyak orang mencari solusi untuk masalah segera.

Geekworking
sumber
Tetapi bagaimana jika string asli berisi garis miring terbalik?
Joseph Redfern
2
chr (92) sangat tidak jelas, mungkin lebih baik untuk digunakan "\\"(string non-mentah dengan backslash)
clemep
9

Karena \ "diizinkan di dalam string mentah. Maka tidak dapat digunakan untuk mengidentifikasi akhir string literal.

Mengapa tidak berhenti mengurai string literal ketika Anda menemukan yang pertama "?

Jika itu masalahnya, maka \ "tidak akan diizinkan di dalam string literal. Tapi itu.

Brian R. Bondy
sumber
1
Persis. Desainer Python kemungkinan mengevaluasi kemungkinan dua alternatif: urutan dua karakter di \"mana saja dalam string mentah yang dikutip ganda, OR \ pada akhir string mentah yang dikutip ganda. Statistik penggunaan harus mendukung urutan dua karakter di mana saja vs. urutan satu karakter di akhir.
Hobs
3

Alasan mengapa r'\'sintaksis salah adalah bahwa meskipun ekspresi string adalah mentah, tanda kutip yang digunakan (tunggal atau ganda) selalu harus melarikan diri karena mereka akan menandai akhir dari kutipan sebaliknya. Jadi jika Anda ingin mengekspresikan kutipan tunggal di dalam string kutipan tunggal, tidak ada cara lain selain menggunakan \'. Hal yang sama berlaku untuk tanda kutip ganda.

Tapi Anda bisa menggunakan:

'\\'
Gumbo
sumber
4
Tidak menjawab 'mengapa' :-)
cdleary
2

Pengguna lain yang sejak itu menghapus jawaban mereka (tidak yakin apakah mereka ingin dikreditkan) menyarankan bahwa perancang bahasa Python mungkin dapat menyederhanakan desain parser dengan menggunakan aturan parsing yang sama dan memperluas karakter yang melarikan diri ke bentuk mentah sebagai renungan. (jika literal ditandai sebagai mentah).

Saya pikir itu ide yang menarik dan saya memasukkannya sebagai komunitas wiki untuk anak cucu.

cdleary
sumber
Tetapi mungkin membiarkan Anda menghindari memiliki dua jalur kode string-literal-parser terpisah.
cdleary
2

Terlepas dari perannya, bahkan string mentah tidak dapat berakhir dengan backslash tunggal, karena backslash lolos dari karakter kutipan berikut ini - Anda masih harus melarikan diri dari karakter kutipan di sekitarnya untuk menanamkannya dalam string. Artinya, r "... \" bukan string literal yang valid — string mentah tidak dapat berakhir dengan jumlah garis miring terbalik yang ganjil.
Jika Anda harus mengakhiri string mentah dengan backslash tunggal, Anda bisa menggunakan dua dan memotong yang kedua.

pawandeep singh
sumber
1

Datang dari C, cukup jelas bagi saya bahwa satu \ berfungsi sebagai karakter pelarian yang memungkinkan Anda untuk menempatkan karakter khusus seperti baris baru, tab, dan kutipan ke dalam string.

Itu memang melarang \ sebagai karakter terakhir karena ia akan lolos dari "dan membuat parser tersedak. Tetapi seperti yang ditunjukkan sebelumnya \ adalah legal.


sumber
1
Ya - inti masalahnya adalah bahwa string mentah memperlakukan \ sebagai literal alih-alih dimulainya urutan pelarian. Yang aneh adalah bahwa ia masih memiliki properti pelolosan untuk mengutip, meskipun diperlakukan sebagai karakter literal.
cdleary
1

beberapa tips:

1) jika Anda perlu memanipulasi backslash untuk path maka modul standar python os.path adalah teman Anda. sebagai contoh :

os.path.normpath ('c: / folder1 /')

2) jika Anda ingin membangun string dengan garis miring terbalik di dalamnya TETAPI tanpa garis miring terbalik di akhir string Anda, maka string mentah adalah teman Anda (gunakan awalan 'r' sebelum string literal Anda). sebagai contoh :

r'\one \two \three'

3) jika Anda perlu awalan string dalam variabel X dengan backslash maka Anda dapat melakukan ini:

X='dummy'
bs=r'\ ' # don't forget the space after backslash or you will get EOL error
X2=bs[0]+X  # X2 now contains \dummy

4) jika Anda perlu membuat string dengan garis miring terbalik di akhir kemudian gabungkan tip 2 dan 3:

voice_name='upper'
lilypond_display=r'\DisplayLilyMusic \ ' # don't forget the space at the end
lilypond_statement=lilypond_display[:-1]+voice_name

sekarang lilypond_statement berisi "\DisplayLilyMusic \upper"

python hidup panjang! :)

n3on


sumber
1
Tidak satu pun dari ini menjawab pertanyaan "mengapa", tetapi # 3 dan # 4 tidak boleh digunakan. Mengiris dan menambahkan string umumnya merupakan praktik yang buruk, dan Anda harus memilih r '\ dummy' untuk # 3 (yang berfungsi dengan baik) dan '' .join ([r '\ DisplayLilyMusic', r '\ upper']) ke # 4.
cdleary
1
Alasannya adalah string tidak dapat diubah dan setiap slice / concatenation membuat objek string baru yang tidak dapat diubah yang biasanya dibuang. Lebih baik untuk mengumpulkan mereka semua dan bergabung dengan mereka bersama-sama dalam satu langkah dengan str.join (komponen)
cdleary
Oh, wah - salah paham apa yang Anda maksudkan untuk # 3. Saya pikir ada '\\' + X sederhana lebih disukai untuk membuat string hanya untuk mengirisnya.
cdleary
Temukan saja os.path.normpathakan menghapus backslash tailing ... Lalu bagaimana saya harus menggabungkan nama file ke jalan ...
Jing He
0

Saya mengalami masalah ini dan menemukan solusi parsial yang baik untuk beberapa kasus. Meskipun python tidak dapat mengakhiri string dengan backslash tunggal, itu dapat diserialisasi dan disimpan dalam file teks dengan backslash tunggal di akhir. Karena itu jika yang Anda butuhkan adalah menyimpan teks dengan backslash tunggal di komputer Anda, itu mungkin:

x = 'a string\\' 
x
'a string\\' 

# Now save it in a text file and it will appear with a single backslash:

with open("my_file.txt", 'w') as h:
    h.write(x)

BTW itu tidak bekerja dengan json jika Anda membuangnya menggunakan perpustakaan json python.

Akhirnya, saya bekerja dengan Spyder, dan saya perhatikan bahwa jika saya membuka variabel dalam editor teks spider dengan mengklik ganda namanya dalam variabel explorer, itu disajikan dengan backslash tunggal dan dapat disalin ke clipboard dengan cara itu (bukan sangat membantu untuk sebagian besar kebutuhan tetapi mungkin untuk beberapa ..).

BossaNova
sumber