Perlの最近のブログ記事

ありゃ、MD5のハッシュって、Perl の crypt 関数じゃ作れないのね・・・

つーか、crypt で生成している文字列って、素の MD5 のメッセージダイジェストじゃないのね・・・完全に MD5 や crypt というものの意味を誤解していた(^^;

今作ってるシステムで、データの内容を MD5 でハッシュして送らないといけないのだが、crypt 関数で作ったハッシュデータを送ったら蹴られてもうた(^^;
crypt で作成するのは、あくまで「MD5 でハッシュした値を含んだ、パスワード処理用の特殊な文字列」ということで、素の MD5 のメッセージダイジェストじゃないってことですよね?>識者の人

つーことで、Perl で MD5 のハッシュを作成するには、system 命令で外部コマンドを叩くか(でも、Linux と FreeBSD でコマンド名が違うから現実的ではないか)、MD5 モジュールを使うか。
・・・で、ver5.8以降の Perl であれば、MD5 モジュールは標準でインストールされているので、素直に、

use Digest::MD5 qw/md5_hex/;
$str = "変換する文字列";
$md5 = md5_hex($str);

としちゃうのが王道というわけですな。

POP3 でメールを受信し、添付で送られてきた画像データを抜き出すスクリプトを Perl で書いた。

最初は受信してきたメールを MIME::Parser で解析して添付ファイルを抽出しようと思ってたんだけど、日本語ファイル名が含まれていた時に何か問題あるという話を見つけたので、十年くらい前に自作した Parser を使うことにした。

ちゃんと RFC 見て書いたコードじゃないけど、何年か業務システムで使っていた間に、「OutlookExpress のメールは、Quoted-Printable エンコードなのかよ!」とか、「EdMax の boundary 文字列には、エスケープしないといけない特殊記号がバンバン混ざってるのね」とか、そんなメールソフト毎の微妙なフォーマットの違いや、添付ファイルが多段に展開する HTML メールとか、色々なことに実践的に対応してきたので、大概のメールは問題なく処理出来るようになってるからな・・・と思ってたんだけど・・・

iPhone のメールはまた独特じゃねえ(^^;
結局今回は iPhone 対応が必要じゃった。

iPhone メールで送られてきたメールが他のメールソフトのものと違うところ。

まず一つめ。添付ファイルのヘッダの順番が全然違う。
ほとんどのメールソフトが添付ファイルのスタートを表す boundary 文字列のあと、Content-Type ヘッダが来るんだけど、iPhone(つーか、Apple 製のメールソフトは皆そうなん?)の場合は Content-Disposition ヘッダが来るんだな。

次に、Content-Disposition ヘッダの filename の記述が独特じゃねえ。
普通は(日本語ファイル名なら)
filename="=?ISO-2022-JP?B?GyRCJF4kJCReJCQbKEIxLmpwZw==?="
という形なんだけど、iPhone メールは
filename*=iso-2022-jp''%1B%24B%3CL%3F%3F%1B%28B.jpg
てな形で来るな。
MIME B エンコードじゃなく、URL エンコードしたファイル名が来るんだな。
filename と = の間に * があるのも謎だし。

いや、どっちも致命的な問題じゃなくて、添付ファイルの抜き出しそのものには影響ないんだけど、Content-Type ヘッダをヘッダ情報をログに吐き出す開始トリガーにしてたり、オリジナルのファイル名がセットされているかどうかを正常にヘッダ情報が抜き出せたかの判断材料にしてたりしたので(filename の後ろに * が付いている形は想定してなかったので、オリジナルのファイル名が抜けなかったのだ)、ちょっと変な動きをしてしまった。

まあ、生のメールデータを見れば問題点はすぐわかるので、ささっと対応したが。

こんなこと書くと、「RFC ちゃんと読んでコード書けば問題ないじゃろ」と知ったかぶりに指摘する人がいるんだが、例えば日本のケータイのメールアドレスなんて(天下の NTT DoCoMo からして)RFC なんか無視してるし、実際のデータを解析して対応しないとどうしようもない部分があるんだぜ。

まあ、興味がある人は、色々なメールソフトから送った生のメールデータを見てみると色々楽しいよ。
全てのソフトのデータを問題なく抽出するにはどんな正規表現を書けばいいかとか、パズルみたいで楽しいよお。(笑)

現在、Perl で作ったシステムをお客さんのサーバに移行中だが、MIME-tools の関係がインストールされていないので、

Can't locate MIME/Words.pm in @INC ~

なエラーが発生。

ということで、CPAN モジュールを使って MIME::Words モジュールのインストールを行ったのだが、結局、関連する以下のモジュールのインストールまたはアップデートが発生した。

    IO::Stringy
    Mail::Header
    ExtUtils::MakeMaker
    Mail::Field
    File::Temp
    Mail::Internet
    Date::Format
    Test::Pod
    Date::Parse
    Module::Build
    Pod::Simple
    ExtUtils::CBuilder
    Test::Harness
    ExtUtils::ParseXS
    Pod::Escapes

CPAN モジュール使ったインストールだと、なんだかんだと make test で失敗して手動インストールしないといけないケースも多々発生するので気分が重かったのだが、どれ一つ失敗することなく無事インストール終了。

う~む。これは朝から気分爽快。

無事当該 Perl スクリプトも動くようになって、今日は朝から気持ち良いのであります。ぶひっ。

う~む。(何か、最近この唸り声で始まるパターンが多いな(^^;)

日頃 Perl で正規表現使ってると、ActionScriptで正規表現使うのが面倒くせぇ、面倒くせぇ。(^^;

例えば、日付がセットされている文字列('Fri, 16 May 2010 02:45:00 +0900')から年月日を抜き出したい時、

Perl なら、変数 dateData に日付がセットされていれば、

    if ($dateData =~ /^.+?(\s+)\s(.+?)\s(\d{4})/) {
        $y4 = $3;
        $m2 = $2;
        $d2 = $1;
    }

じゃん(冗長に書いても)。これが ActionScript になると、

    var pattern:RegExp = /^.+?(\s+)\s(.+?)\s(\d{4})/;
    var result:Object = pattern.exec(dateData);
    if (result != null) {
        y4 = result[3];
        m2 = result[2];
        d2 = result[1];
    }

とか。
まあ、オブジェクト指向言語なので仕方ないのだが(^^;、RegExp オブジェクトを使って云々というのが面倒臭いというか、if 文一発で済んじゃう Perl からすると、どうにも冗長な印象が否めない。

あと、null 値を文字変数に突っこむ時には空文字に型キャストしてくれれば良いのに。

マッチングせずに結果に null 値が返ってきた時、そのまま y4 = result[3] しようとしたら、

Cannot access a property or method of a null object reference.

って怒られちまったい。(^^;

まあ、ちゃんと結果を if (result != null) って確認してからセットすれば全然問題ないんだけど(^^;

いやあ、文字、数値の型の区別も無く、正規表現と親和性の高い言語仕様の Perl を使い続けてると、ほんまに別の言語を使うのが面倒くさくなるな。

確かに Perl は自由度が高くて、それ故に「教育しづらい言語」あるいは「社内のコーディング基準を作りづらい言語」なのは事実だと思うが、もう少し「業務システムの構築にも使える言語」として浸透してもよかったのになあと思う。
なんか、VisualBasic と Java しか知らん人間が「Perl はどうやらこうたら」という誤解に満ちた評価を口にしているのを見ると、ムッとする今日この頃である。:-P

そうそう。先週の月曜日はもう一つ悲しいことがあった。

それは、10年も前に作った「Apache のアクセスログをあれこれ集計する Perl プログラム」のバグが発見されたこと。(^^;

今年の 1月分から集計がされてないというお客さんからの一報で調べてみると、ありゃりゃ、substr で「4桁の年の後ろ 2桁」を抜いているのだが、こりゃ、後ろ 1桁しか抜いてないよ。とほほ・・・。

2010年の'0'しか取らずに(その後前ゼロ補完してるので)'00'年(2000年)のログを見にいってる。今までは、例えば昨年なら'09'と取ってるつもりがやっぱり'9'しか取って無くても、その後 2桁に補完する処理を入れていたので、無事'09'年(2009年)のログに行き着いていたというわけだ。だから10年間もバグが発見されることがなかったんだな(^^;;;
テストはしたんだろうけど、2010年のデータまではやってなかったんだろうな。

まあ、今なら substr なんか使わずに正規表現でビシっと抜くんで、こんな失敗はないんだけど。

日付を見たら、2000年3月に「俺が」作ったプログラムだった。(^^;
ちっ、後輩が作ったプログラムだったら散々文句を言ってやろうと思ったのに!

しかし、このプログラムを作った時は、よもや移り変わりの激しい Web の世界で、10年も使い続けられるとは思ってもいなかったよ・・・(^^;

ありり?

UNION で結合する SELECT 文では ORDER BY って使えんのやったっけ?
エラーにはならないのだが、まったく無視されてるな・・・(^^;
もちろん、SELECT 文単体や、UNION した表全体に対する ORDER BY は問題なく使えるのだが。

UNION の場合、重複行を取るためのソートが表全体に対して行われるわけで、それで各表の ORDER BY は無視されるのかと思いきや、UNION ALL でも一緒やね。

はは。日頃 UNION 使わないもんで(^^;、細かい仕様知らなんだ。

A 003
A 001
A 002
B 102
B 101
C 310
C 320
...

みたいなテーブルから抽出したレコードを

B 101
B 102
A 001
A 002
A 003
C 310
C 320

みたいに並べたいんで(最初の項目の名前をd1、2番目の項目をd2とした場合)、

(SELECT * FROM table1 WHERE d1='B' ORDER BY d2)
UNION
(SELECT * FROM table1 WHERE d1='A' ORDER BY d2)
UNION
(SELECT * FROM table1 WHERE d1='C' ORDER BY d2)

としてたんだけど、

B 102
B 101
A 003
A 001
A 002
C 310
C 320

としか出んね。(テーブルに格納されている順番そのままに出てるだけ)

取りあえずソースが冗長化して格好悪いんだけど、来週にはテストをしたいんで、それぞれ個別に SELECT 実行してプログラム内で結合するようにするわ。

識者の方、良い SQL 文をご存じであればご教示ください。

最近 MySQL では UTF-8 しか扱ってなかったのだが、今回作成したプログラムは「Shift_JIS で書かれた CSV ファイルを読み込んで DB に登録。もちろん NEC 特殊文字や NEC 選定 IBM 拡張文字もガンガン混ざってますぜ!」という香ばしいもの。

ソース自体は EUC-JP で書いたので、DB の中身も EUC-JP で保管するようにして、自前のコード変換関数で Shift_JIS→EUC-JP に変換している。
CREATE TABLE するときにも、ちゃんと DEFAULT CHARSET=ujis 付けてるしね。

なのに、データを登録した後で phpMyAdmin でチェックすると、日本語化け化けやん。

どうも、MySQL ではサーバ側で勝手に文字コードのエンコードをしてしまうので、UTF-8 以外のコードではこういう文字化けが発生してしまうという。
日頃 PostgreSQL ばかり使ってて、いきなり MySQL に来たら絶対ハマっちゃうよなあ、これは。

で、解決方法としては、本来の SQL を投げる前に、

SET NAMES ujis

という SQL を発行すること。

具体的には、

$query    =<<EndOfQuery;
SET NAMES ujis
EndOfQuery

$sth = $dbh->prepare($query);
$sth->execute();

としてから、本来の SQL を

$query    =<<EndOfQuery;
INSERT
INTO
  hogehoge
~本来の SQL~
EndOfQuery

$sth = $dbh->prepare($query);
$sth->execute();

と実行すれば良いと。

なんだかなぁ(^^; テーブル作る時に DEFAULT CHARSET=ujis なんて宣言している意味がなかったな。(^^;
まあ、DB に詳しい偉い人たちが文句を言ってないってことは、この実装も間違いではないんだろうけど、なんだかなあ(^^;

ちなみに、MySQL の設定ファイルや実行時のオプションであれこれする方法もあるようなのだが、今回使用しているのが安いレンタルサーバで、MySQL の設定ファイルは触れない仕様らしいので、毎度 SET NAMES ujis を発行する方法しかないようだ。

とほほ。またも無駄な時間を・・・

psql -n hogedb とコマンド叩いて psql(PostgreSQL のターミナル型フロントエンド)を立ち上げ、

INSERT
INTO
    t_address
VALUES (
    '1',
    '742-0301',
    '岩国市周東町祖生',
    '2010-01-08 17:40:46'::timestamp
);

という SQL を実行しても、SELECT してみると、

hogedb=# SELECT * FROM t_address 
 uid |   post   |          address           |        cdate
-----+----------+----------------------------+------------------------
 1   | 742-0301 |                            | 2010-01-08 17:40:46
(1 rows)

という具合に住所が表示されない。
エラーは出てないので、登録されているはずだがなあ・・・

DB の Encoding は EUC_JP。

PuTTY の文字コードの設定が UTF-8/Auto-Detect Japanese になっていたので、EUC-JP に変えてみたが駄目。

Perl でちょいプロを作って、DBI/DBD 経由で書き込んでみたが同じ状況。エラーは出ないのだが、psql で該当テーブルを見てみると、日本語が表示されない。

今度は、SELECT する「ちょいプロ」作って確認してみよ・・・とプログラムを書きだしたところで、ハっと思いついて pg_dump hogedb > hogedb.txt と DB 内容のダンプを出力してみた。

そしたら、

--
-- Data for Name: t_address; Type: TABLE DATA; Schema: public; Owner: xxxxxx
--

COPY t_address (uid, post, address, cdate) FROM stdin;
1       742-0301        岩国市周東町祖生      2010-01-08 17:40:46

という具合にしっかり日本語部分も登録されているのが確認できた。

psql が日本語を表示してないだけか!?
う~ん、何でじゃろうね。
psql って、ページャーは more を使ってるんだったっけ?

なにせ、PostgreSQL 8.4.2 と CentOS 5.4 の組み合わせが初めてなのと、ここ数年、そういやあ新規開発案件は MySQL ばかりで、psql を使ってあれこれというケースもあまりなかったよ。
だから知らないのだが、最近の psql はこうなのか?

昔、psql が日本語対応してなかった時も、全然表示しないということはなかったからなあ。文字化けするだけで。

ま、取りあえず psql 使ってする作業は終わったので、これ以上原因は追及しないが、またも無駄な時間を過ごしてしまった。(^^;

いやあ、Web 上の某情報を自動で取ってきて DB 化するという処理を、やっつけでパパッと Perl スクリプトで書いて実行してたら、

DBD::mysql::st execute failed: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ...

なエラーが。

はい。シングルクォーテーションのエスケープ漏れです。(^^;
今回、ベタで SQL 書いて、直接 DBD/DBI で execute してるからねえ。ちゃんと手動でエスケープしないと。

INSERT 文にセットする前に、

$value =~ s/\'/\'\'/g;

とかね。(シングルクォーテーションを二重に)

そしたら今度は、別のスクリプトで、

sh: -c: line 0: unexpected EOF while looking for matching `''
sh: -c: line 1: syntax error: unexpected end of file

なエラーが・・・とほほ。

シングルクォーテーションを含んだ文言を echo してプログラムに食わせる処理を Perl 内で実行してたんで、そこで引っかかってる。(^^;

ちゃんと、

$value =~ s/\'/\\\'/g;

しなくちゃいけませんなあ。(シングルクォーテーションの前に \ を)

ちゃんと作る時は、この辺の処理は自前の関数で処理するようにしてるんで、ちょっとテスト的にやっつけでチャチャっと作るとついつい忘れてしまいますなあ。

と、自責の意味で書いておく。

さて、Perl の sprintf で、文字列の右側に 0 を補完するのはどうすればええんじゃろうねえ?

例えば、

$d    = '112200225';

して、この値を sprintf で編集すると、

$d      = sprintf("%020d", $d);

なら、00000000000112200225 となっちゃうんだけど、実際は、後ろに 0 を補完して、11220022500000000000 としたいのよ。

試しに sprintf("%-020d", $d) ってやってみたけど、これじゃあ何も補完されないだけだった。

後ろにスペースを埋めるというやり方はあるので、先にスペースを埋めてからそれを正規表現で 0 に変換しちゃえばいいんだが、こういうやり方しかないんかいのお?

$d    = '112200225';
$d    = sprintf("%-20s", $d);
$d    =~ s/\s/0/g;
print "$d\n";

とすれば、出力結果は 11220022500000000000 だ。
これをね、sprintf 一発でやりたいんだけどなあ。

識者の方、ぜひご教示ください。

このアーカイブについて

このページには、過去に書かれたブログ記事のうちPerlカテゴリに属しているものが含まれています。

前のカテゴリはAIR/Flexです。

次のカテゴリはPHPです。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

電気ウナギ的○○ mobile ver.

携帯版「電気ウナギ的○○」はこちら