- ベストアンサー
UNIXでのプログラムその2
UNIXでのシェル/AWK/SEDなどで以下のような 処理をおこないたいのですがいい方法を教えて下さい。 以下のような変換をやりたい。 MED1 = ( AMED1 NOT WIN ) interact (( BME1_1 OR ACON ) OR TH1 ) ↓ MED1_1 = AMED1 NOT WIN MED1_2 = BME1_1 or ACON MED1 = MED1_1 interact MED1_2 OR TH1
- みんなの回答 (6)
- 専門家の回答
質問者が選んだベストアンサー
若干修正: No4のソースで、消し忘れの行と冗長部分を削除しました。 #!/bin/awk -f { str = $0 NVAR=0 varbase=$1 #shori str=par_replace(str,varbase) #output for(i=1;i<=NVAR;i++){ print VARNAME[i]" = "VARVAL[i] } print str } function par_replace(str,varbase){ while(match(str,/\([^()]*\)/)){ NVAR++ VARNAME[NVAR]=varbase"_"NVAR VARVAL[NVAR]=substr(str,RSTART+1,RLENGTH-2) sub(/\([^()]*\)/," "VARNAME[NVAR]" ",str) } return str }
その他の回答 (5)
- wolv
- ベストアンサー率37% (376/1001)
PS: 回答1でlintがどうこういいましたが、回答3でnotnotさんが言われたyaccをlintだと勘違いして回答してました。ごめんなさい。 検索しても適当なページが見つからないわけです^^;
- wolv
- ベストアンサー率37% (376/1001)
回答1で書いた仕様とちょっとちがいますが、汎用性のあるスクリプトができました。 「(」「括弧以外の連続」「)」の部分を順次変数に置き換えます。 #!/bin/awk -f { str = $0 NVAR=0 varbase=$1 #shori str=par_replace(str,varbase) for(i=1;i<=NVAR;i++){ VARVAL[ivar]=par_replace(VARVAL[ivar],VARNAME[ivar]) } #output for(i=1;i<=NVAR;i++){ print VARNAME[i]" = "VARVAL[i] } print str } function par_replace(str,varbase){ FLAG=0 while(str ~ /\([^()]*\)/){ match(str,/\([^()]*\)/) NVAR++ VARNAME[NVAR]=varbase"_"NVAR VARVAL[NVAR]=substr(str,RSTART+1,RLENGTH-2) sub(/\([^()]*\)/," "VARNAME[NVAR]" ",str) FLAG=1 } return str } #標準のawkではmatchという関数が使えないかもしれません(手元の本には、matchはnawkで実装されていると書いてあります) 実行例(上記scriptをpar_repl.awkとすると) awk -f par_repl.awk [ENTER] MED1 = ( AMED1 NOT WIN ) interact (( BME1_1 OR ACON ) OR TH1 )[ENTER] MED1_1 = AMED1 NOT WIN MED1_2 = BME1_1 OR ACON MED1_3 = MED1_2 OR TH1 MED1 = MED1_1 interact MED1_3 Ctrl+D (実行例終わり)
- notnot
- ベストアンサー率47% (4900/10358)
数式のコンパイル時の構文解析のようなことをやりたいのであれば、一般には、yacc もしくは bison という字句解析プログラム生成ツールを使います。 それらを使わないでも、perl か ruby あたりを使えば出来るでしょう。方針は#1の書かれた方法でいいと思いますが、多重のカッコ処理が難しそうですね。
- terra5
- ベストアンサー率34% (574/1662)
こういうのをきっちり汎用的に作ると大変なので、 特定パターン専用にいくつか作って、漏れたのを手で修正ってのはよくやります。 だいたいが一時的に必要なだけですので。 とりあえず、 a = ( b ) interact ( ( c ) d ) の形の=,(,),interactをキーに a_1 = b a_2 = c a = a_1 interact a_2 d の形に処理するのをperlで書きましたので、 あとは必要なパターンだけ修正したのを作って使ってください。 正規表現だけわかれば、なおせると思います。 (awkは正規表現のでの置換が非力なので、こういう場合はsedやperl等使います) あと、タブには非対応なので、必要に応じて" *"の部分を"[ \t]*"に変えてください。 while(<>) { if ( $_ =~ /^ *\w *= *[(] *.+ *[)] interact *[(] *[(] *.+ *[)] *.+ *[)] *$/ ) { chop; $v1 = $v2 = $v3 = $v4 = $_; $v1 =~ s/^ *(\w) *=.*$/$1/; $v2 =~ s/^ *\w *= *[(] *(.+) *[)] *interact *.*$/$1/; $v3 =~ s/^ *\w *= *[(] *.+ *[)] *interact *[(] *[(] *(.*) *[)] .*$/$1/; $v4 =~ s/^ *\w *= *[(] *.+ *[)] *interact *[(] *[(] *.* *[)] *(.+) *[)] *$/$1/; printf("%s_1 = %s\n", $v1, $v2); printf("%s_2 = %s\n", $v1, $v3); printf("%s = %s_1 interact %s_2 %s\n", $v1, $v1, $v1, $v4); } else { print $_; } }
- wolv
- ベストアンサー率37% (376/1001)
これは、たぶん「字句解析」だか「構文解析」と呼ばれる処理の一種で、awk・sedなどではなく、lintという処理系を使ってやるべき処理です。 (上記の文に含まれているキーワードを使ってWWWを検索してみましたが、適当なページが見つからなかったので、どこか語句の間違いがあるかもしれません。) awk・sedでもできないことはないでしょうが、やや複雑な処理になると思います。 さて、それでもとにかくawkやsedでやるとして、 実際の処理を具体的にしたいのですが、やりたい処理は、 ・「(」,「)」の前後には空白が省略されていることもありうる。 ・2つめの単語は「=」である。 ・ひとつめの単語を式の名前とみなせる ・式中に「(」「)」の組が出てきたばあいは、「式の名前_数値」という「変数」を新たに定義し、括弧と括弧に囲まれた部分を変数に置き換える。 ・式中に括弧がなくなるまで上記の処理を再帰的に行う。 ということでよいのでしょうか? また、上記の処理を行った場合、 "MED1 = ( AMED1 NOT WIN ) interact (( BME1_1 OR ACON ) OR TH1 ) " は、 "MED1_1 = AMED1 NOT WIN MED1_2_1 = BME1_1 or ACON MED1_2 = MED1_2_1 or TH1 MED1 = MED1_1 interact MED1_2" に変換されることになりますが、それでかまいませんか?
お礼
質問に全部答えたつもりだったのですが、 最後の質問に回答をもらしていました。 ここの書くのもおかしいのですが 他に方法がみつからなかったので、ここに書きます。 出力形式についても上記の形で問題ありません。
補足
ご検討ありがとうございます。 回答します。 ・「(」,「)」の前後には空白が省略されていることもありうる。 →今、処理しようとしているデータは 空白が必ずあるのですが、汎用性を考慮するとどちらでもという方が好ましいです。 ・2つめの単語は「=」である。 →これは必ずそうです。 ・ひとつめの単語を式の名前とみなせる →これも必ずそうです。 ・式中に「(」「)」の組が出てきたばあいは、「式の名前_数値」という「変数」 を新たに定義し、括弧と括弧に囲まれた部分を変数に置き換える。 →これもその通りです。 ・式中に括弧がなくなるまで上記の処理を再帰的に行う。 →その通りです。 お手数かけて申し訳ありませんが、よろしくお願いします。 これができれば大変助かります。
お礼
ばっちり動きました。 感謝!感謝!です。 これを参考にあとは自分なりに拡張させていけそうです。 本当にお世話になりました。