Perlで文字列を一行ずつ処理する方法

Posted at 2006-09-22T11:05:40+09:00 in Coding

Perlで文字列を一行ずつ処理する方法を考えてみた。

Perlで文字列を一行ずつ処理するには、

my $str = '複数行のテキスト';
my @lines = split m{\n}, $str;
foreach my $line ( @lines ) {
    # 一行ずつ処理
}

というのが考えられる。

ただコレだとテキストを配列にしているため テキスト + 配列分のメモリを使う。 なのでちょっと効率が悪い。(ような気がする)

で、別の方法を考えてみて、置換演算子s///を使えばいいんじゃないかと思った。

my $str = '複数行のテキスト';
$str =~ s/^\s*|\s*$//gs;
$str .= "\n";
$str =~ s{^(.*)\n}{
    # 一行ずつ処理
}egm;

コレでできるはず。はずと言ってるのは実際にテストしてないから。

置換演算子s///に修飾子egmをつけているのがポイント。

ただコレが本当に効率がいいかは不明。テキスト + 配列よりは効率はいいはず。 自信ないけど。

なんか間違ってたらお知らせください。

Perlで配列を簡単にハッシュにする方法

Posted at 2006-09-14T14:15:57+09:00 in Coding

本日はPerlに関する小ネタ。というか忘れないようにするメモ。

Perlで配列を簡単にハッシュにするには、

my @array = ( foo, bar, baz );
my %hash = map { $_ => 1 } @array;
# %hash = ( foo => 1, bar => 1, baz => 1 );

みたいにすればいい。誰でも思いつきそうやり方だけど最近まで気づかなかった。 でコレを利用すると配列からユニークな要素を取り出せる。

my @array = ( foo, bar, baz, foo, bar, baz, hoge, huga, moge );
my @array2 = keys map { $_ => 1 } @array;
# @array2 = ( foo, bar, baz, hoge, huga, moge );

++[date:2006/09/14-20:13:00] コメント欄で指摘されましたが上の例は微妙に動きません。

実際には

my @array = ( foo, bar, baz, foo, bar, baz, hoge, huga, moge );
my %hash = map { $_ => 1 } @array;
my @array2 = keys %hash;
# @array2 = ( foo, bar, baz, hoge, huga, moge );

としないとだめっぽいです。

あとコメント欄でfbisさんに教えてもらいましたが、 配列の重複消去は、

my @array = ( foo, bar, baz, foo, bar, baz, hoge, huga, moge );
my @array2 = do { my %t; grep !$t{$_}++ , @array };

でもできるみたいです。詳しくはコメント欄参照。

++

ただしmapsplitgrepなんかを続けすぎるとわけわかめな暗号文になる。

use CGI qw(path_info);
use URI::Escape;
my $path_info = join q{/}, map { &uri_escape($_) }, split q{/}, &path_info();

これはPATH_INFOをスラッシュごとに区切ってURIエスケープしてから、 セパレーターにスラッシュを指定して結合してるんだけど、 だぶんぱっと見ただけでは、分かりづらい。

一行でかけるから書く方は楽なんでけど、他人がこれを管理するとなるとめんどくさそう。 これが十個も続いていたら多分しぬ。

ということで、本日のネタは終了。 なんていうか、初心者向け?

ちなみに、自分もまだ完全にはPerlを理解しきっていなかったりする。

Perlで文字列を一般実体参照や数字文字参照に変換する方法まとめ

Posted at 2006-07-02T14:20:51+09:00 in Coding

しばらく前のエントリPerl で文字列を数字文字参照に変換する方法でも

同じ話題を扱いましたが、出した例、解決策がオーバースペックであまりスマートではなかったので、まとめ直すエントリ。

なお、以下に挙げる例は、UnicodeをサポートしたPerl(Perl 5.8以降)を対象にしたものとなります。

数字文字参照へ変換

数字文字参照へ変換する場合、utf8フラグが重要になってきます。 utf8フラグが立っていないと、うまいこと変換されません。 というか日本語などを扱う場合、utf8フラグは結構重要な気がします。

use Encode;
$str = '日本語文字列'; # 日本語の文字列
$charset = 'utf8';    # 文字列の文字コード
$str = decode($charset, $str); # utf8フラグを立てる
$str =~ s/(.)/ '&#' . ord($1) . ';'/eg; # 十進数で変換
print $str , "\n"; # 日本語文字列 となる

こんな感じです。

徒書の北村さんのエントリPerlで文字列をHTML数値文字参照に変換 でもこれと同じ例と、他の方法が取り上げられているので一読をお勧めします。

一般実体参照へ変換

一般実体参照へ変換する場合HTML::Entities と言うモジュールを使います。

ただし日本語を扱う場合注意が必要で、utf8フラグがたって無いと文字化けします。

use HTML::Entities;
use Encode;
my $str = '<日本語&文字列>';
$str = decode('utf8', $str);
$str = encode_entities($str);
print $str , "\n"; # &lt;&#x65E5;&#x672C;&#x8A9E;&amp;&#x6587;&#x5B57;&#x5217;&gt;となる。

こんな感じです。HTML::Entities使うと一般実体参照への変換は簡単にできますが、 日本語文字列が一緒に数字文字参照へと変換されてしまうので、何らかの工夫が必要だと思われます。

あと、ドキュメントをきちんと読めてないので、なにかを見落としている気がしなくも無いです。

最後に

例として出したコードが間違ってるとか、用語の使い方がおかしいとか、 何か問題があれば、コメントなりトラックバックなりで教えてください。 すぐに訂正します。

&amp;を含む文字列の処理問題

Posted at 2006-06-27T14:18:33+09:00 in Coding

blosxomの自作記法系プラグイン nyarlax を書き直ししていたときに、

&を含む文字列どう処理するかによって、エントリの内容が変わってしまう問題に 直面しました。

というかしばらく前のエントリマークアップがおかしい件 でも同じことが原因になってるわけですが。

Wikiやその他Blogツールの独自記法系プラグインにも同じ問題が含まれているような気がするので、 問題点を整理する意味でもまとめてみるエントリ。

ちなみに例になっているコードは全部Perlです。

何が問題か

いったい何が問題でどういう場合問題が起きるのかというと、 (X)HTMLXMLにCGIなどでプログラムコードを出力ときに プログラムコードの内容が変わってしまうということが起きます。

例えば以下のコードを貼り付けたいとします。

my $str = '&amp;の問題';
$str =~ s/&amp;/&/g;

しかし、処理の仕方によっては、

my $str = '&の問題';
$str =~ s/&/&/g;

のようになってしまいます。なにがどう変わってんの?というと &&になってしまっています。

何故こんなことが起きるのか

上記の問題は以下のコードのような処理を行っていると起こりえます。

# 例1
my $str = '&を含む&lt;文字列&gt;';
$str =~ s/&amp;/&/g;                     # &amp;     => &
$str =~ s/&/&amp;/g;                     # &         => &amp;
$str =~ s/&amp;(\w+);/&$1;/g;            # &amp;gt;  => &gt;
$str =~ s/&amp;#(\d+);/&#$1;/g;          # &amp;#26; => &#26;
$str =~ s/&amp;#x([a-f0-9]+);/&#x$1;/ig; # &amp;#x2f => &#x2f;
# $str = '&amp;を含む&lt;文字列&gt;' となる。

上記のコードのような処理では&という文字列を出力したい場合&amp;と書くことになります。 ようするに&&と書く必要があるわけです。(X)HTMLなんかに貼り付ける場合はそうなります。

が、これはめんどくさい。少なくともCGIなんかで処理する場合はこの手間は省きたいわけです。

あちらが立てばこちらが立たず

じゃあ、一般実体参照数字文字参照として扱わされないようにすればいいじゃないと思うわけです。

そうするには、

# 例2
my $str = '&を含む&lt;文字列&gt;';
$str =~ s/&/&amp;/g; # &         => &amp;
# $str = '&amp;を含む&amp;lt;文字列&amp;gt;' となる。

こんな感じにします。しかし、これだと今度は一般実体参照数字文字参照 が使えなくなってしまいます

&を含む文字列、一般実体参照数字文字参照をどう表示したいのか

問題は要するに、一般実体参照や数字文字参照を単なる文字列として扱いたいのか、 それとも一般実体参照や数字文字参照そのものとして扱いたいのか、ということになります。

プログラムコードであれば前者、普通の文章ならば後者になるわけです。で、実際実装する場合 このあたり考えないと面倒なことになります。というかこの問題どうするか考えるのが面倒だったりしますが。

解決策の例

プログラムコードに前者(単なる文字列として扱う)を適用し、 普通の文章に後者(一般実体参照や数字文字参照そのものとして扱う)を適用する、という実装でもいいと思うんですが、 記法によって扱いが変わる、というのは若干ややこしい気がするので、別の方法を考えてみます。というか 自分のところの実装方法だったりします。

どう言う実装かと言うと、基本的に&&amp;に変換し&そのものを出力するにはバックスラッシュ(*)でエスケープする と言う感じになってます。要は、\&と書くと&*が出力されるという感じ。

ただ、この方法は自分ひとりだけが使う場合いいんですが、第三者が使う場合微妙な気がします。

最後に

文章がおかしいとかわかりづらいとかここは間違っているとかがあったら、 コメントなりトラックバックなりで指摘してください。というか自分でも若干こんがらがってます。 最初なにがどうなってるのかよく分かってませんでした。今でもそうですが。

ようするに&とかを処理しつつ&とかを扱うのはややこしいという話。 こんがらがっているのは自分だけかもしれませんが。

おまけ

そういえば、このBlog(空繰再繰)にアイコン(正確にはfaviconですが)をつけました。 なかなかいい感じ。faviconに対応しているブラウザだと表示されます。

あとIE6(=Internet Explorer 6)でサイドバーが落ちてるのをとりあえず直しました。 が、あのCSSはひどいような。IE6にしか適用されないようになってるけど。

Perl で文字列を数字文字参照に変換する方法

Posted at 2006-06-18T15:15:10+09:00 in Coding

Perl で文字列を HTML --Entities-- ++数字文字参照++ に変換する方法がわからなくて、

いろいろやったり、はてなで質問してみたりした結果、 やり方が分かったのでメモ。

追記:2006-07-02T13:00:00+09:00

Perlで文字列を一般実体参照や数字文字参照に変換する方法まとめ にまとめ直しました。

追記終わり

追記:2006-06-22T18:00:00+09:00

Perlで文字列をHTML数値文字参照に変換 で北村さんに突っ込まれて気づいたので間違いの訂正。

&#26085;のような文字参照の形式は、

HTML Entities ではなく HTML数字文字参照 です。

微妙に勘違いしてました。

URI直したいですが、URI変えるのは困ることが起きそうなのでそのままにしておきます。

あとTrackback受け取るシステムが微妙におかしいです。 直さなくては。

さらに追記。

なんか表記が直ってないので直しました。

これで大丈夫なはず。

追記終了

Perl で文字列を HTML --Entities-- ++数字文字参照++ に変換する方法

追記:2006-06-22T18:00:00+09:00

この例はややオーバースペックらしいです。 詳しくは北村さんのエントリPerlで文字列をHTML数値文字参照に変換 を参照してもらえると助かります。

北村さんの方法のほうがスマートだったりする。

追記終了

結論から言えば Unicode::Escape を使えば できます。

どうやるかのかというと、

use Unicode::Escape;
my $str     = '日本語文字列';
my $charset = 'utf8';         # 文字コード
$str = Unicode::Escape::escape($str,$charset);
$str =~ s/\\u([0-9a-f]+)/&#x$1;/ig; # 16進数
# 10進数の場合こっち
# $str =~ s/\\u([0-9a-f]+)/'&#' . hex($1) . ';'/egi;

こんな感じ。結構簡単だった。

しかし、問題が無いわけではないッ!

何故断言しているのでしょうか(´∀` )?っていうか何で断言できるのでしょうか。

答えは簡単です。Unicode::Escapeは、

サーバによっては入っていません

しかも

インストールがめんどくさい

からです。……言い切っちゃってるよ。いいのかよ。っていうか何なんだこのノリ。

まあそれは置いといて。Nyarla.netのサーバは2006/06/18現在 さくらのレンタルサーバのスタンダードプラン を使っているのですが、Unicode::Escapeは入ってませんでした。

で、PurePerlモジュールだったらFTPでアップロードするだけで使えるようになるんですが、 Unicode::Escapeを動かすために必要なモジュールUnicode::StringXSを利用しているためコンパイルしないとだめなので、telnetでログインして

%perl -MCPAN -e shell
cpan> install Unicode::Escape

してやる必要があります。これがめんどい理由。ちなみにレンタルサーバが シェルログインを許可して無いとできません。

もっとも一度やってしまうとあとは楽なので、問題ないかもしれませんが。 っていうか利用するのにちょっと敷居が高い気がしなくも無いです。 GoogleCPAN レンタルサーバあたりで検索すると 色々出てくるので詳しいことはそのあたりで調べてください。

どうでもいいですが

上のほうで微妙にAA使ってる理由はUnicode::Escapeのテストするためだったりします。 うまくいってるといいけど。

追記2006-06023-10:00:00

現在Unicode::Escapeは数字文字参照のために使ってません。

追記終了

しかし、またしても長いし見づらいような……。CSSがまずいのかな?

About Me

name
Naoki Okamura
nick
nyarla
contact
nyarla[ at ]thotep.net
Category
Log
Banners
Powered by
nim