Python文字列リテラルを解析する


9

課題は、Pythonが行うように文字列解析し、文字列の内容を出力することです。

  • 入力(コマンドライン引数またはstdin):文字列リテラル(例:"hello")(または複数のリテラル、以下の文字列リテラル連結を参照)
  • 出力(stdout) :文字列の内容(例えばhello

文字列を解析するためのルール:

  • 文字列リテラルは'a'、一重引用符()、二重引用符("a")、三重単一引用符('''a''')、または三重二重引用符("""a""")の対応するペアで囲まれます。文字列を開いたタイプの引用符の最初の再発は、文字列を終了します。
  • バックスラッシュエスケープ: \'文字列内となり'\"となり"\\なり\。他のバックスラッシュエスケープを実装する必要はありません。エスケープシーケンスの一部ではないバックスラッシュは、バックスラッシュのままです。
  • 文字列リテラルの連結:隣接する文字列リテラルの内容が連結されます。たとえば、に"hello" 'world'なりhelloworldます。
  • 入力には、リテラルの一部ではないスペースが含まれている可能性があります。
  • リテラルの内外で、他の種類の空白をサポートする必要はありません。

追加のルール:

  • evalexecおよび同様のものは、リテラルまたはその一部を解析することはできません
  • 入力は有効であると想定できます。
  • 最大入力長は1023文字と想定できます。

例:

  • "hello" ' world' -> hello world
  • """\"""'\\\A""" -> """'\\A
  • ( '''"""'''"""'''""" ) (括弧なし、ただしスペースあり)-> """'''

最短のコードが勝ちます。


保存できる形式の出力ですか、それとも印刷してそれで完了できますか?
DavidC 2013

@David Printingそれはあなたがする必要があるすべてです。
Florenquake 2013

したがって(たとえば) "\ z"では、バックスラッシュとzを出力するためにコードが特に必要ですか?しかし、\ 'が二重引用符または三重引用符の内側にある場合でも、単なるアポストロフィになりますか?あれは正しいですか?
ブレッドボックス2013

@breadboxその通りです。
flornquake 2013

コードは生の文字列をサポートする必要がありますか?そして、非生の文字列と生の文字列の連結はどうですか?
バクリウ2013

回答:


4

Perl、54文字

#!/usr/bin/perl -p
s/ |("""|'''|"|')((\\?.)*?)\1/$2/g;s/\\(["'\\])/$1/g

これを投稿したときと同じように、Jan DvorakのRubyソリューションとほとんど同じであることに気付きました。実際、私はそれがどれほど似ているかに少し気を配っていますが、私は「偉大な心は同じように考える」と言って、それを手放します。

このプログラムは、Perlスクリプトで文字を数える奇妙なコーナーケースを強調しています。私の読んで、スクリプトに単一引用符が含まれているということは、-pオプションを合計に対して2文字として数える必要があるということです。通常、Perlスクリプトのサイズを計算する場合、オプションの最初のダッシュ文字は、-e適切なプログラムを導入するとバンドルできるという理由で、自由であると見なされますが、余分なエスケープも考慮する必要があります。コマンドラインでスクリプトを入力する必要があります。一重引用符は大量のエスケープを必要とするため、そのペナルティを回避するために、ファイルから実行されるスクリプトとして数える必要がある#!/usr/bin/perlため、無料でを取得しますが、オプション文字は取得しません。少し混乱します。


2
あなたが違うものになりたいなら(('|")\2{2}?)、と同じ長さです("""|'''|"|')
ピーター・テイラー

3

C、178文字

char*p,*q,b[1024];d;main(t){for(p=q=gets(b);*p=*q++;)
d?*p==92&!(*q-*p&&*q-34&&*q-39)?*p++=*q++:*p-d||t&&*q-d|q[1]-d?++p:
(d=0,q+=2*t):*p-32?d=*p,t=*q==d&q[1]==d,q+=2*t:0;puts(b);}

これは、すべてが3項演算子チェーンギャング内で実行されるCソリューションの1つです。

プログラムは、文字を同じバッファーにコピーして戻し、メタ文字を上書きします。d文字列の内側にある場合は区切り文字を保持し、区切りt文字が三重引用符である場合はtrueです。


ループ制御変数の条件付きの追加の増分を含める必要があると思います。'foo \\' bar 'の場合、foo \ ar'が得られます。これは、\\を\に置き換えたように見えますが、次に、新たに入力された\を使用して解析を続行し、次のトークンを\ 'と見なします。
manatwork 2013

実際、その例は無効な入力です。'foo\\'文字列foo \を参照し、その後に空白文字でも文字列区切り文字でもない文字が続きます。
ブレッドボックス2013

おっとっと。そのルールを誤解しました。その後、もちろんあなたのコードは正しいです。
manatwork 2013

3

ルビー、 74 73文字

puts gets.gsub(/('''|"""|'|")((\\?.)*?)\1|./,'\2').gsub /\\([\\'"])/,'\1'

ここでのコアは2つの正規表現です。最初の正規表現は文字列の境界を決定し、内容のみを選択します。変更は、文字列内にないすべてのものを削除するためのものであり、閉じられていない文字列も削除します。バックスラッシュは、possive-optionalの後に何かが続くものとして扱われます。したがって、正規表現エンジンは(\\?.)有効な入力に対してバックトラックしないため(@breadboxに感謝)、単一のバックスラッシュはそこで一致できません。引用は遅延繰り返しによって処理されます。次に、2番目の正規表現は、エスケープ可能な各文字の前にバックスラッシュを取り除きます。正規表現は、常に左端の選択肢を最初に選択することをエンジンに依存しています。

状態マシンのアプローチも検討しましたが、正規表現ソリューションに比べてかなり大きい(19の状態x 4文字クラス)ことがわかりました。興味があれば、ステートマシンを投稿することもできます。


このメソッドの1つのマイナーな不具合: 'foo \\' bar 'は' foo \ 'bar'ではなくfoo \になります。
manatwork 2013

@manatwork書式設定で何かが失われていない限り、これは正しいです。最初のバックスラッシュは2番目のバックスラッシュをエスケープします。'foo\\'最初の文字列でありbar'、入力が文字列コンテキスト外の場合'foo\\'bar'
John Dvorak

おっとっと。以前にどのように計算したかはわかりません。もちろんそれは正しいです。ごめんなさい。
manatwork 2013

これを実行しようとすると、「nested *?+ in regexp」というエラーメッセージが表示されます。必要な最小バージョンまたはランタイムフラグはありますか?
ブレッドボックス2013

@breadbox他のバージョンはチェックしていませんが、ruby 1.9.3(JRuby 1.7.2)を実行しています。少なくとも1.9.3を想定して編集する必要がありますか?
John Dvorak
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.