「パスワード暗号化について(CGIスクリプト)」の続きとなっています。文字数が800文字を超えてしまったため、分割させていただきました。
 続いて解読処理です。こっちは更に謎です。

$salt = $logpassword =~ /^\$1\$(.*)\$/ && $1 || substr($logpassword, 0, 2);

 最初は//内の処理です。$1$だけは読めますが、「^」も「(.*)」も最後の$も不明です。「^」はEXORではありませんよね・・・?
 次に&&とやはり||です。この辺は「え?ギャグ?」って感じです(まったく分かってません)。
 最後にパターン結合演算子ですが・・・。「スカラー式をm//、s///、tr///と結びつける」と言われても何のことやら。大体上の表記ではmもsもtrも使ってないんで・・・。マッチmの略形でしょうか?

 ということで長くなってしまいましたが、これらの動作の目的、そして動作原理を教えていただきたく質問させていただきました。
 どうかよろしくお願いします。

このQ&Aに関連する最新のQ&A

A 回答 (3件)

こちらこそよろしくお願いします。


正規表現に付いては、それこそ一冊の本にもなっているくらいなので、あとで
じっくりラクダ本やその「詳説 正規表現」(http://www.oreilly.co.jp/BOOK/regex/)
をご覧いただくといいと思いますが、とりあえず簡単に補足しておきます。

まず「^」ですが、これは「頭から評価していく」という意味ではありません。
「n文字目から…」の方が正解です。

$a = 'Perl is not a Pearl';
$b = 'What is Perl?';
のとき、
$a = /^Perl/; は先頭がPerlで始まるので真
$b = /^Perl/; は先頭がWhatで始まるので偽
$a = /Perl/; はPerlを含むので真
$b = /Perl/; もPerlを含むので真
となります。

「.*」は0回でも一致します。/Pe.*rl/ はPerlにもPearlにもマッチします。
1回以上必ずなにかないといけない場合は/Pe.+rl/と「+」を使います。
上の例で言うとこの正規表現は$aでは真、$bでは偽になります。

さて、「()」は実際のカッコを指しているのではないことにご注意ください。
'This is (not) Perl' =~ /(.*)/ も真ですし、'This is not Perl' =~ /(.*)/
も真です。本来の「()」にマッチさせたければ\を使って /\(.*\)/とします。

ただの「()」は「カッコの中のマッチした文字列をこっそり$1に突っ込んでお
いてください」という意味です。$1の1は、カッコの登場順の番号を表します。
例えば
$date = '2001/5/11 21:25:30';
$date =~ m/^(\d+)\/(\d+)\/(\d+) (\d+):(\d+):(\d+)/;
とすると、$1に「2001」が、$2に「5」が、$3に「11」が…$6に「30」が入り
ます。

なお、「\d」は数字を表す記号です。また「/」はそのまま書くと
m//のスラッシュと区別がつかなくなるので「\/」としてあります。これを
避けるためm//を「/」ではない任意の文字で囲む方法もあります。
$date =~ m@^(\d+)/(\d+)/(\d+) (\d+):(\d+):(\d+)@;
これだと@が区切りになるので「/」はそのままでよくなります。このときは
「m」は省略できません(なんか余計な説明加えちゃってかえって混乱させ
ちゃったかな…)。

> (@log = <IN>;とかforeach(0 .. $#data){}とか)

foreachのかわりにmap {} @data; を使うことを覚えたりするとさらにクセに
なりますよ。
    • good
    • 0
この回答へのお礼

 再度お付き合いいただきありがとう御座います。考察2です(笑)。

 なんかやたらと手強い正規表現ですが(最初の頃は謎の羅列でしたが)、片鱗をちょっと垣間見させていただいました。

$logpassword =~ /^\$1\$(.*)\$/

仰る通りに上記を解読してみると・・・。
 まず、基本的に「$1$何か$」という文字列を評価し、「何か」の部分以外がマッチすれば、「.*」であるために「何かの部分が空文字列」でもひとまず真ということですね。
 それで、「/^$1$/」の形であるために「最初が$1$のもの」を評価することになると。
 その後、()があるので()内でマッチするものがあれば$1に代入しろ、と。ここでは()内が「.*」つまり「なんでもいい」ということなんで、結局「$1$」と「$」に挟まれた部分の文字列を$1に代入せよ、と言うことなんですね。なんで$1かっていうのは、()が1つ目だからであると。

 ちょっと意訳しすぎてるところもありますが、おおざっぱに言うとこの正規表現の形はこんな感じでOKですかね?

 ただ、またちょっと疑問というか確認したいことは、「^」はつまり「文字列の0文字目から」という意味になるのでしょうか?だとすると、n文字目の場合はどうなるんでしょうか?リファレンスにも書いてないようなんで・・・。まぁ、これは後学だから別の本を参照すべきですね。

 ¥のエスケープについてはちょっとかじってるのでなんとか理解できました。取得したIPを分けるときなんかにsplit(/\./,$ENV{'りもーとあど'})とするのと一緒ですね。
 dは・・・。フォーマットの時のsprintf("%.3d",$data)みたいなものでしょうか?C言語では、まぁdは整数fは小数点とかありましたけど、あれと一緒ですかね?表記方法も一緒っぽいし。

 んで合ってたら結論ですが、結局この質問って「正規表現なんたら」の問題だったわけですね・・・。CGIとか暗号解読とか、全然関係ないし・・・。正規表現恐るべし、祖国統一万歳ですね。

 それじゃ、これで(この質問は)最後かと思いたいんですが、確認をよろしくお願いします(メールじゃないんでもちろんどなたでも。是非お願いします)。

お礼日時:2001/05/12 03:44

そういう解釈でよろしいと思います。



「n文字目」についてですが、/^.{n}\$1\$/ ですね。{}で繰り返す数を
指定します。でもあまり使うことはないですね。固定で先頭から何文字か
わかってる場合はsubstrで切り出したりできますしね。
substr($xxx, n) =~ /^\$1\$/ ともできます。…でも書いてないのは
リファレンスとしていかがなものか。

dはdecimal, 10進数の略です。sprintfのものも同じ語源だと思います。

Perlの正規表現はさらにものすごく多機能です。ゆっくり覚えていくことを
お薦めします。
    • good
    • 0
この回答へのお礼

 どうもありがとう御座いました。複数回に渡って丁寧に説明して下さり、とてもわかりやすく勉強になりました。
 教えて下さったことをしっかり頭にたたき込んで、これからも勉強していきたいと思います。
 今回は本当に助かりました。心より御礼申し上げます。

悠夜

お礼日時:2001/05/14 09:29

おっしゃるとおり//はm//の省略形です。

ですから「^」は、正規表現の
「文字列先頭」をあらわす記号です。よってこの部分は
「$logpasswordが '$1$' で始まり0以上の何文字かが続き$が来るような文字
列か?」
といったことを調査しています。
&&や||は、論理演算子ですね。A && Bで
「Aを評価(実行するってことです)して、真なら続けてBを評価してその結果を
返す。Aが偽ならBには一切感知せず偽を返す」
ということになり、A || Bで
「Aを評価して、真ならBには一切感知せずその結果を返す。Aが偽なら引き続
きBを評価してその結果を返す。」
ということになります。

上記の意味は総じて以下のようなことなんじゃないでしょうか。

「$logpasswordがこれこれのような文字列で、しかも途中の(.*)部分が
真(つまり空文字列じゃない)だったときはその(.*)部分を、
そうじゃないときは$logpasswordの先頭から2文字を取り出して、
saltに入れてください」

if文で書きなおすと上記の式は以下のようなことに
なると思います。
if ($logpassword =~ m/^\$1\$(.*)\$/) {
if (defined($1) && $1 ne '') {
$salt = $1;
} else {
$salt = substr($logpassword, 0, 2);
}
} else {
$salt = substr($logpassword, 0, 2);
}

まあたしかに初めてご覧になった方にはわかりやすいものではないでしょうが
…あんまりPerlをいじめないであげてください。&&や||をこのように使うのは
Cでもshellでもいろんな言語にもありまして、UNIX関係者には馴染みがある用法です。
lispなどではむしろifよりもこちらのほうが標準的です。
馴れると最初のような式の方が上記ifを使ったものより読みやすくなったりし
ます。

もちろん、「おれはifで書いた方がいいぜ!」とお思いになるのでしたら
誰にかまうことなくそうお書きになればよろしいでしょう。
どう書いてもいいのがPerlです。

この回答への補足

 早速のご回答ありがとう御座います。とてもわかりやすい説明で助かりました。でももうちょっと分からないところがあるので、迷惑ついでにお願いいたします。

 さて、まず残った疑問点なんですが、やはり、

「$logpassword =~ m/^\$1\$(.*)\$/」

の式ですね。
 ひとまずm//がマッチ演算子だということが理解できました。そして「^」ですが、これは要するに文字列の先頭から評価していくんだ、ということで、いいんですかね?初心者の目で見ると、変数の中身の評価なんていつでも最初から始まるじゃないかと思うんですが・・・。これはそういうことではなく、「n文字目から評価する場合もあるので、それじゃないということを明示している」ということなのでしょうか?

 1,次に(.*)ですが、これは「パターンの中の()で囲んだ部分」ですよね。「.*」は要するに「任意の1文字の0回以上の繰り返し(0回以上ってことは、全く何もない時でも一応評価する、ということでしょうか、それとも0は真でも空文字は偽、を示すのでしょうか)」を示していて、結局は「マッチ成功時に、最初の1文字($1)を引っこ抜いて$saltに代入する」と読んで正解なのでしょうか?
 ・・・なんか何言ってるのか分からないですね(汗)。

 2,そういうことじゃなく、「$_の最初が$1$で始まって途中がどーでもよくて、最後に$が来るっていう文字列だった場合にはその途中の部分を抜き出して$1に代入するんだ」ってことなんですかね。

 ・・・意見が上の2つに分かれています。僕の中で(汗)。
 できれば2番が正解であって欲しいんですが・・・(1番は謎に包まれています)。
 2番が正解なら、「if (defined($1) && $1 ne '') 」の部分も「$1がひとまずなんらかの文字なら」の一言で決着が付くんですよね。

 という感じで考察してみました。いかがなものでしょうか?

 ちなみに、個人的にPerlは好きです。Cをちょっとかじっていたので、Perlの謎の破壊力にちょっと驚いているところです(@log = <IN>;とかforeach(0 .. $#data){}とか)。
 今回、「if($data){}ってなんなんだ!?」ってことを理解した(真と偽について初めて知った)ときのような感じを論理演算子でまた味わいました。慣れるとついつい使ってしまうような表現ですね。面白いです。
 というわけで、また何かしら多分きっとお世話になると思うので今後ともよろしくお願いします。

補足日時:2001/05/11 16:07
    • good
    • 0

このQ&Aに関連する人気のQ&A

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人が検索しているワード

このQ&Aと関連する良く見られている質問

Qperlスクリプト s/^\s+//;  s/\s+$//;  return wantarray ? @out : $out[0]; について

自作の掲示板を作ろうと思い、perlの勉強をしている者です。人様の作ったスクリプトを解析しています。以下のスクリプトはライブラリに記述されていたものです。


sub tttt {
my @out = @_;
for (@out) {
s/^\s+//;
s/\s+$//;
}
return wantarray ? @out : $out[0];


このスクリプトなんですが、 s/^\s+//; の部分の「+」と s/\s+$//; の部分の「+$」、 また「return wantarray~」 の三つの部分のスクリプトが、どういった働きをしているの分かりません。専門書やウェブ上のリファレンスも色々調べたのですが・・。

分かる方いらっしゃいましたらご教授下さると幸いです。よろしくお願いします。

Aベストアンサー

まず前2つの「+」は,正規表現における,「直前の表現を一回以上繰り返し」をあらわします.
「\s」は空白文字一文字を表す正規表現ですので,「\s+」は,「一文字以上の空白文字」になります.
次に,最初の「^」と2番目の「$」は,その正規表現がどこに現れるかを示す記号です.それぞれ,先頭と最後尾にあることを示します.
したがって,「^\s+」は,「最初に空白が一文字以上ある文字列」に,
「\s+$」は「行末に空白が一文字以上ある文字列」にヒットします.
置換構文sはご存知なんですかね.すなわち,この2文で,行の最初と最後の空白を消しているんです.

次に, wantarray ですが,
http://www2u.biglobe.ne.jp/~MAS/perl/ref/wantarray.html
によると,このサブルーチンttttを呼び出すときに,何を返り値にしているかで真偽が決まる関数です.
呼び出す時に配列を希望していたら @out 全体を,変数を希望していたら $out[0]だけを返します.

QPrel正規表現で'$1$'.$saltのあたりが理解できない。

小生Perlを勉強中です。
Perl Codeに以下のようなパスワード暗号処理のサブルーチンが
ありましたが、読めません。教えて下さい。

sub encrypt{
local($inpw)=$_[0];
local(@SALT,$salt,$encrypt);

@SALT=('a'..'z','A'..'Z','0'..'9','.','\');
srand;
$salt=$SALT[int(rand(@SALT)).$SALT[int(rand(@SALT))];
$encrypt=crypt($inpw,$salt)||crypt($inpw,'$1$'.$salt);
return $encrypt;
}

とあります。
特に、下から3行目の($inpw,'$1$'.$salt)が
理解できません。
解説していただければ幸いです。

Aベストアンサー

過去に同様の質問がありました。ctpsysさんの疑問にすべて答えられるかどうかわかりませんが、すくなくとも
>下から3行目の($inpw,'$1$'.$salt)が理解できません
というご質問に対しては参考になるかと思います。

参考URL:http://oshiete1.goo.ne.jp/kotaeru.php3?q=74593,http://oshiete1.goo.ne.jp/kotaeru.php3?q=74593,

Qif($key){ ($key eq 'aaa') || ~~を複数縦に記述したい

sub AAA {my($key)=@_;
if($key){
($key eq "aaa") || ($key eq "bbb") || ($key eq "ccc") || ($key eq "ddd") || &Error("$keyが不正です。");
}
}

のような形があるとして、$key eq "" の数が50個近くあるような場合、横に表示するのではなく縦に
($key eq "aaa") ||
($key eq "bbb") ||
($key eq "ccc") ||
($key eq "ddd") ||
と、見栄えよく表示させたいのですが
どのように記述すればよいのでしょうか。

また代替案などありましたらあわせてご教授頂けますと幸いです。

Aベストアンサー

正規表現で

$key =~ /^(
aaa|
bbb|
ccc|
ddd)$/x || &Error("$keyが不正です。");

Qformデータのデコード「s///」や「tr///」

掲示板CGIの自筆に挑戦中の初心者です。form から受取った データを
デコードするところで、教本やサンプルに必ず出てくる↓これですが、
$value =~ tr/+/ /;
$value =~ s/%([0-9a-fA-F][0-9a-fA-F])/pack("C",hex($1))/eg;
サンプル毎の微妙な違いの意味が消化できず、混乱しています。
(上記引例は杜甫々さんのサイトから拝借しました)

<input type="text">に書いた「+」も1行目で半角sapceになるようですが、
(1)「+」は「+」として残すにはどうするのでしょう?
(2)1行目と2行目の順番が逆だと、何か結果が変わりますか?

htmlタグ不可の処理が良くわからなくて、とりあえず「=~ tr/<>/ /;」
で消しているのですが、<input type="text">に書かれた「<」や「>」を
(3)「<」「>」としてhtmlに書き出すには、どうするのですか?

tr/xx/yy/zz; とか s/xx/yy/zz; の「zz」が良くわかりません。
(4)引例の eg は、yyを実行文と解釈 (e)、かつ何度でも置換 (g) ですか?

ついでに恐縮ですが、#によるコメント化について、
(5)$color="#rrggbb"; とか $target="#ancName"; を書いても大丈夫ですか?

掲示板CGIの自筆に挑戦中の初心者です。form から受取った データを
デコードするところで、教本やサンプルに必ず出てくる↓これですが、
$value =~ tr/+/ /;
$value =~ s/%([0-9a-fA-F][0-9a-fA-F])/pack("C",hex($1))/eg;
サンプル毎の微妙な違いの意味が消化できず、混乱しています。
(上記引例は杜甫々さんのサイトから拝借しました)

<input type="text">に書いた「+」も1行目で半角sapceになるようですが、
(1)「+」は「+」として残すにはどうするのでしょう?
(2)1行目と2行目の順番が...続きを読む

Aベストアンサー

では私は (1) と (2) を。

(1)
上記のスクリプトのままで、「+」は「+」として残ります。
なぜなら、上記 $value =~ tr/+/ /; の個所の $value には
URLエンコードされた文字列が入っていますので、「+」は「%2B」と
あらわされており、「 」(スペース)が「+」とあらわされています。
なので、「+」を「 」に戻してやっているわけです。

(2)
(1) の理由で、順番が逆だと、「+」が全て「 」に変換されてしまいます。

と、こんなところかと。

Q$hts =~ s/##([^#]+)##/$FORM{$1}/g の意味を教えてください!

お世話になります。
perl素人なのですが必要に迫られてWEBで調べながら
ソースを解析していますが、次のコードで完全に止まってしまいました。

$hts =~ s/##([^#]+)##/$FORM{$1}/g

この場合、
#hts から ##([^#]+)## を探して $FORM{$1} に全て置き換えようとしていると思うのですが、以下2点が理解できず困っています。

1.##([^#]+)## の意味
$htsに##で囲まれた文字列が複数あるのでそれら全てを探すということでしょうか?

2.$FORM{$1} の意味
$1は1.で検索した結果だと思いますが、$FORM{ }は一体なんでしょうか?

素人がいきなり解析するのは無謀なのは承知の上ですが、
どうしても業務で必要なので、お知恵をお貸し下さい。
よろしくお願いいたします。

Aベストアンサー

1.
perlを基準に「正規表現」で調べてごらん

2.
同じくperlでHTMLのformデータを受け取る方法を調べてごらん


このカテゴリの人気Q&Aランキング

おすすめ情報