まずは回答から。
正規表現で「制御文字以外」のチェック - ockeghem(徳丸浩)の日記このうち後ろ二つを正規表現として書くにはどうすればいいかを考えていました。
- 文字エンコーディングの妥当姓
- 制御文字(\x00〜\x1f, \x7f)のチェック
- 文字列長のチェック
こういう時には、「全文字がOKならOK」と考えるのではなく、「一文字でもNGならNG」と考えると楽になります。それは「スペースと非制御文字以外」なのですから、/[^ \S]/
が求めていた正規表現で、=~
ではなく!~
が使うべき演算子ということになります。全角スペースもOKにしたければ、/[^ \x{3000}\S]/
。[追記参照]
#!perl -l use strict; use warnings; use utf8; sub check { $_[0] !~ /[^ \x{3000}\S]/; } print 0+check("妥当な 文字列"); print 0+check("妥当な 文字列"); # fullwidth space print 0+check("妥当な\t文字列"); print 0+check("妥当な 文字列\n");
で、今回の本題です。
残る問題は、後者についてですが、以下のサンプルプログラムは *match* を表示します。正規表現のオプションs, m, msのいずれを追加しても同じ結果でした。
- もっといい方法はないのか?
- Perlの場合末尾の\nがうまくチェックできない
の後者。実のところ、^
と$
は、フラグによって扱いがかわってしまうので利用はさけた方がよいのです。それどころか、.
まで意味がかわってしまうのです。
#!perl use strict; use warnings; my $str = <<EOT; first second third EOT for my $re ( qr/^(.*?)$/, qr/^(.*?)$/m, qr/^(.*?)$/s, qr/^(.*?)$/ms ) { my $s = $str; print "\$re = $re\n"; $s =~ s{$re}{print "<$1>"}eg; print "\n"; }
どうしてこうなってしまうかは宿題として(笑)この問題を抜本的に避けるための Best Practice が以下です。
- 文字列の先頭は、
^
ではなく\A
と指定する。 - 文字列の末尾は、
$
ではなく\z
と指定する。 - モードは常に
ms
にしておく。.
は「改行をのぞく文字全種」ではなく、ただの「文字全種」となる。^
は「行頭」、$
は「行末」の意として使う。
Perl best Practices に載っているものですが、フクロウ本によれば、Javaでも使えるはずの作法です。
困ったことに、JavaScriptはこれをフルサポートしていないのです。mはあってもsはなく、そのため.
が常に「改行を除く全文字」の意味にしかならない。仕方がないので[\s\S]
と書いています。
Dan the Regular Expressionist
naruseさん
今回の話は空白文字でなく制御文字をはじきたいんですよね。違いませんか。
そうか。ならば、話はもっと簡単だったりします。
[Run via codepad]#!perl -l use strict; use warnings; use utf8; sub check { $_[0] !~ /\p{C}/; } print 0+check("妥当な 文字列"); print 0+check("妥当な 文字列"); # fullwidth space print 0+check("妥当な\t文字列"); print 0+check("妥当な 文字列\n"); print 0+check("\x{00}\x{01}\x{02}\x{03}");
最終的には、【「制御文字は原則拒否するけど改行は許す」文字の1文字以上、100文字以下】のような書き方をしたいのです。
nihenさんの\p{Cc}をひっくり返して、/\A[\P{Cc}\r\n]{1,100}\z/ でどうかな〜と思っておりますが、どうでしょうか。