Pythonの生の文字列リテラルが単一のバックスラッシュで終了できないのはなぜですか?


178

技術的には、ドキュメントで説明されいるようにバックスラッシュの奇数。

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

パーサーは生の文字列内のバックスラッシュを通常の文字として扱うことができるようです(それは、生の文字列が何であるかということではありませんか?)。


8
これはよくある質問のように見えます。あなたが質問したとき、そうでなかったかもしれません。私はあなたが引用したドキュメントがほとんど同じことを言っているのを知っていますが、私はドキュメントの別のソースを追加すると思いました。
oob

回答:


124

その理由は、私が太字で強調したそのセクションの部分で説明されています。

文字列の引用符はバックスラッシュでエスケープできますが、バックスラッシュは文字列に残ります。たとえば、r"\""は、円記号と二重引用符の2つの文字で構成される有効な文字列リテラルです。r"\"は有効な文字列リテラルではありません(未加工の文字列でも奇数のバックスラッシュで終わることはできません)。特に、生の文字列は単一のバックスラッシュで終了できません(バックスラッシュは次の引用文字をエスケープするため)。また、単一のバックスラッシュとそれに続く改行は、行の継続としてではなく、文字列の一部としての2つの文字として解釈されることにも注意してください。

したがって、生の文字列は100%生ではなく、基本的なバックスラッシュ処理がまだ残っています。


20
あらすごい…それは変だ。ナイスキャッチ。r '\' '== "\\'"であることは理にかなっていますが、エスケープ文字が消えずに効果を持つことはまだ奇妙です。
cdleary 2009年

2
@ihightowerこれはファイルシステムパスで機能する可能性がありますが、バックスラッシュの他の使用法があります。また、ファイルシステムパスの場合は、セパレータをハードコードしないでください。「os.path.sep」、または「os.path」のより高レベルの機能を使用します。(または 'pathlib'(使用可能な場合))
oefe '11

5
注:回避策は、隣接するリテラル連結を使用することです。r"foo\bar\baz" "\\"(あいまいな場合は括弧で囲む)は、コンパイル時に単一のリテラルを作成します。その最初の部分は未加工で、最後の小さなビットだけが未加工ではないため、末尾にバックスラッシュを付けることができます。
ShadowRanger 2016年

2
IMOは、なぜこれがこのように設計されているのかは言うまでもなく、問題(許可されている/機能するもの、および機能しないもの)を再言しています。その理由を説明するFAQエントリがあります(生の文字列は特定の目的のために設計されており、その目的のコンテキストでは理にかなっています)。
ShreevatsaR 2018

3
それでは生の文字列のポイントは何ですか?コンセプトの怪しい実装のようです。
マシュージェームスブリッグス

100

Pythonの生の文字列についての全体的な誤解は、ほとんどの人が(生の文字列内の)バックスラッシュは他のすべてと同じように単なる通常の文字であると考えているということです。そうではない。理解する鍵は、このpythonのチュートリアルシーケンスです。

' r 'または ' R 'プレフィックスが存在する場合、バックスラッシュに続く文字は変更されずに文字列に含まれ、すべてのバックスラッシュは文字列に残ります

したがって、バックスラッシュに続く文字すべて、未加工の文字列の一部です。パーサーが生の文字列(非Unicode文字列)を入力してバックスラッシュを検出すると、2文字(バックスラッシュとそれに続く文字)があることがわかります。

こちらです:

r'abc \ d 'a、b、c、\、dで構成されます

r'abc \ 'd'a、b、c、\、 '、dで構成されます

r'abc \ ''a、b、c、\、 'で構成されます

そして:

r'abc \ 'a、b、c、\、'で構成されていますが、現在、終了引用符はありません。

最後のケースは、ドキュメントによれば、パーサーは上記の最後の引用符が文字列の一部であるため、最後の引用符を見つけることができないことを示しています。


8
これは実際に受け入れられた答えよりも明確です。いい内訳。
マッド物理学者

4
また、これは受け入れられた回答よりもかなり明確であり、物理学者でもあります
xdavidliu

22

そういうものだ!私はそれをpythonのこれらの小さな欠陥の1つと見なしています!

それには正当な理由があるとは思いませんが、それは間違いなく解析ではありません。\を最後の文字として生の文字列を解析するのは本当に簡単です。

キャッチは、生の文字列の最後の文字に\を許可すると、 "を生の文字列内に入れることができなくなります。Pythonは、最後の文字として\を許可する代わりに、許可を許可したようです。

ただし、これで問題が発生することはありません。

Windowsフォルダーのパスを簡単に作成できないのではないかと心配している場合c:\mypath\は、心配する必要はありません。たとえば、次のように表すことができr"C:\mypath"ます。サブディレクトリ名を追加する必要がある場合は、文字列の連結を使用しないでください。とにかくそれを行うには正しい方法ではありません!使用するos.path.join

>>> import os
>>> os.path.join(r"C:\mypath", "subfolder")
'C:\\mypath\\subfolder'

2
良い付属資料。:-)しかし、Devilの提唱者:パス区切り文字を追加して、ファイルパスとディレクトリパスを区別したい場合があります。os.path.joinの良い点は、それらを折りたたむことです:assert os.path.join( '/ home / cdleary /'、 'foo /'、 'bar /')== '/ home / cdleary / foo / bar / '
cdleary 2009年

ただし、(技術的な)違いはありません!os.path.isdirは、特定のパスがディレクトリ(フォルダ)であるかどうかを通知します
09年

2
うん、それはパスをディレクトリまたはファイルであると予想するかどうかをコードを読んでいる誰かに示すだけです。
cdleary 2009年

Windowsの規約では、ファイルには常に拡張子が付いています。\パス\データ:これは、Cのようなパスを持つテキストファイル持っている(通常の状況下では)まったくそうではありません
HASEN

5
..または、それらを「c:/ mypath」として表すことができ、バックスラッシュの問題を完全に忘れる:-)
John Fouhy


14

もう1つのトリックは、「\」に評価されるchr(92)を使用することです。

私は最近、バックスラッシュの文字列をきれいにする必要があり、次のことがうまくいきました:

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

これは「なぜ」を処理しないことを私は理解していますが、スレッドは差し迫った問題の解決策を探している多くの人々を魅了しています。


しかし、元の文字列にバックスラッシュが含まれている場合はどうなりますか?
Joseph Redfern、2012年

2
chr(92)はひどくあいまいで、おそらく使用する方が良い"\\"(バックスラッシュ付きの未処理文字列)
clemep

9

\ "は生の文字列内で許可されているため、文字列リテラルの終わりを識別するために使用することはできません。

最初の「」に遭遇したときに文字列リテラルの解析を停止しないのはなぜですか?

その場合、\ "は文字列リテラル内では許可されませんが、許可されます。


1
丁度。Python設計者は、2つの代替案の可能性を評価した可能性があり\"ます。使用統計では、最後の1文字のシーケンスではなく、どこでも2文字のシーケンスを優先する必要があります。
ホブ2012年

3

r'\'構文が正しくない理由は、文字列式が未加工であっても、引用符の終わりを示すため、使用されている引用符(シングルまたはダブル)は常にエスケープする必要があるためです。したがって、単一引用符で囲まれた文字列内で単一引用符を表現したい場合は、使用する以外に方法はありません\'。同じことが二重引用符にも当てはまります。

しかし、あなたは使うことができます:

'\\'

4
「なぜ」に答えない:-)
cdleary 2009年

2

その後、回答を削除した別のユーザー(クレジットを付与するかどうかは不明)は、Python言語の設計者が同じ構文解析ルールを使用し、エスケープされた文字を生の形式に後付けとして展開することで、パーサーの設計を簡略化できる可能性があることを示唆しました(リテラルが未加工としてマークされている場合)。

面白いアイデアだと思って、後世のコミュニティwikiに含めています。


ただし、2つの個別の文字列リテラルパーサーコードパスを使用しないようにすることができます。
cdleary 2009年

2

バックスラッシュは次の引用文字をエスケープするため、その役割にもかかわらず、未加工の文字列であっても単一のバックスラッシュで終了することはできません。文字列に埋め込むには、周囲の引用文字をエスケープする必要があります。つまり、r "... \"は有効な文字列リテラルではありません。生の文字列を奇数のバックスラッシュで終了することはできません。
生の文字列を単一のバックスラッシュで終了する必要がある場合は、2つ使用して2番目のスラッシュを切り捨てます。


1

Cから来ると、単一の\がエスケープ文字として機能し、改行、タブ、引用符などの特殊文字を文字列に入れることができることは明らかです。

実際には、\をエスケープしてパーサーを窒息させるので、\を最後の文字として使用できません。


1
ええ-問題の核心は、生の文字列がエスケープシーケンスの開始ではなく、\をリテラルとして扱うことでした。奇妙なことに、リテラル文字として扱われるにもかかわらず、引用のためのエスケーププロパティがまだあります。
cdleary 2009年

1

いくつかのヒント :

1)パスのバックスラッシュを操作する必要がある場合は、標準のPythonモジュールos.pathが役立ちます。例えば ​​:

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

2)バックスラッシュを含む文字列を作成したいが、文字列の最後にバックスラッシュを付けない場合、生の文字列が好都合です(リテラル文字列の前に 'r'プレフィックスを使用します)。例えば ​​:

r'\one \two \three'

3)変数Xの文字列の前にバックスラッシュを付ける必要がある場合、これを行うことができます:

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)末尾にバックスラッシュを含む文字列を作成する必要がある場合は、ヒント2と3を組み合わせます。

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

今、lilypond_statementには "\DisplayLilyMusic \upper"

長いpythonを生きる!:)

n3on


1
これらのどれも「なぜ」の質問に答えませんが、#3と#4は使用しないでください。文字列をスライスして追加することは一般に悪い習慣であり、#3(これは正常に動作します)にはr '\ dummy'を、#4には '' .join([r '\ DisplayLilyMusic'、r '\ upper'])を優先する必要があります。
cdleary 2009年

1
理由は、文字列は不変であり、スライス/連結ごとに、通常は破棄される新しい不変文字列オブジェクトが作成されるためです。それらをすべて蓄積し、str.join(components)を使用して1つのステップでそれらを結合する方がよい
cdleary 2009年

おっと、おっと#3の意味を誤解した。単純な '\\' + Xは、スライスするために文字列を作成するよりも好ましいと思います。
cdleary 2009年

見つけるだけでos.path.normpath末尾のバックスラッシュが削除されます...次に、ファイル名をパスに連結する方法を教えてください...
Jing He

0

私はこの問題に遭遇し、いくつかのケースに適した部分的な解決策を見つけました。Pythonは単一のバックスラッシュで文字列を終了できないにもかかわらず、シリアル化して、末尾に単一のバックスラッシュを含むテキストファイルに保存できます。したがって、必要なのが1つのバックスラッシュでテキストをコンピューターに保存する場合は、次のことが可能です。

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)

ところで、Pythonのjsonライブラリを使用してダンプすると、jsonで動作しません。

最後に、Spyderを使用しています。変数エクスプローラーで変数の名前をダブルクリックしてスパイダーのテキストエディターで変数を開くと、単一のバックスラッシュが表示され、その方法でクリップボードにコピーできます(そうではありません)。ほとんどのニーズに非常に役立ちますが、一部のニーズにはおそらく役立ちます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.