perl でハッシュ(連想配列)のソート

2010年7月20日 16:40 perl でハッシュ(連想配列)のソート

perl でハッシュ(連想配列)のソートを行なう処理、今まで何度も何度も何度も何度も(ry 書いてきたのに、毎回忘れるんですよね。
どんだけ鳥頭なんだ、と。

というわけで、忘れても困らないようにここに書いておきます。

ハッシュのキーでソートする

ハッシュのキーでソートするのは簡単です。

sort keys %hash;

これでソートされたキーのリストが返ります。

foreach を使ってキー順にハッシュを処理したいなら、

foreach $key (sort keys %hash){
	print qq|Key: $key\n|;
	print qq|Value: $hash{$key}\n|;
}

こんな感じになります。

ハッシュの値でソートする

ハッシュの値でソートする場合は、以下のようになります。

sort {$hash{$a} <=> $hash{$b}} keys %hash;

これで、値でソートされたハッシュのキーのリストが返ります。

foreach を使って値の順にハッシュを処理したい場合は、

foreach $key (sort {$hash{$a} <=> $hash{$b}} keys %hash){
	print qq|Key: $key\n|;
	print qq|Value: $hash{$key}\n|;
}

こんな感じです。

関連するブログ記事
カテゴリー
perl/CGI
ã‚¿ã‚°
foreach | keys | perl | sort | 連想配列

コメント(8)

 

perl で四捨五入を行なう方法

perl で四捨五入の処理を行なう方法をあれこれまとめておきます。

sprintf を使う方法

あんまり推奨されていない方法だとは思いますが、sprintf で四捨五入をする場合。

小数点以下の部分を四捨五入する場合は "%.1f" "%.3f" などと指定します(1や3等の数字で小数点以下の四捨五入する桁を指定)。

$num = 1.4256;

#-- 小数点第一位に四捨五入
$round_num = sprintf("%.1f",$num); # 1.4
#-- 小数点第三位に四捨五入
$round_num = sprintf("%.3f",$num); # 1.426

Math::Round::nearest モジュールを使う方法

sprintf を使うより、こちらの方が正確です。
nearest(桁数,数値); という形式で、四捨五入の結果を返してくれます。

use Math::Round;
$num = 46.8345;

#-- 小数点第一位に四捨五入
$round_num = nearest(0.1,$num); # 46.8
#-- 小数点第三位に四捨五入
$round_num = nearest(0.001,$num); # 46.835
#-- 一の位に四捨五入
$round_num = nearest(1,$num); # 47
関連するブログ記事
カテゴリー
perl/CGI
ã‚¿ã‚°
perl | 四捨五入

コメント(489)

 

perl で正規表現にマッチした文字列を配列に入れる方法

ある文字列から、正規表現にマッチした文字列のリストを、配列に入れる方法です。
意外と最近までこれ知らなかったです。

#-- 文字列
$string = '0a12bc345def6789ghij';

#-- 数字だけを抽出して配列に入れる
@nums = $string =~ /[0-9]+/g;

#-- 配列に入った値を出力
foreach $num (@nums){
	print $num."\n";
}

上記の出力結果は、

0
12
345
6789

となるはずです。

関連するブログ記事
カテゴリー
perl/CGI
ã‚¿ã‚°
perl | 正規表現

コメント(8)

 

perl で XML-RPC API を使ってブログ記事を投稿・編集する

XML-RPC API を使って、perl で各種ブログに記事を投稿したり、記事を編集したりする方法の解説です。

各ブログサービスごとの、エンドポイントの URL や、 blogid 他、必要な情報は、XML-RPC API で投稿できるブログサービスの情報まとめにまとめてあります。

metaWeblog.newPost メソッドを使ったブログ記事の新規投稿

metaWeblog.newPost メソッドを使って、ブログ記事の新規投稿をする perl スクリプトは以下のような感じ。

use XMLRPC::Lite;

#-- 以下四項目はブログサービスによって異なる
$blogid   = 'abe';
$username = 'tatsuya';
$password = 'password';
$endpoint = 'http://www.xxxx.com/xml-rpc.cgi';

$result = XMLRPC::Lite
	-> proxy($endpoint)
	-> call('metaWeblog.newPost', $blogid, $username, $password,
		{
			#-- ブログ記事タイトル
			'title' => XMLRPC::Data->type('string', $entry_title),
			#-- ブログ記事本文
			'description' => XMLRPC::Data->type('string', $entry_body),
			#-- コメントを受け付けるかどうか(1でコメント受付)
			'mt_allow_comments' => 1,
			#-- トラックバックを受け付けるかどうか(1でトラックバック受付)
			'mt_allow_pings' => 1,
			#-- ブログ記事本文の「続き」部分
			'mt_text_more' => XMLRPC::Data->type('string', $entry_more),
			#-- ブログ記事の概要
			'mt_excerpt' => XMLRPC::Data->type('string', $entry_excerpt),
			#-- ブログ記事のキーワード
			'mt_keywords' => XMLRPC::Data->type('string', $entry_keyword),
		},
		1
	)
	-> result;
if(!defined($result)){
	#-- 投稿エラー
}else{
	#-- 投稿成功
	#-- $result にエントリーIDが入るので、保存しておく
}

metaWeblog.editPost メソッドを使ったブログ記事の編集

metaWeblog.editPost メソッドを使って、既存のブログ記事を編集する perl スクリプトは以下のような感じ。
前述の metaWeblog.newPost で取得したエントリーIDを保持している必要があります。

use XMLRPC::Lite;

#-- 投稿時に取得したエントリーID
$postid   = '00001';
#-- 以下三項目はブログサービスによって異なる
$username = 'tatsuya';
$password = 'password';
$endpoint = 'http://www.xxxx.com/xml-rpc.cgi';

$result = XMLRPC::Lite
	-> proxy($endpoint)
	-> call('metaWeblog.editPost', $postid, $username, $password,
		{
			#-- ブログ記事タイトル
			'title' => XMLRPC::Data->type('string', $entry_title),
			#-- ブログ記事本文
			'description' => XMLRPC::Data->type('string', $entry_body),
			#-- コメントを受け付けるかどうか(1でコメント受付)
			'mt_allow_comments' => 1,
			#-- トラックバックを受け付けるかどうか(1でトラックバック受付)
			'mt_allow_pings' => 1,
			#-- ブログ記事本文の「続き」部分
			'mt_text_more' => XMLRPC::Data->type('string', $entry_more),
			#-- ブログ記事の概要
			'mt_excerpt' => XMLRPC::Data->type('string', $entry_excerpt),
			#-- ブログ記事のキーワード
			'mt_keywords' => XMLRPC::Data->type('string', $entry_keyword),
		},
		1
	)
	-> result;
if(!defined($result)){
	#-- 投稿エラー
}else{
	#-- 投稿成功
}
関連するブログ記事
カテゴリー
perl/CGI
ã‚¿ã‚°
API | perl | xml-rpc | ブログ

コメント(402) | トラックバック(2)

 

perl / PHP / JavaScript 各言語での URL(URI)エンコード・デコード方法まとめ

日本語等の2バイト文字を URL として使用するための、URLエンコード・デコード方法を、perl / PHP /JavaScript の各言語別にまとめてみました。

と言っても、PHP と JavaScript はそれ専用の関数が用意されてるから簡単なんですけどね。

perl で URLエンコード・デコード

perl で URLエンコードするには、以下のようにします。

$word =  "はぴはぴはっぴー♪";
$word =~ s/([^0-9A-Za-z_ ])/'%'.unpack('H2',$1)/ge;
$word =~ s/\s/+/g;

これで「はぴはぴはっぴー♪」という文字列が「%82%cd%82%d2%82%cd%82%d2%82%cd%82%c1%82%d2%81%5b%81%f4」という文字列にエンコードされます。

続いて、perl での URLデコード方法。

$word =  "%82%cd%82%d2%82%cd%82%d2%82%cd%82%c1%82%d2%81%5b%81%f4";
$word =~ s/+/\s/g;
$word =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/ge;

これで「%82%cd%82%d2%82%cd%82%d2%82%cd%82%c1%82%d2%81%5b%81%f4」という文字列が「はぴはぴはっぴー♪」という文字列にデコードされます。

PHP で URLエンコード・デコード

前述の通り、PHP にはURLエンコード・デコード用の関数が用意されているので、非常に簡単です。

#-- URLエンコード
$word = "はぴはぴはっぴー♪";
$encoded = urlencode($word);

#-- URLデコード
$encoded = "%82%cd%82%d2%82%cd%82%d2%82%cd%82%c1%82%d2%81%5b%81%f4";
$word = urldecode($encoded);

JavaScript で URLエンコード・デコード

PHP と同じく、JavaScript にも URLエンコード・デコード用の関数が用意されているので、同じく簡単です。

// URLエンコード
var word = "はぴはぴはっぴー♪";
var encoded = encodeURI(word);

// URLデコード
var encoded = "%82%cd%82%d2%82%cd%82%d2%82%cd%82%c1%82%d2%81%5b%81%f4";
var word = decodeURI(encoded);
関連するブログ記事
カテゴリー
JavaScript | PHP | perl/CGI
ã‚¿ã‚°
decodeURI | encodeURI | JavaScript | perl | PHP | urldecode | urlencode | 正規表現

コメント(1)

 

perl や PHP で Cookie(クッキー)を読み書きする方法

Cookie の出力

Cookie は HTTPヘッダに出力することで、アクセスしたユーザーのブラウザ毎に書き込むことができます。

Cookie を書き込む際の書式は、

name=value; expires=Sun, 15-Nov-2009 23:59:59 GMT; domain=www.abe-tatsuya.com; path=/cgi-bin/; secure

という感じです。

name
必須。任意の「name」に対する値「value」を指定します。
例) loginid=abetatsuya
expires
Cookie の有効期限を指定します。省略するとブラウザを閉じるまでの間が有効期限になります。
また、過去の時刻を指定すると、該当 Cookie を削除します。
domain
Cookie を有効にするドメイン名を指定します。省略可。
path
Cookie を有効にするパスを指定します。省略すると、Cookie を書き込んだファイルのあるパスとなります。
secure
指定すると、SSL での通信時のみ Cookie をやり取りします。

この情報を、Set-Cookieヘッダとして出力すれば良いので、 perl の CGI の場合には、

print qq|Set-Cookie: loginid=abetatsuya; |;
print qq|expires=Sun, 15-Nov-2009 23:59:59 GMT; |;
print qq|domain=www.abe-tatsuya.com; path=/cgi-bin/; secure\n|;
print qq|Content-type: text/html; chearset=UTF-8\n|;
print qq|\n|;
#-- 以下、ページを出力

という感じにすれば良いわけです。

PHP の場合は、その名の通り setcookie っていう関数が用意されてるので、非常に楽チンですね。

setcookie("loginid","abetatsuya",time,"/cgi-bin",
 "www.abe-tatsuya.com",true);

こんな感じで書けばOKです。
詳細はPHP: setcookie - Manual等を参照。

複数の Cookie を書き込みたい場合は、複数回 Set-Cookie ヘッダを出力すれば大丈夫です。

Cookie の読み込み

書き込んだ Cookie を読み込みたい場合は、環境変数 HTTP_COOKIE を参照します。

perl であれば $ENV{'HTTP_COOKIE'} 内に「name=value」という形式で入っています。
複数の値が指定されている場合は「; 」区切りで「name1=value1; name2=value2」のように入ります。

「; 」で split した後にさらに「=」で split して、$COOKIE{'name'} = value のような連想配列に入れると使い易いかと思います。

PHP の場合は、$_COOKIE['name'] という変数内に、そのまま指定した値が入ってきます。

関連するブログ記事
カテゴリー
PHP | perl/CGI
ã‚¿ã‚°
CGI | Cookie | HTTPヘッダ | perl | PHP | Set-Cookie | setcookie | 環境変数

コメント(21)

 

perl の CGI や PHP でフォーム入力チェックによく使う正規表現のまとめ

perl や PHP で Webアプリケーションを作る際、ユーザーがフォームに入力した値を受け取って、何らかの処理を行なう、というのは基本中の基本です。

ユーザーが入力した値をチェックして、こちらが想定したフォーマットで送られているかどうか確認し、想定外の値ならばエラーを返す、というのも基本中の基本ですね。

というわけで、そんなフォームからのユーザー入力の値のエラーチェックによく使いそうな正規表現をまとめてみました。

以下、perl の方は「$FORM{'data'}」という変数の中に、 PHP の方は「$_REQUEST['data']」という変数の中に、ユーザー入力の値が入っているとします。

半角数字のみを受け付ける場合

フォームに入力された値の中に、「半角数字以外が含まれている場合」にエラーを返すには、以下のようにします。

#-- perl の場合
if($FORM{'data'} =~ /\D/){
	print qq|Error: 半角数字で入力して下さい\n|;
}

#-- PHP の場合
if(preg_match("/\D/",$_REQUEST['data'])){
	print ("Error: 半角数字で入力して下さい\n");
}

ただし、上記の場合はフォーム入力が空の場合はチェックを通ってしまいますので、別途チェックが必要です。

なので以下のように、「1桁以上の半角数字のみ受け付ける」ようなチェック方法の方が良いかと思います。

#-- perl の場合
if($FORM{'data'} !~ /^\d+$/){
	print qq|Error: 半角数字で入力して下さい\n|;
}

#-- PHP の場合
if(!preg_match("/^\d+$/",$_REQUEST['data'])){
	print ("Error: 半角数字で入力して下さい\n");
}

「5桁の数字のみ受け付ける」という場合は、以下のようにします。

#-- perl の場合
if($FORM{'data'} !~ /^\d{5}$/){
	print qq|Error: 5桁の半角数字を入力して下さい\n|;
}

#-- PHP の場合
if(!preg_match("/^\d{5}$/",$_REQUEST['data'])){
	print ("Error: 5桁の半角数字を入力して下さい\n");
}

桁数を変えたければ、「\d{5}」の部分を「\d{10}」とか「\d{6}」とかにすればOK。

「5桁ぴったりではなく、5桁以内(1~5桁)の半角数字のみ受け付ける」という場合は、以下のようにします。

#-- perl の場合
if($FORM{'data'} !~ /^\d{1,5}$/){
	print qq|Error: 1~5桁の半角数字を入力して下さい。\n|;
}

#-- PHP の場合
if(!preg_match("/^\d{1,5}$/",$_REQUEST['data'])){
	print ("Error: 1~5桁の半角数字を入力して下さい。\n");
}

1~5桁ではなく、2~4桁にしたければ「\d{1,5}」の部分を「\d{2,4}」にすれば良いですし、0~10桁にしたければ「\d{0,10}」にすればOK。

半角数字と半角ドット「.」のみ受け付ける場合

要するに、数字だけを入力して欲しいけれど、小数もOKにしたいので、小数点「.」も許可したい、という場合ですね。

半角数字と半角ドット以外が含まれている場合にエラーを返すには、以下のようにします。

#-- perl の場合
if($FORM{'data'} =~ /[^\d\.]/){
	print qq|Error: 半角数字と半角ドット「.」で入力して下さい。\n|;
}

#-- PHP の場合
if(preg_match("/[^\d\.]/",$_REQUEST['data'])){
	print ("Error: 半角数字と半角ドット「.」で入力して下さい。\n");
}

しかし、これも入力が空だと通ってしまいますし、半角ドットが二つ以上あっても通ってしまいますので「小数は許可したい」というニーズを超えてしまっています。

なので「一桁以上の数値からはじまっていて、0~1個の半角ドットと半角数字だけが含まれ、最後は半角数字で終わるもののみ受け付ける」という考え方で正規表現を書くのが良いかと思います。

#-- perl の場合
if($FORM{'data'} !~ /^\d$|^\d+\.?\d+$/){
	print qq|Error: 半角数字と半角ドット「.」で入力して下さい。\n|;
}

#-- PHP の場合
if(!preg_match("/^\d$|^\d+\.?\d+$/",$_REQUEST['data'])){
	print ("Error: 半角数字と半角ドット「.」で入力して下さい。\n");
}

上記は、「一桁の数字か、一桁以上の数字から始まって0~1個のドットを含み一桁以上の数字で終わる」もののみマッチします。

半角英数字のみ受け付ける場合

正規表現で「半角英数字」は「\w」で表すことができます。
また、「半角英数字以外」は「\W」で表すことができます。

2010/07/16追記
flagged utf8 では、「\w」に日本語が含まれるという指摘を頂きました。
※参考リンク1 参考リンク2

なので、前述の「半角数字のみを受け付ける場合」の「\d」「\D」を「\w」「\W」に置き換えるだけで基本的には問題ないかと思います。

ただし「\w」には半角アンダーバー「_」も含まれます(逆に「\W」は正確には「半角英数字とアンダーバー以外」ですね)。
なので、半角アンダーバー「_」を除外したい場合は「\w」の代わりに「[A-Za-z0-9]」を使うのが良いかと。
また、「\W」の代わりは「[^A-Za-z0-9]」になります。

たまにあるケースで、ユーザーIDが「数桁の英字+数桁の数字」(ABC0123みたいな)で固定されているケースでは、それに応じた正規表現を書くのが良いでしょう。

例えば「3桁の英字+4桁の数字」の場合は以下のようになります。

#-- perl の場合
if($FORM{'data'} !~ /^[A-Za-z]{3}\d{4}$/){
	print qq|Error: 3桁の英字+4桁の数字を入力して下さい。\n|;
}

#-- PHP の場合
if(!preg_match("/^[A-Za-z]{3}\d{4}$/",$_REQUEST['data'])){
	print ("Error: 3桁の英字+4桁の数字を入力して下さい。\n");
}

英字が大文字のみで統一されている場合は「[A-Za-z]」の部分を「[A-Z]」に、小文字で統一なら「[a-z]」にしても良いですが、大文字と小文字でIDに差異がないのなら、どちらでも受け付けられる方がユーザビリティが良いでしょう。

郵便番号入力欄のエラーチェック

郵便番号は、ユーザーに入力の際にハイフンを省力してもらって、数字7桁のみ受け付けるようにするのが手っ取り早くて楽ですね。

#-- perl の場合
if($FORM{'data'} !~ /^\d{7}$/){
	print qq|Error: 郵便番号を半角数字(ハイフンなし)で入力して下さい\n|;
}

#-- PHP の場合
if(!preg_match("/^\d{7}$/",$_REQUEST['data'])){
	print ("Error: 郵便番号を半角数字(ハイフンなし)で入力して下さい\n");
}

ハイフンありじゃないと困る、という場合は以下のような感じになります。

#-- perl の場合
if($FORM{'data'} !~ /^\d{3}-\d{4}$/){
	print qq|Error: 郵便番号を半角数字(ハイフンつき)で入力して下さい\n|;
}

#-- PHP の場合
if(!preg_match("/^\d{3}-\d{4}$/",$_REQUEST['data'])){
	print ("Error: 郵便番号を半角数字(ハイフンつき)で入力して下さい\n");
}

ハイフンがあってもなくても受け付けるようにしたい、という場合は以下のような感じに。

#-- perl の場合
if($FORM{'data'} !~ /^\d{3}-?\d{4}$/){
	print qq|Error: 郵便番号を半角数字で入力して下さい\n|;
}

#-- PHP の場合
if(!preg_match("/^\d{3}-?\d{4}$/",$_REQUEST['data'])){
	print ("Error: 郵便番号を半角数字で入力して下さい\n");
}

ハイフンがあってもなくても受け付けるようにしておいて、後でスクリプト内でハイフンを取り除いて統一させておくのが一番良いかもですね。

電話番号入力欄のエラーチェック

電話番号も、郵便番号と同じく、ユーザーに入力の際にハイフンを省略してもらうのが楽ですよね。

国内の電話番号は、多分「0から始まる10~11桁の数字」で大丈夫だと思うので、以下のような感じ。

#-- perl の場合
if($FORM{'data'} !~ /^0\d{9,10}$/){
	print qq|Error: 電話番号を半角数字(ハイフンなし)で入力して下さい\n|;
}

#-- PHP の場合
if(!preg_match("/^0\d{9,10}$/",$_REQUEST['data'])){
	print ("Error: 電話番号を半角数字(ハイフンなし)で入力して下さい\n");
}

携帯電話に限定するなら「1文字目と3文字目が0で、全部で11桁の数字」で良いと思うので、以下のような感じでしょうか。

#-- perl の場合
if($FORM{'data'} !~ /^0\d0\d{8}$/){
	print qq|Error: 電話番号を半角数字(ハイフンなし)で入力して下さい\n|;
}

#-- PHP の場合
if(!preg_match("/^0\d0\d{8}$/",$_REQUEST['data'])){
	print ("Error: 電話番号を半角数字(ハイフンなし)で入力して下さい\n");
}

ハイフンありの場合は「市外局番2~6桁-市内局番0~4桁-4桁の数字」(これで合ってるかどうか正直自信ないです)にマッチしているかどうかチェックすれば良いと思うので、以下のような感じ。

#-- perl の場合
if($FORM{'data'} !~ /^0\d{1,5}-\d{0,4}-?\d{4}$/
 || $FORM{'data'} !~ /^.{11,13}$/){
	print qq|Error: 電話番号を半角数字で入力して下さい\n|;
}

#-- PHP の場合
if(!preg_match("/^0\d{1,5}-\d{0,4}-?\d{4}$/",$_REQUEST['data'])
 || !preg_match("/^.{11,13}$/",$_REQUEST['data'])){
	print ("Error: 電話番号を半角数字で入力して下さい\n");
}

「市外局番2~6桁-市内局番0~4桁-4桁の数字」のチェックだけでなく、総文字数が11~13文字であるかどうかもチェックしています。
※単純な文字数チェックは length関数とか strlen関数とか使った方が良いと思いますが、今回は正規表現の記事なので、無理矢理正規表現でやってみました(^^;


他にもまだ色々ありますが、この記事書くのに意外と時間がかかったので、続きはまた次の機会に。

関連するブログ記事
カテゴリー
PHP | perl/CGI
ã‚¿ã‚°
CGI | perl | PHP | preg_match | 正規表現

コメント(327)

 

perl や PHP で Excelファイル(xlsファイル)を簡単に出力する方法

perl や PHP で、Excelファイル(拡張子が「.xls」のファイルとか)を出力するために、色々とライブラリみたいなものがあるみたいなんですが、裏技的でちょっと邪道な、もっと簡単な解決方法があります。

Excel で作った文書を保存する際に、「Webページとして保存」というメニューがあることからも想像できますが、Excelで html文書を開いたら、普通に Excel 文書として読めるんですよね。

なので、 perl の CGI や PHP で、Excel 形式で出力しようと思ったら、perl の CGI や PHP でよく出力する Content-type ヘッダ一覧 (MIME-type)を参考に、以下のようなヘッダを出力。

#-- perl の場合
print qq|Content-type: application/vnd.ms-excel\n\n|;

#-- PHP の場合
header('Content-Type: application/vnd.ms-excel');

そしてその後は、普通に html を出力すれば、勝手に Excel が開いて、Excel 文書として出力されます。

Excel を開くのではなく、ダウンロードさせたい場合は、perlやPHPでファイルのダウンロード確認ダイアログを表示させる方法を参考にすれば良いと思います。

#-- perl の場合
print qq|Content-type: application/vnd.ms-excel\n|;
print qq|Content-Disposition: attachment; filename="sample.xls"\n\n|;

#-- PHP の場合
header('Content-Type: application/vnd.ms-excel');
header('Content-Disposition: attachment; filename="sample.xls"');

なお、Excel ファイルを前述のように「Webページとして保存」すると、作成したファイルが html 文書として保存されるので、それを元に編集して perl の CGI や PHP の出力結果を用意すると良いかと思います。
Excel が吐き出す html ソースは凄まじく汚くて読み辛いですが......。

関連するブログ記事
カテゴリー
PHP | perl/CGI
ã‚¿ã‚°
CGI | Content-Disposition | Content-type | Excel | HTTPヘッダ | Internet Explorer | MIME | perl | PHP | Windows | xhtml | ダウンロード | 拡張子

コメント(541) | トラックバック(4)

 

.htaccess で CGI スクリプトを実行できるように設定する方法

.htaccess を使用して、ディレクトリ単位で CGI の実行を許可する方法の解説です。

CGI スクリプトを実行可能にする

ディレクトリに対して、CGI スクリプトの実行を可能にするためには、.htaccess に以下のように記述します。

Options +ExecCGI

「ExecCGI」(CGI の実行)というオプションを追加(「+」)してる、というようなニュアンスですね。

逆に、

Options -ExecCGI

と書けば、CGI スクリプトの実行ができなくなります。

拡張子「.cgi」を CGI スクリプトであると認識させる

以下のように .htaccess に記述することで、Webサーバーに、拡張子「.cgi」がCGI スクリプトである、と認識させることができます。

AddType application/x-httpd-cgi .cgi

上記以外にも例えば、「.pl」の拡張子のファイルを CGI スクリプトと認識させたければ、

AddType application/x-httpd-cgi .pl

と書けば良いですし、PHP を CGI スクリプトとして実行する場合は、

AddType application/x-httpd-cgi .php

と書けば良いでしょう。

関連するブログ記事
カテゴリー
.htaccess | perl/CGI
ã‚¿ã‚°
.htaccess | AddType | CGI | Content-type | Options | perl | PHP

コメント(12)

 

PHP と perl の配列の添え字に関するメモ

PHP の配列自動指定について

PHPでは、配列に空の添え字を指定することで、自動で添え字を +1 できるんですね。
恥ずかしながら知りませんでした。

例えば、

$list = array();
for($a=0;$a<10;$a++){
	$list[] = $a;
}

と書けば、 $list[0]~$list[9] にそれぞれ 0~9が入る、と。

他にも、

$list = array();
$list[0] = 'Red';
$list[1] = 'Blue';
$list[5] = 'Purple';
$list[] = 'Pink';

とやると、最後の行は $list[6] = 'Pink'; となるそうな。

perl では似たような方法あるのかな......?

PHP と perl の配列の添え字最大値取得方法

上記のような手段を取ると、やっぱり配列の添え字の現在の最大値を見るような処理が必要になってくるケースなんかもある気がします。

そういう場合、perl だと、

@list = ('Red', 'Blue', 'Purple', 'Pink');
$max = $#list;

という感じで、$#配列名 で配列の添え字の最大値が取得できます。

PHPの方にはざっと調べたところ、こういう簡単な手段はないようで、

$list = array('Red', 'Blue', 'Purple', 'Pink');
$max = max(array_keys($list));

とかやるみたいです。

参考: 最大添字を取得するには - Yahoo!知恵袋

関連するブログ記事
カテゴリー
PHP | perl/CGI
ã‚¿ã‚°
perl | PHP | 配列

コメント(8) | トラックバック(1)

 

au と Softbank の input要素や textarea要素内にタグが入っていた場合の表示の違い

昨日仕事で、携帯からサイトの更新ができる、ちょっとしたCMSツールみたいなのを作っていたんですが、新たな発見があったのでメモ。

作っていたツールというのは「普段PCで更新しているPC向けサイトを、緊急時なんかに外出先でも更新できるようにする」という目的のものです。

ケータイのブラウザでツールにログインして、更新するページの項目を選択。その後表示されるフォームで、コンテンツを携帯から書き換える。というような処理をしています。

で、サイトのコンテンツを更新するツールなので、フォームの input要素や textarea要素には、現在のコンテンツが入った状態で出力されます。
当然、HTMLタグも入っているわけです。下記のような感じ。

<input type="text" name="header" value="<strong>新着情報</strong>" />

<textarea name="content">クリスマス中止のお知らせ!<br />今年のクリスマスは中止!</textarea>

動作確認をしてみると、ソフトバンクのケータイでは input要素や textarea要素内にHTMLタグが入っていても、普通にフォームが表示されました。
auのケータイではアウト。 input要素の value属性内や、textarea要素内の、HTMLタグがある部分で入力欄の内容が終わっちゃいます。

まあ双方一機種ずつしか確認してないので、ソフトバンクでも駄目なケータイがあるかもしれないし、au でも OKなケータイがあるかもしれません。
au とソフトバンクの違いというか、搭載しているブラウザの違いと言うべきでしょうか。
ちなみに、PCで表示した場合、IEだと普通に見れます。
DoCoMo のケータイは持ってないので確かめてません。

どっちにしろ、これは横着した結果こうなったというだけで、ほんとは全部「<」は「&lt;」、「>」は「&gt;」と出力するべきです。

というわけで、そのフォームを出力する前に、

$content =~ s/</&lt;/g;
$content =~ s/>/&gt;/g;

みたいにしてから、

print qq|<input type="text" name="header" value="$content" />\n|;

とか、

print qq|<textarea name="content">$content</textarea>\n|;

とやれば、au でもちゃんと表示できました。

関連するブログ記事
カテゴリー
perl/CGI | モバイル(携帯電話・ケータイ)Web
ã‚¿ã‚°
au | input要素 | perl | textarea要素 | xhtml | ソフトバンク | モバイル | 実体参照 | 正規表現

コメント(9)

 

PostgreSQLで複数のテーブルを結合して処理する方法

今日は久々に旧ブログから記事を引っ張ってきました。

以下は二年以上前に書いたブログ記事の転載です。


新鮮なネタがないので、ストックの中からPostgreSQLネタでも。
というわけで、PostgreSQLで複数のテーブルを結合して処理する方法のメモです。

「複数のテーブルを結合して集計する」というのがどういう意味かと言うと、例えばまず会員制のショッピングサイトなどでユーザーの情報を管理するDBがあると仮定します。
で、一つ目のテーブル「TABLE1」には下記のように、ユーザーID・ユーザーの氏名・ユーザーのメールアドレス等の基本情報が格納されているとします。

TABLE1
UserID UserName MailAddress
Amethyst 鬼瓦 権三郎 [email protected]
Alexandrite 俵田山 兼松 [email protected]
Sapphire 平等院 鳳凰堂 [email protected]

次に、二つ目のテーブル「TABLE2」には、各ユーザーの購入商品の情報が格納されているとします。
例えば、注文ID・注文者のユーザーID・商品名・商品価格etc。

TABLE2
OrderID UserID ItemName ItemPrice
1 Amethyst 扇風機 2980
2 Amethyst エアコン 99800
3 Sapphire コーヒーメーカー 2800
4 Amethyst 電気コタツ 5800
5 Alexandrite 上戸彩写真集 4800
6 Sapphire マグカップ 1200
7 Alexandrite エロマンガ 980

さて、上記の注文商品の情報が格納されたTABLE2のデータを元に、注文したユーザー宛に一括で受注確認のメールを送信するプログラムを作りたい時、どうすれば良いでしょうか。

単純に処理しようと思えば、まずTABLE2のデータを一行ずつ取得し、その情報の中からユーザーIDを取得し、次にそのユーザーIDをキーにしてTABLE1からメールアドレスを取得、という流れになります。
perlで書くと、下記のような感じ

#-- TABLE2のデータを取得
$SQLstat1 = $DB->prepare("select OrderID, UserID, ItemName, ItemPrice from TABLE2");
#-- 一行ずつ処理する
while(@DATA = $SQLstat1->execute){
    #-- 取得したデータを変数にセット
    ($OrderID,$UserID,$ItemName,$ItemPrice) = @DATA;
    #-- $UserIDをキーにしてTABLE1のデータを取得
    $SQLstat2 = $DB->prepare("select UserName, MailAddress from TABLE1 where UserID = '$UserID'");
    #-- 取得したデータを変数にセット
    ($UserName,$MailAddress) = $SQLstat->execute;
    #-- 以下にメール送信の処理が入る
    &SendMail;
}

※上記の処理の場合は、実際にはプレースホルダを使った方が良いと思いますが、ここでは処理の流れをわかりやすくするために敢えてこういう記述にしました。

さて、上記のスクリプトの場合は一つ目のselect文の中で何度も別のselect文を発行していることになります。
が、select文にinner joinという句を使うと、一度のSQL文で上記の処理を済ますことができます。
↓こんな感じ。

#-- TABLE2のデータを元にTABLE1からUserIDをキーにしてデータを取得
$SQLstat = $DB->prepare("select TABLE2.OrderID, TABLE2.UserID, TABLE2.ItemName,
 TABLE2.ItemPrice, TABLE1.UserName, TABLE1.MailAddrss
 from TABLE2 inner join TABLE1 on TABLE2.UserID = TABLE1.UserID");
#-- 一行ずつ処理する
while(@DATA = $SQLstat->execute){
    #-- 取得したデータを変数にセット
    ($OrderID,$UserID,$ItemName,$ItemPrice,$UserName,$MailAddress) = @DATA;
    #-- 以下にメール送信の処理が入る
    &SendMail;
}

上記スクリプトの
from TABLE2 inner join TABLE1 on TABLE2.UserID = TABLE1.UserID
の部分がポイントです。
これは「TABLE2とTABLE1を結合するよ。条件として"TABLE2のUserIDとTABLE1のUserIDが同じ"データを結合してね」
ということです。
ちなみに、上記select文で出てくる TABLE2.OrderID とか TABLE1.UserID とかはそれぞれ「TABLE2内のOrderID」「TABLE1内のUserID」を表します。

このinner join句は、select文だけではなくupdate文にも使えますので何かと便利です。
delete文には使った記憶がないのでよくわかりません。多分使えるんじゃないですかね?

関連するブログ記事
カテゴリー
PostgreSQL | perl/CGI
ã‚¿ã‚°
inner join | perl | PostgreSQL | RDBMS | SQL | コマンドライン | シェル

コメント(25)

 

perl の CGI や PHP でファイルをアップロードする方法

フォームからファイルをアップロードして、そのファイル自体をサーバーに保存したり、あるいはファイルの内容をデータベースに反映したりするようなスクリプトも、作る機会がかなり多いので、手順をメモしておきます。

フォームの html

ファイルをアップロードさせるフォームの場合、通常の form 要素とは違って、enctype 属性をつける必要があります。

<form method="POST" action="upload.cgi" enctype="multipart/form-data">
  <!-- ここにフォームの内容が入る -->
  <input type="file" name="uploadfile" value="" size="20" />
  <input type="submit" value="アップロード" />
</form>

enctype 属性の他に注意する点としては、必ず POST メソッドを指定することが挙げられます。
GET メソッドではファイルのアップロードはできません。

アップされたファイルの受け取り方 (perl の場合)

僕自身は昔、まだ CGI.pm が perl の標準モジュールじゃなかった頃に作ったコードで、アップされたファイルの内容を受け取る処理をすることが多いですが、今は CGI.pm が標準モジュールとして最初から入っているので、これを使うのが手っ取り早いでしょう。

下記サイトが参考になります。

アップされたファイルの受け取り方 (PHP の場合)

PHP の場合は、非常に簡単です。
定義済変数 $_FILES に、アップロードされたファイルの情報が自動的に入ります。

例えば、

<form method="POST" action="upload.cgi" enctype="multipart/form-data">
  <input type="file" name="uploadfile" value="" size="20" />
</form>

のように、アップロードするファイルの入力欄の名前を uploadfile とした場合、変数 $_FILES は以下のように参照できます。

$_FILES['uploadfile']['name']
元のファイル名
$_FILES['uploadfile']['type']
ファイルの MIME-Type
$_FILES['uploadfile']['size']
ファイルのサイズ (バイト単位)
$_FILES['uploadfile']['tmp_name']
アップロードされたファイルのテンポラリ(一時)ファイルの名前(パス)

後は、サーバー上でファイルを保存するなら、 $_FILES['uploadfile']['tmp_name'] をどこかにコピーすれば良いし、ファイルが CSV 等で、その中身を見て何らかの処理をするなら、$_FILES['uploadfile']['tmp_name'] を開いて中身を読めば良いわけです。

関連するブログ記事
カテゴリー
PHP | perl/CGI
ã‚¿ã‚°
CGI | CGI.pm | enctype属性 | form要素 | input要素 | MIME | perl | PHP | xhtml | アップロード

コメント(137)

 

SEO対策として使われる、perl の CGI や PHP に GET で渡す値を「/」(スラッシュ)で区切る方法

よく「CGI や PHP は SEO に不利だ!」なんてことが言われますが、これは技術的なことをよく理解していない人に向けて、難しいことを極端に省略した説明なので、大きな誤解を孕んでいます。

正確には、

http://www.abe-tatsuya.com/aaa.cgi?v1=bbb&v2=ccc

のようなURIの、CGI や PHP に値を GET メソッドで渡す「?v1=bbb&v2=ccc」といった部分が問題なわけです。
こういった値を渡さない、シンプルな URI であれば、拡張子が何であろうが関係はないはずです。

なので、逆に CGI や PHP でなく、拡張子が .html であっても、

http://www.abe-tatsuya.com/aaa.html?v1=bbb&v2=ccc

のような URI なら、同様に検索エンジン側から「これは動的なURIだ」と判断されて、低く評価されるはずです。
(アクセス解析目的とか、ページ側で JavaScript 等で何かの処理をする際に、こういう手法を取ることはあり得ますよね)

で、そういった CGI や PHP 等に、GET メソッドで値を渡した結果によって表示する内容を変えるようなコンテンツの場合、検索エンジンから低く評価されることを避けるために、下記のような URI で処理することが多いです。

http://www.abe-tatsuya.com/aaa/bbb/ccc

最初の例との違いは、以下の通りです。

  1. 「aaa.cgi」の拡張子「.cgi」がない
  2. 「ここからが GET メソッドで渡す値ですよ」という意味の記号「?」がなく、代わりに「/」で区切っている
  3. GET メソッドで渡す値の名前(「v1=」「v2=」)がない
  4. GET メソッドで渡す値の区切りの記号「&」がなく、代わりに「/」で区切っている

これで、一見して動的な URI ではなく、単なるディレクトリ内にあるファイルの URI のように見せることができるわけです。

これを実現するためにやらないといけないことは、

  1. 「aaa」という拡張子のないファイルが、CGI または PHP のスクリプトであることをサーバーに教えてあげる
  2. 「aaa」のスクリプト内で、「/」区切りで渡される値を受け付ける処理を書く

大きく分けて、この二点です。

拡張子のないファイルを CGI や PHP として動作させる方法

「aaa」のような拡張子のないファイルを、CGI または PHP として動作させるには、.htaccess を使います。

特定のファイルに対するアクセス制限や、.htaccess で特定のファイルのみにBasic認証を設定する方法の応用で、

<Files aaa>
ForceType cgi-script
</Files>

と書けば、「aaa」というファイルが CGI として実行されます。

PHP として実行したい場合は、

<Files aaa>
ForceType application/x-httpd-php
</Files>

と書けば、「aaa」というファイルが PHP として実行されます。

perl の CGI や PHP で、「/」(スラッシュ)で区切られた値を受け取る方法

とりあえず「aaa」のようなファイル名のスクリプトが動くようになりました。
次は「/bbb/ccc」のような、一見ただのディレクトリへのリクエストのように見える URI を、スクリプトに渡される値として処理するための方法です。

これには、環境変数 PATH_INFO を参照します。

例えば、perl の CGI である「http://www.abe-tatsuya.com/aaa」に対して、「/bbb/ccc」という値を渡す、つまり、「http://www.abe-tatsuya.com/aaa/bbb/ccc」という URI にアクセスさせる場合、「aaa」というスクリプトの中で、$ENV{'PATH_INFO'}を参照すると、その内容は「/bbb/ccc」となります。

なので例えば、「aaa」という CGI スクリプトの最初のほうに、

#-- PATH_INFO を「/」で分けて配列に格納
@params = split(/\//,$ENV{'PATH_INFO'});
shift(@params);

等とすれば、$params[0],$params[1]... の順番に、「/」区切りで渡された値が入ります。

PHP の場合は、$_SERVER['PATH_INFO']を参照すれば、同様に処理できます。

// PATH_INFO を「/」で分けて配列に格納
$params = split("/",$_SERVER['PATH_INFO']);
array_shift($params);

これで、perl と同様に、$params[0],$params[1]...の順番に、「/」区切りで渡された値が入ります。

後は、渡された値に応じて、出力内容を変えるように色々とスクリプトを書けば OK です。

関連するブログ記事
カテゴリー
.htaccess | PHP | SEO(検索エンジン最適化) | perl/CGI
ã‚¿ã‚°
.htaccess | CGI | ForceType | GETメソッド | PATH_INFO | perl | PHP | SEO | 環境変数

コメント(304) | トラックバック(1)

 

ケータイの端末識別情報を取得する方法

「IDとパスワードを使ってログインする」というステップを省略するために、PC用のWeb上のサービスやシステムでは Cookie を使うことがよくあります。

ところがPCと違ってケータイは、Cookieに対応していない端末が多数を占めます。
そのため、ケータイ向けのWeb上のサービスやシステムを作る際、ログインを簡略化するために、「サブスクライバID」とか「端末シリアル番号」とか「固体識別情報」とか呼ばれるものを使うことが、よくあります。

これは、それぞれの携帯電話個別に、電話番号とは別に割り振られた固有のIDのようなもので、多分 mixi の「かんたんログイン」とかも、これを取得して認証しているはず。

DoCoMo と Softbank は未確認ですが、少なくとも au の場合は、機種変更をしても、この番号は引き継がれるので、ケータイ所有者個人を識別するためにはかなり有効です。

ただし、どこのキャリアかは知りませんが、「解約して使われなくなった端末識別情報が、別の誰かに割り振られることがある」なんてことを聞いたことがあるので、この端末識別情報に頼りすぎたログイン認証システムは、安全ではないかもしれないことも付記しておきます。

DoCoMo の iモード端末の固体識別情報

html の a要素や form要素に utn属性 をつけることによって、そのリンクや送信ボタンをクリックした際に、サーバーに固体識別情報が送信されます。
なお、送信される前に端末側には「固体識別情報を送信しますか?」みたいな確認ダイアログが表示され、同意した場合のみ送信されます。

<!-- a要素の場合 -->
<p><a href="http://www.abe-tatsuya.com/login.cgi" utn>ログイン</a></p>

<!-- form要素の場合 -->
<form method="POST"
action="http://www.abe-tatsuya.com/login.cgi" utn>
  <input type="submit" value="ログイン" />
</form>

本当は valid な xhtml にするために、utn="utn" としたいところなんですが、ケータイというのは機種ごとに仕様が異なっていたりして、「utn="utn"」という書き方だと正常に動作してくれない機種なんかもありそうで怖いので、DoCoMo の utn属性の説明ページの通りに書いています。
全機種の動作確認ができる機会も、まずないですし……。

このエントリーのコメントでご指摘頂いた通り、i-mode HTML/XHTML 比較表を見ると、「utn="utn"」という書き方で大丈夫なようです。
コメント頂いたyurikoさん、ありがとうございました。

で、サーバー側に送られてくる固体識別情報はどこに含まれているかというと、User-Agent ヘッダに含まれています。

非FOMA端末の場合、固体識別情報を含んだ User-Agent は以下のようになります。

DoCoMo/1.0/X503i/c10/ser***********

「ser***********」の「***********」の部分には、11桁の英数字が入り、これが固体識別情報に当たります。

perl でこれを取得しようとすると、以下のような感じでしょうか(普段自分が使ってるソースは、他のサブルーチンから渡される変数を色々使ってて、ブログに掲載しても可読性が低いと思われるので新たに書きました。以下のソースは動作確認を全くしてません)。

if($ENV{'HTTP_USER_AGENT'} =~ /^DoCoMo\/1\.0\/.*\/(ser\w{11})$/){
	#-- 固体識別情報を変数に代入
	$mobile_id = $1;
}

FOMA端末の場合は「携帯電話の製造番号」と「FOMAカードの製造番号」の二種類の固体識別情報が取得できます。
「携帯電話の製造番号」の方は、機種変更をしたら変わってしまうと思うんですが、「FOMAカードの製造番号」の方は、多分機種変更をしても、FOMAカードが同一であれば変わらないはず。

FOMA端末の、固体識別情報を含んだ User-Agent は以下の通り。

DoCoMo/2.0 YYYY(c10;serXXXXXXXXXXXXXXX; iccxxxxxxxxxxxxxxxxxxxx)

「serXXXXXXXXXXXXXXX」の部分が携帯電話の製造番号で、「XXXXXXXXXXXXXXX」には15桁の英数字が入ります。
「iccxxxxxxxxxxxxxxxxxxxx」の部分がFOMAカードの製造番号で、「xxxxxxxxxxxxxxxxxxxx」には20桁の英数字が入ります。

用途にもよりますが、基本的には FOMAカードの製造番号を取得した方が便利ですかね。
FOMAカードの製造番号を取得する perl のスクリプトは以下(上の同じく、以下のソースは動作確認を全くしてません)。

if($ENV{'HTTP_USER_AGENT'} =~ /^DoCoMo\/2\.0 .*; (icc\w{20})\)$/){
	#-- 固体識別情報を変数に代入
	$mobile_id = $1;
}

au の EZweb 端末のサブスクライバID

au の場合は、自動的にサブスクライバIDと呼ばれる X_UP_SUBNO ヘッダを、サーバーに送信しています。

ただし、ユーザー側がこのヘッダを「通知しない」という設定にしている場合は、送信しません。
通知設定に関しては、au 公式サイトのお知らせにあります。

サーバーに送信される X_UP_SUBNO ヘッダは、以下のようなフォーマットになっています。

xxxxxxxxxxxxxx_**.ezweb.ne.jp

最初の「xxxxxxxxxxxxxx」の部分が14桁の数字で、端末固有の番号にあたるようです。
※参考: モバイルCGI研究(EZweb編)環境変数リファレンス [CGIぽん]

サブスクライバIDを取得する perl スクリプトは以下の通り(同様に以下のソースは動作確認を全くしてません)。

if($ENV{'HTTP_X_UP_SUBNO'} =~ /^(\d{14})_\w{2}\.ezweb\.ne\.jp$/){
	#-- サブスクライバIDを変数に代入
	$mobile_id = $1;
}

Softbank 端末の端末シリアル番号

Softbank の端末の場合、ユーザーが「ユーザーID通知」または「製造番号通知」の設定を on にしていれば、端末シリアル番号というものが User-Agent に含まれます。

端末の世代によって、フォーマットが異なってややこしいですが、ソフトバンクの公式サイトのユーザーエージェントについての説明によると、以下のようなパターンがあるらしいです。

#-- SoftBank 6-5 Series
J-PHONE/4.0/J-SH51/SN************ SH/0001a Profile/MIDP-1.0 Configuration/CLDC-1.0
Ext-Profile/JSCL-1.1.0

#-- SoftBank 3G Series (Vodafone時代の端末)
Vodafone/1.0/V904SH/SHJ001/SN************ Browser/VF-NetFront/3.3 Profile/MIDP-2.0 
Configuration/CLDC-1.1
#-- SoftBank 3G Series (Softbank時代の端末)
SoftBank/1.0/910T/TJ001/SN************ Browser/NetFront/3.3 Profile/MIDP-2.0 
Configuration/CLDC-1.1

「SN************」の部分が端末シリアル番号で「************」には11~15桁の英数字が入るようです。

端末シリアル番号を取得する perl スクリプトは、ちょっといいかげんですが以下のような感じ(同様に以下のソースは動作確認を全くしてません)。

if($ENV{'HTTP_USER_AGENT'} =~ /^(J\-PHONE|Vodafone|Softbank)\/.*\/(SN\w{11,15}) .*/){
	#-- 端末シリアル番号を変数に代入
	$mobile_id = $2;
}

端末識別情報に頼りすぎないこと

ログイン認証を端末識別情報に依存しすぎたせいで、「機種変更したらログインできない」なんていう事態に陥ったサービスを知っています。

また、たまたま携帯事業者側の都合で、全く同じ番号の端末識別情報が複数の端末に割り振られた、なんてケースも聞いたことがあります。

ケータイは文字入力が不便なインターフェイスなので、ログインの簡略化のために端末識別情報を使うのは、ユーザーの利便性向上のためにも、良いことだとは思うのですが、認証をこれだけに頼るのは、ちょっと危険かもしれません。

関連するブログ記事
カテゴリー
perl/CGI | モバイル(携帯電話・ケータイ)Web
ã‚¿ã‚°
au | a要素 | Cookie | DoCoMo | form要素 | HTTPヘッダ | perl | User-Agent | utn属性 | xhtml | X_UP_SUBNO | アクセス制限 | サブスクライバID | ソフトバンク | モバイル | 固体識別情報 | 正規表現 | 環境変数 | 端末シリアル番号

コメント(10)

 

メール受信時に perl スクリプトを起動して自動処理させる方法

メールが届くと同時に何らかのアクションを起こすプログラムというのは、かなり作る機会の多い部類に入るかと思います。

ここ数年で特に多いものだと、ケータイ向けサイトの案件で、空メールを受信したら自動でユーザー登録用のフォームのアドレスを書いたメールを、空メール送信者に送る、みたいなものとか、ブログやSNSの日記なんかを、ケータイメールで投稿できるようにする処理とかですね。
そして王道の自動返信メールとか。

後、途中で飽きちゃったんですが、昔、送られてくるメールの内容を自分で学習して言葉を覚えて、文章を生成して返信する bot (人工無脳)を趣味で作ってたことがあります。

で、実際こういう「メールを受け取ったら何らかの処理を自動で行なう」という機能を実現するには、どうすれば良いかというと、

  1. 特定のメールアカウントにメールが届いた際に、何らかのプログラムが起動するように設定する
  2. そのプログラムを書く

という二つのステップが必要になります。

まずは、この1ステップ目の「特定のメールアカウントにメールが届いた際に、何らかのプログラムが起動するように設定する」方法について、簡単にまとめてみます。

aliases を使う場合

aliases の設定と使い方についてのメモにも書きましたが、下記のようにすることで、特定のアカウントに届いたメールを、パイプを使って直接何らかのプログラムに渡して実行することができます。

entry: "|/home/tatsuya/entry.pl"

上記の例では、「entry」というメールアカウントにメールが届いた際に、自動的に「/home/tatsuya/entry.pl」という perl スクリプトが起動するように設定しています。

~/.forward を使う場合

サーバーの各ユーザーアカウント毎に、個別に設定が可能な ~/.forward を使う場合は(レンタルサーバーを使う場合はこちらの方が利用する機会が多いかもですね)、~/.forward に以下のように書けばOKです。

"|exec /home/tatsuya/entry.pl"

procmail を使う場合

届いたメールに対して、自動でマッチング処理等を行なって、結果によって行なう処理を振り分けてくれる便利な存在である procmail を使う場合は、~/.procmailrc に以下のように書きます。

:0 :
* ^To: entry
| /home/tatsuya/entry.pl

「^To: entry」の部分は正規表現が使えるので、メールのヘッダ(FromやSubject等)に対して細かい条件を設定して振り分けることで、一つのメールアカウントで様々な処理を行なうことができるのも良いですね。

メールを処理する perl スクリプトの書き方

さて、続いて第2ステップ「そのプログラムを書く」の部分です。

aliases、~/.forward、procmail の三種類の設定方法を紹介しましたが、どの方法を用いても、「指定したプログラムの標準入力に、受け取ったメールの内容を渡す」というのは同じです。

つまり、受け取る側の perl スクリプトでは、標準入力の内容を読めば良いだけです。

while(<STDIN>){
	#-- 「$_」にメールの内容(ヘッダも本文も添付ファイルも全て)が
	#-- 一行ずつ代入されていくので、正規表現とかを使って色々処理する
}

これだけです。

後は、「メール送信者のアドレスをデータベースに登録しておいて、ユーザー登録用のURIを生成してメールを返信する処理を書く」とか、「メール送信者のアドレスからユーザーIDを引っ張ってきて、メール本文の内容をそのユーザーIDの日記に投稿させる」とか、「本文を形態素解析して、内容を bot (人工無脳)に覚えさせる」とか、色々とやれば良いわけです。

関連するブログ記事
カテゴリー
Linux/UNIX | perl/CGI
ã‚¿ã‚°
.forward | aliases | perl | procmail | sendmail | SMTP | UNIX | ケータイメール | コマンドライン | メール | モバイル | レンタルサーバー | 人工無脳

コメント(14) | トラックバック(3)

 

perl の CGI や PHP でよく出力する Content-type ヘッダ一覧 (MIME-type)

ここ数日、perlやPHPでファイルのダウンロード確認ダイアログを表示させる方法へのアクセスが、すば抜けて多いです。

こういうスクリプトを書こうとしている人って多いんでしょうか。

なので、件のエントリーの補完ということで、今回は perl の CGI や PHP で個人的によく出力する or 今後出力する機会がありそうな Content-type ヘッダをまとめておきます。
まとめとけば自分が後で一番楽だし。

テキスト・文書・MSオフィス関連

ファイルの種類 拡張子 MIME-Type
テキスト文書 .txt text/plain
CSVファイル .csv text/csv
TSVファイル .tsv text/tab-separated-values
ワード文書 .doc application/msword
エクセルシート .xls application/vnd.ms-excel
パワーポイント .ppt application/vnd.ms-powerpoint
PDF文書 .pdf application/pdf
Docuworks .xdw application/vnd.fujixerox.docuworks
HTML文書 .html .htm text/html
スタイルシート .css text/css
JavaScriptファイル .js text/javascript
HDML文書 .hdml text/x-hdml

画像関連

ファイルの種類 拡張子 MIME-Type
JPEG .jpg .jpeg image/jpeg
PNG .png image/png
GIF .gif image/gif
ビットマップ .bmp image/bmp
イラストレーター .ai application/postscript

音声関連

ファイルの種類 拡張子 MIME-Type
MP3 .mp3 audio/mpeg
MP4 .m4a audio/mp4
WAV .wav audio/x-wav
MIDI .mid .midi audio/midi
SMAF .mmf application/x-smaf

動画関連

ファイルの種類 拡張子 MIME-Type
MPEG .mpg .mpeg video/mpeg
WMV .wmv video/x-ms-wmv
Flash (Shockwave) .swf application/x-shockwave-flash
3GPP2 .3g2 video/3gpp2

圧縮ファイル関連

ファイルの種類 拡張子 MIME-Type
ZIP形式 .zip application/zip
LZH形式 .lha .lzh application/x-lzh
tar / tar+gzip形式 .tar .tgz application/x-tar

その他

ファイルの種類 拡張子 MIME-Type
実行ファイル .exe application/octet-stream

perl なら出力の一番最初に、

print qq|Content-type: MIME-Type\n|;

PHPなら出力の一番最初に、

header("Content-type: MIME-Type");

とやれば良いわけです。

関連するブログ記事
カテゴリー
PHP | perl/CGI
ã‚¿ã‚°
CGI | Content-type | HTTPヘッダ | MIME | perl | PHP | RFC | ダウンロード

コメント(754) | トラックバック(3)

 

Yahoo!モバイルのjigブラウザ機能の問題点と、perlとPHPでのjigブラウザのUser-Agent振り分け方法

「ケータイでPCのサイトが閲覧できる!」という謳い文句の割に、所謂「フルブラウザ」と比較しても機能的に中途半端で(特にJavaScript非対応はイタい)、個人的にはあんまり使い物にならないと思っている jigブラウザ。

同一URLで、User-Agentを元にして、ケータイとPCを自動で振り分ける際に、jigブラウザをPCサイト側に振り分けると、サイトの構成によっては、あまりいただけないことになってしまいます。

特に問題なのが、ケータイ版のYahoo!検索「Yahoo!モバイル」で検索した際に「PCサイトとの一致」でヒットしたサイトは、自動的にjigブラウザで表示されてしまうこと。

Yahoo!モバイルのインデックスが微妙なせいで、ケータイサイトとPCサイトを同一URLで公開していると、「携帯サイトとの一致」ではヒットしないけど「PCサイトとの一致」ではヒットする、みたいなことも起こりうるんですよね。

「PCサイトとの一致」でヒットしたサイトを、自動でjigブラウザで表示するのは基本的には便利な機能なんですが……。
こっちはちゃんとケータイに合わせた仕様でのサイトを提供しているんだから、わざわざ中途半端な機能しかないjigブラウザでPCサイト見るくらいなら、普通にケータイ用のブラウザでケータイ向けサイトを見てもらった方が、まともにサイト内のコンテンツを利用することができるわけです。

そういうわけで、jigブラウザからのアクセスの場合は、それを判別して、サイト内に注意書きを自動で入れるとか、そもそもjigブラウザでもケータイ向けサイトを表示するようにするとか、そんな対処が必要になります。

「Yahoo!モバイル」等で提供されている「jigブラウザ WEB」のデフォルトのUser-Agentは、

Mozilla/4.0 (jig browser web; version; 機種名

という感じです。
「version」はjigブラウザのバージョン。「1.0.4」みたいな数字が入る。
「機種名」は利用している携帯の機種名。「F904i」「812SH」みたいなのが入る。

なのでとりあえず、User-Agent内に「jig browser web」が含まれている場合はjigブラウザだと判断して、何らかの処理をするのが手っ取り早いかと思います。

perlなら、

if($ENV{'HTTP_USER_AGENT'} =~ /jig browser web/){
	#-- ここにjigブラウザの場合の処理
}

PHPなら、

if(preg_match("/jig browser web/",$_SERVER['HTTP_USER_AGENT'])){
	// ここにjigブラウザの場合の処理
}

こんな感じで。

ちなみに、GoogleもPCサイトの検索結果に出たサイトを携帯で見る場合、独自のブラウザを介して表示しますが、こっちは未調査です。
そのうち調べます。

関連するブログ記事
カテゴリー
PHP | perl/CGI | モバイル(携帯電話・ケータイ)Web
ã‚¿ã‚°
jigブラウザ | perl | PHP | User-Agent | Yahoo!モバイル | YST | モバイル | 環境変数

コメント(8)

 

perlやPHPでファイルのダウンロード確認ダイアログを表示させる方法

perlやPHPを使って、ユーザーにWebブラウザから何らかのファイルをダウンロードしてもらうスクリプトというのは、作る機会も結構多いかと思います。

しかし、ダウンロードしてもらうファイルがjpegやpng、gif等の画像ファイルだったり、PDFやワード・エクセル等のファイルだったり、音声や動画ファイルだったり等、ブラウザでそのまま閲覧できるファイルだった場合、ローカルにダウンロードするダイアログが出ずに、そのままブラウザに表示されてしまうことがよくあります。

そういう場合、「『右クリック⇒対象をファイルに保存』でローカルに保存できます」なんて注意書きをつけて、そのCGIやPHPスクリプトにリンクを張ることも多いかもしれませんね。

この現象は、CGIやPHPからブラウザに送信するHTTPヘッダを少し変えることで、避けることが可能です。
(ただし、ブラウザによっては駄目かも……)

具体的には、以下のようなHTTPヘッダを送信します。

Content-Disposition: attachment; filename="sample.jpg"

「sample.jpg」の部分には、ファイルのダウンロードダイアログにデフォルトで表示される、保存先ファイル名を指定します。

これを踏まえて、perlである特定のjpeg画像を、ダウンロード確認ダイアログを表示させてダウンロードさせる場合は、以下のようになります。

#-- ダウンロードさせる元ファイル
$source = '/home/tatsuya/data/img/00001.jpg';
#-- 保存時のファイル名(デフォルト)
$filename = 'uetoaya.jpg';

#-- HTTPヘッダ送信
print qq|Content-type: image/jpeg\n|;
print qq|Content-Disposition: attachment; filename="$filename"\n|;
print qq|\n|;
#-- ファイルを読み込んで出力
open(FILE,"$source");
while(<FILE>){print;}
close(FILE);

PHPの場合は、以下のような感じ。

// ダウンロードさせる元ファイル
$source = '/home/tatsuya/data/img/00002.jpg';
// 保存時のファイル名(デフォルト)
$filename = 'aibusaki.jpg';

// HTTPヘッダ送信
header("Content-type: image/jpeg");
header("Content-Disposition: attachment; filename=\"{$filename}\"");
// ファイルを読み込んで出力
readfile($source);

何らかのWebアプリケーションのバックアップデータなんかをダウンロードする場合とかは、保存時のファイル名を「ダウンロード時の日付.txt」みたいな感じにするようにしとくと便利かもですね。

関連するブログ記事
カテゴリー
PHP | perl/CGI
ã‚¿ã‚°
CGI | Content-Disposition | Content-type | HTTPヘッダ | perl | PHP | ダウンロード

コメント(34)

 

perlのCGIでよく利用する環境変数一覧

タイトル通り、perlの、特にCGIでよく使う環境変数の一覧です。

ちなみに、perlの環境変数は連想配列 %ENV に格納されていて、 $ENV{'REMOTE_ADDR'} 等として参照できます。

REMOTE_ADDR
スクリプトを実行したクライアントのリモートアドレス(IPアドレス)。
REMOTE_HOST
スクリプトを実行したクライアントのリモートホスト。
HTTP_USER_AGENT
スクリプトを実行したクライアントのユーザーエージェント
HTTP_COOKIE
スクリプトを実行したクライアントのCOOKIEの値
HTTP_HOST
リクエストされたURIのホスト名。
REQUEST_METHOD
スクリプトを実行したメソッド。GET、POST、HEAD等。
CONTENT_LENGTH
POSTメソッドで渡されたデータの長さ。
QUERY_STRING
GETメソッドで渡されたクエリの内容。所謂「●●.cgi?■■■」の■■■の部分。
PATH_INFO
リクエストされたURIのパス。最近流行の、SEOを意識してGETメソッドではなく「/」区切りでデータをCGIに渡す手法を使う際には、これを参照することが多い。
SCRIPT_NAME
実行されたスクリプトのURI上の「/」からのパス。
SCRIPT_FILENAME
実行されたスクリプトのルートからのパス。
SERVER_PORT
通信に利用したサーバーのポート。SSLと非SSLでのアクセスの判断等に使ったりする。
関連するブログ記事
カテゴリー
perl/CGI
ã‚¿ã‚°
CGI | perl | 環境変数

コメント(7)

 

perlでPostgreSQL

2007年8月11日 23:59 perlでPostgreSQL

今回も旧サイトからネタを引っ張ってきました。すんません。

PostgreSQL関連は早めに転載しとかないと、色々と困るので......。


DBIモジュールを使用する

perlにはデータベースを操作するためのモジュール「DBI」が用意されている。
Windows用のActivePerlを使う場合は、2003年3月現在は別途DBIモジュールをインストールしないといけない。
詳しいやり方は忘れた。DOS窓からppmコマンドで色々やったらインストールできたはず。

とりあえず、

#-- DBIモジュールの使用を宣言
use DBI;

データベースに接続

DBIモジュールを使ってPostgreSQLデータベースに接続するには、connect ステートメントを使用する。
「Amethyst」という名前のデータベースに、アカウント名「amethyst」パスワード「abcdefg」で接続する場合、下記のようにする。

#-- データベースに接続
$DB = DBI->connect("DBI:Pg:dbname=Amethyst","amethyst","abcdefg");

一つのプログラムで何度もデータベースに対してSQL文を発行する場合は、 その都度connectしたりせずにプログラムの最初に connect して、全ての処理が終わったら disconnect した方が良いらしい。


データの編集

データベース内のデータの編集関連のSQL文(insert,update,delete)の使用例。

#-- insert文の使用例
#-- SQL文を変数 $SQLstatに格納
$SQLstat = $DB->prepare("insert into $table_name (ID,Name) values ($id,$name)");
#-- SQL文を実行
$RESULT = $SQLstat->execute;
#-- 実行したSQL文を破棄
$SQLstat->finish;

#-- update文の使用例
#-- SQL文を変数 $SQLstatに格納
$SQLstat = $DB->prepare("update $table_name set Name='$name' where ID = '$id'");
#-- SQL文を実行
$RESULT = $SQLstat->execute;
#-- 実行したSQL文を破棄
$SQLstat->finish;

#-- delete文の使用例
#-- SQL文を変数 $SQLstatに格納
$SQLstat = $DB->prepare("delete from $table_name where ID = '$id'");
#-- SQL文を実行
$RESULT = $SQLstat->execute;
#-- 実行したSQL文を破棄
$SQLstat->finish;

SQL文内に変数を用いる場合も、数値以外はシングルクォーテーション「'」で括ること。

各SQL文を execute すると、結果が変数 $RESULT に代入される。
$RESULT に代入される値はSQL文実行によって変更のあった列数なので、処理実行後 $RESULT の値を調べることで、エラーチェックができる。

#-- エラーチェック
if($RESULT < 1){die print 'データの編集に失敗しました';}

データの検索

データベース内のデータの検索のためのSQL文(select)の使用例。

#-- SQL文を変数 $SQLstatに格納
$SQLstat = $DB->prepare("select * from $table_name where Name like '%$name%'");
#-- SQL文を実行
$SQLstat->execute;
#-- マッチした件数を取得
$match = $SQLstat->rows;
print qq|$match 件 マッチしました。\n\n|;
#-- マッチしたデータを配列 @DATA に順番に格納
while(@DATA = $SQLstat->fetchrow_array){
	($ID,$NAME,$PASS,$MAIL,$BIRTH) = @DATA;
	print qq|ID: $ID\n|;
	print qq|Name: $NAME\n|;
	print qq|Password: $PASS\n|;
	print qq|Email: $MAIL\n|;
	print qq|Birthday: $BIRTH\n|;
	print qq|\n|;
}
#-- 実行したSQL文を破棄
$SQLstat->finish;

rows は、execute したSQL文にマッチした列数を返す。
fetchrow_array は、実行されるたびに、execute したSQL文で取り出した列を順番に一行ずつ配列として返す。


プレースホルダ

下記のように、ループの中で何度も似たようなSQL文を prepare して execute して finish するのは非効率的らしい。

foreach $ID (keys(%NAME)){
	$SQLstat = $DB->prepare("update $table_name set Name='$NAME{'$ID'}' where ID = $ID");
	$RESULT = $SQLstat->execute;
	$SQLstat->finish;
	if($RESULT < 1){die print qq|ID: $ID, Name: $NAME{'$ID'} の更新に失敗しました。\n|;}
}

こういう時は、ループの外でSQL文を prepare し、変数が入る部分には「?」を入れておく。
この「?」をプレースホルダと呼ぶらしい。

$SQLstat = $DB->prepare("update $table_name set Name=? where ID = ?");
foreach $ID (keys(%NAME)){
	$RESULT = $SQLstat->execute($NAME{'$ID'},$ID);
	if($RESULT < 1){die print qq|ID: $ID, Name: $NAME{'$ID'} の更新に失敗しました。\n|;}
}
$SQLstat->finish;

このように、execute する際にプレースホルダにバインドする順番に変数を渡してやると、意図通りに実行される。
また、execute する前に bind_param でプレースホルダにバインドする値を指定する方法もある。

$SQLstat = $DB->prepare("update $table_name set Name=? where ID = ?");
foreach $ID (keys(%NAME)){
	$SQLstat->bind_param(1,$NAME{'$ID'});
	$SQLstat->bind_param(2,$ID);
	$RESULT = $SQLstat->execute;
	if($RESULT < 1){die print qq|ID: $ID, Name: $NAME{'$ID'} の更新に失敗しました。\n|;}
}
$SQLstat->finish;

bind_param 時に渡す値は「何番目のプレースホルダなのか」という数値と、「バインドする値」の二つ。


データベースとの接続を終了する

データベースを操作する処理が終了したら、データベースとの接続を終了する。

#-- データベースとの接続を終了
$DB->disconnect;
関連するブログ記事
カテゴリー
PostgreSQL | perl/CGI
ã‚¿ã‚°
perl | PostgreSQL | RDBMS | SQL

コメント(6)