web-dev-qa-db-ja.com

Pythonの文字列のエスケープシーケンスを処理します

ファイルまたはユーザーから入力を取得すると、エスケープシーケンスを含む文字列を取得することがあります。エスケープシーケンスを処理したいと思います Python文字列リテラルのエスケープシーケンスを処理するのと同じ方法で

たとえば、myStringが次のように定義されているとします。

>>> myString = "spam\\neggs"
>>> print(myString)
spam\neggs

これを行う関数(processと呼びます)が必要です。

>>> print(process(myString))
spam
eggs

関数がPython(上記のリンクの表に記載)のすべてのエスケープシーケンスを処理できることが重要です。

Pythonにはこれを行う機能がありますか?

89
dln385

正しいことは、「string-escape」コードを使用して文字列をデコードすることです。

>>> myString = "spam\\neggs"
>>> decoded_string = bytes(myString, "utf-8").decode("unicode_escape") # python3 
>>> decoded_string = myString.decode('string_escape') # python2
>>> print(decoded_string)
spam
eggs

ASTまたはevalを使用しないでください。文字列コーデックを使用する方がはるかに安全です。

118
Jerub

unicode_escapeは一般的に機能しません

string_escapeまたはunicode_escapeソリューションは一般に機能しないことがわかりました。特に、実際のUnicodeが存在する場合は機能しません。

every非ASCII文字がエスケープされることが確実な場合(および、最初の128文字を超えるものは非ASCIIであることを忘れないでください)、unicode_escapeが正しいことを行います。ただし、文字列に非ASCII文字がリテラルとして含まれていると、問題が発生します。

unicode_escapeは、基本的にバイトをUnicodeテキストに変換するように設計されています。しかし、多くの場所(たとえば、Pythonソースコード)では、ソースデータは既にUnicodeテキストです。

これが正しく機能する唯一の方法は、最初にテキストをバイトにエンコードする場合です。 UTF-8はすべてのテキストの賢明なエンコーディングであるため、動作するはずです。

次の例はPython 3にあるため、文字列リテラルは簡潔ですが、Python 2と3の両方でわずかに異なる症状が現れるという同じ問題が存在します。

>>> s = 'naïve \\t test'
>>> print(s.encode('utf-8').decode('unicode_escape'))
naïve   test

まあ、それは間違っています。

テキストをテキストにデコードするコーデックを使用する新しい推奨方法は、codecs.decodeを直接呼び出すことです。それは役立ちますか?

>>> import codecs
>>> print(codecs.decode(s, 'unicode_escape'))
naïve   test

どういたしまして。 (また、上記はPython 2のUnicodeErrorです。)

unicode_escapeコーデックは、その名前にもかかわらず、すべての非ASCIIバイトがLatin-1(ISO-8859-1)エンコーディングであると想定していることがわかりました。したがって、次のようにする必要があります。

>>> print(s.encode('latin-1').decode('unicode_escape'))
naïve    test

しかし、それはひどいです。これにより、Unicodeがまったく発明されたことがないかのように、256個のLatin-1文字に制限されます。

>>> print('Ernő \\t Rubik'.encode('latin-1').decode('unicode_escape'))
UnicodeEncodeError: 'latin-1' codec can't encode character '\u0151'
in position 3: ordinal not in range(256)

問題を解決するための正規表現を追加する

(驚いたことに、今では2つの問題はありません。)

必要なのは、ASCIIテキストであることが確実なものにのみunicode_escapeデコーダーを適用することです。特に、Pythonテキストであることが保証されている有効なASCIIエスケープシーケンスにのみ適用することを確認できます。

計画では、正規表現を使用してエスケープシーケンスを見つけ、re.subの引数として関数を使用して、エスケープされていない値に置き換えます。

import re
import codecs

ESCAPE_SEQUENCE_RE = re.compile(r'''
    ( \\U........      # 8-digit hex escapes
    | \\u....          # 4-digit hex escapes
    | \\x..            # 2-digit hex escapes
    | \\[0-7]{1,3}     # Octal escapes
    | \\N\{[^}]+\}     # Unicode characters by name
    | \\[\\'"abfnrtv]  # Single-character escapes
    )''', re.UNICODE | re.VERBOSE)

def decode_escapes(s):
    def decode_match(match):
        return codecs.decode(match.group(0), 'unicode-escape')

    return ESCAPE_SEQUENCE_RE.sub(decode_match, s)

そしてそれで:

>>> print(decode_escapes('Ernő \\t Rubik'))
Ernő     Rubik
103
rspeer

python 3:

>>> import codecs
>>> myString = "spam\\neggs"
>>> print(codecs.escape_decode(bytes(myString, "utf-8"))[0].decode("utf-8"))
spam
eggs
>>> myString = "naïve \\t test"
>>> print(codecs.escape_decode(bytes(myString, "utf-8"))[0].decode("utf-8"))
naïve    test

codecs.escape_decodeに関する詳細:

  • codecs.escape_decodeはバイト単位のデコーダーです
  • codecs.escape_decodeは、b"\\n"-> b"\n"b"\\xce"-> b"\xce"などのASCIIエスケープシーケンスをデコードします。
  • codecs.escape_decodeは、バイトオブジェクトのエンコードを気にする必要も、認識する必要もありませんが、エスケープされたバイトのエンコードは、オブジェクトの残りのエンコードと一致する必要があります。

バックグラウンド:

25
user19087

ast.literal_eval 関数が近づきますが、文字列が最初に適切に引用されることが期待されます。

もちろん、バックスラッシュエスケープのPythonの解釈は、文字列の引用方法に依存します("" vs r"" vs u""、三重引用符など)を使用して、ユーザー入力を適切な引用符で囲み、literal_eval。引用符で囲むと、literal_eval数値、タプル、辞書などを返すことから.

ユーザーが文字列をラップしたいタイプの引用符で囲まれていない引用符を入力すると、物事は依然として厄介になるかもしれません。

8
Greg Hewgill

これはそれを行うのに悪い方法ですが、文字列引数に渡されたエスケープされた8進数を解釈しようとすると、うまくいきました。

input_string = eval('b"' + sys.argv[1] + '"')

Evalとast.literal_evalには違いがあることに言及する価値があります(evalは安全性がはるかに低い)。 pythonのeval()とast.literal_eval()の使用を参照してください?

2
LimeTr33

以下のコードは、文字列に表示するために\ nが必要です。

import string

our_str = 'The String is \\n, \\n and \\n!'
new_str = string.replace(our_str, '/\\n', '/\n', 1)
print(new_str)
1