SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

PerlでデスクトップCGI

デスクトップCGIでWebとデスクトップを融合する 第3回

独自の日記記述言語からHTML形式のWeb日記を出力・配信する


日記記述言語DDLからWeb日記のHTMLを生成する - ddl2html.pl

 「ddl2html.pl」は、前項のDDL記述をWeb日記のHTMLに変換するスクリプトです。以下に示すコードを見ると長大に感じるかもしれませんが、スクリプトとしては単純なものです。行指向のテキスト処理を正規表現を使って行う変換の例として参考にしていただければと思います。

 気をつける点をひとつ挙げておきます。ifelsif文でelsifの繰り返しですが、分岐条件が重ならないように書かなくてはなりません。そうしないと、最初にマッチした部分だけが処理されることになります。多重に処理をしたい場合は、分岐処理ブロックの中に書くか、別のifelsif文を重ねて処理する必要があります。

 参考になるテクニックとしては、tableデータを取得する場合に、分岐処理ブロック内でキャプションデータ行と項目データ行をwhile(<>)ループとは別に標準入力で取得している部分です。スクリプトの該当部分にコメントを付けておきます。

 他のスクリプトに関連する内容は後述します。まずはスクリプトをざっとながめてみてください。

「ddl2html.pl」 DDL記述からWeb日記のHTMLを出力するPerlスクリプト
#!/Perl/bin/MSWin32-x86-object/jperl.exe
use strict;

# メモのカテゴリのASCII文字列をキーに日本語文字列を連想配列の値として取得
# メモ格納ディレクトリにあるcategory.txt(添付)から読み込む
my $docroot = $ENV{'DOCROOT'};# メモ格納ディレクトリのある
                              # ディレクトリのフルパス
my $memodir = "memo";# メモ格納ディレクトリ名
my $catstr;
my $category;
my %categories;
if(open(IN, "<$docroot/$memodir/category.txt")){
    while(<IN>){
        chomp;
        ($catstr, $category) = split(/\t/);
        $categories{$catstr} = $category;
    }
    close(IN);
}else{
    # category.txt が無ければ、次の連想配列のデータを使う
    %categories = (
        "rn" => "更新履歴",
    );
}

my $header = <<'DDLHEADER';
<HTML>
<HEAD>
    <TITLE>更新日記</TITLE>
    <META HTTP-EQUIV="Content-Type" content="text/html;
          charset=Shift_JIS">
    <meta name="description" content="<-- your content -->">
    <meta name="keywords" content="<-- your contnet -->">
    <link rel="self" type="text/html" href="<-- your html -->">
    <link rel="alternate" type="application/rss+xml"
          href="<-- your rss -->">
    <link rel="alternate" type="application/atom+xml"
          href="<-- your atom -->">
<!-- 筆者の使用しているスタイルシートを設定(添付) -->
    <link rel="stylesheet" type="text/css" href="tsnetwork.css" />

<style type="text/css">
<!--
.invisible {
    display: none;
}
.visible {
    display: block;
}
//-->
</style>

<!--
Firefoxルビ表示用スタイルシートの設定
Mozillaでルビ表示3 - 徒書
http://www.akatsukinishisu.net/itazuragaki/id/ruby_for_Mozilla_3
からスタイルシートを取得し、firefox.cssとして保存してください。
-->
<script type="text/javascript">
<!--
if(navigator.userAgent.indexOf("Firefox") != -1){
    document.write("<link rel='stylesheet' type='text/css'
 href='firefox.css' />");
}
-->
</script>

<!-- Google Maps key setup -->
<script src="http://maps.google.com/maps?file=api&v=1&
key=(Google key 文字列)" type="text/javascript">
</script>

<!-- Google Maps トグル表示用 Javascript -->
<script type="text/javascript">
<!--
<!-- 「Webページ制作テクニック 逆引き大全 500の極意」
392番の極意を参考にしました。
(西川賢哉、KENT著、チーム・エムツー編著、秀和システム、2002年)-->
function toggleVis(idName){
    var styleMenu = document.getElementById(idName).style;
    if(styleMenu.display == "block"){
        styleMenu.display = "none";
    }else{
        styleMenu.display = "block";
    }
}
<!-- Google Maps サンプルを参考にしました。-->
function onLoad(lng, lat, zoom, mapid) {
    var map = new GMap2(document.getElementById(mapid));
    map.addControl(new GSmallMapControl());
    map.addControl(new GMapTypeControl());
    var point = new GPoint(lng, lat);
    map.centerAndZoom(point, zoom);
    var marker = new GMarker(point);
    map.addOverlay(marker);
}
//-->
</script>

</HEAD>
<BODY>
<!-- your header -->
<HR>
DDLHEADER

my $footer = <<'DDLFOOTER';
<HR>
<!-- your footer -->
</BODY>
</HTML>
DDLFOOTER

# 日付時間
my $sec;
my $min;
my $hour;
my $mday;
my $mon;
my $year;
my $wday;
my $yday;
my $isdst;
my $thisday;
# カテゴリー
$category = "";
my $catid;
my $catcount = 0;
# タイトル
my $title = "";
# 段落
my $description;
# リスト
my $blevel;
my $level;
my $diff;
my $ts;
# 定義リスト
my $term;
my $definition;
# 共通
my $i;
my $str;
# 表
my $caption;
my @heads;
my @fd;
# microformat
my $style;
my $classname;
my %aary;
# 範囲指定フラグ
my $bq;
my $ps;
my $pre;
my $ul;
my $ol;
my $uls;
my $dl;
my $ta;
my $class;
while(<>){
    chomp;
    if(/^dat*e*:$/i && $pre != 1){
        ($mday,$mon,$year,$wday) = (localtime)[3..6];
        $mon += 1;
        $year += 1900;
        $thisday = ("Sun.","Mon.","Tue.","Wed.","Thu.","Fri.",
"Sat.")[$wday];
        print "<DT>$mon/$mday/$year ($thisday)<DD>\n";
        next;
    }elsif(/^up*da*t*e*d*:$/i && $pre != 1){
        ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)
 = localtime(time);
        $mon += 1;
        $year += 1900;
        printf("<div class=\"updated\">更新: %4d-%1.2d-%1.2dT
%1.2d:%1.2d:%1.2d +09:00</div>\n",$year,$mon,$mday,$hour,$min,$sec);
    }elsif(/^c:\s+(.+)$/i && $pre != 1){
        $category = $1;
        if($title ne ""){
            $catid = time + $catcount;
            print "<div class=\"emph\"><A HREF=\"renewal_index.html#
$category\">[$categories{$category}]</A>
 <A NAME=\"${category} _$catid\">$title</A></div>\n";
            $title = "";
            $category = "";
            $catcount++;
        }
        next;
    }elsif(/^t:\s+(.+)$/i && $pre != 1){
        $title = $1;
        $title =~ s/\[U:\s+([^;]+);T:\s+([^\]]+)\]/<a href="$1">$2<\/a>/i;
        $title =~ s/\[U:\s+([^;\]]+)\]/<a href="$1">$1<\/a>/i;
        if($category ne ""){
            $catid = time + $catcount;
            print "<div class=\"emph\"><A HREF=\"renewal_index.html#
$category\">[$categories{$category}]</A>
 <A NAME=\"${category} _$catid\">$title</A></div>\n";
            $category = "";
            $title = "";
            $catcount++;
        }
        next;
    }elsif(/^p:\s+(.+)$/i && $pre != 1){
        $description = &linking($1);
        print "<p>$description</p>\n";
        next;
    }elsif(/^pre:$/i){
        print "<pre>\n";
        $pre = 1;
        next;
    }elsif(/^\/pre:/i){
        print "</pre><br>\n";
        $pre = 0;
        next;
    }elsif(/^ul:$/i && $pre != 1){
        print "<ul>\n";
        $ul = 1;
        next;
    }elsif(/^\/ul:$/i && $pre != 1){
        print "</ul><br>\n";
        $ul = 0;
        next;
    }elsif(/^ol:$/i && $pre != 1){
        print "<ol>\n";
        $ol = 1;
        next;
    }elsif(/^\/ol/i && $pre != 1){
        print "</ol><br>\n";
        $ol = 0;
        next;
    }elsif(/^uls:$/i && $pre != 1){
        $blevel = 0;
        print "<ul>\n";
        $uls = 1;
        next;
    }elsif(/^\/uls:$/i && $pre != 1){
        while($blevel > 0){
            print "\t" x $blevel;
            print "</ul>\n";
            $blevel--;
        }
        print "</ul><br>\n";
        $uls = 0;
        next;
    }elsif(/^dl:$/i && $pre != 1){
        print "\t<dl>\n";
        $dl = 1;
        next;
    }elsif(/^\/dl/i && $pre != 1){
        print "\t</dl><br>\n";
        $dl = 0;
        next;
    }elsif(/^tab*l*e*:$/i && $pre != 1){
        chop($caption = <>); # 最初行のキャプションデータを取得します。
        $caption = &linking($caption);
        print "<TABLE BORDER=7>\n<CAPTION ALIGN=top><B>
$caption</CAPTION>\n";
        # 表の項目データを取得して、HTMLを出力します。
        chop($_ = <>);
        $_ = &linking($_);
        @heads = split(/\t/);
        print "<TH>";
        for($i=0; $i<$#heads; $i++){
            print "$heads[$i]</TH><TH>";
        }
        print "$heads[$#heads]</TH>\n";
        $ta = 1;
        next;
    }elsif(/^\/tab*l*e*:$/i && $pre != 1){
        print "</TABLE><BR>\n";
        $ta = 0;
        next;
    }elsif(/^bq:$/i && $pre != 1){
        $bq = 1;
        print "<BLOCKQUOTE>\n";next;
    }elsif(/^\/bq:$/i && $pre != 1){
        $bq = 0;
        print "</BLOCKQUOTE>\n";next;
    }elsif(/^ps:$/i && $pre != 1){
        $ps = 1;
        print "<div class=\"postscript\">\n";next;
    }elsif(/^\/ps:$/i && $pre != 1){
        $ps = 0;
        print "</div>\n";next;
    }elsif(/^(li|em)*(book):$/i && $pre != 1){
        $style = $1;$classname = $2;
        if($style =~ /li/i){
            print "<div class=\"$classname\">\n";
        }elsif($style =~ /em/i){
            print "<span class=\"$classname\">";
        }
        $class = 1;next;
    }elsif(/^\/(li|em)*(book):$/i && $pre != 1){
        $style = $1;
        if($style =~ /li/i){
            print "</div>\n";
        }elsif($style =~ /em/i){
            print "</span>";
        }
        $class = 0;$style = "";next;
    }elsif(/^vp:$/i){
        $style = "vp";
        $class = 1;next;
    }elsif(/^\/vp:$/i){
        $class = 0;$style = "";next;
    }elsif(/^ddl:$/i){
        print $header;next;
    }elsif(/^\/ddl:$/i){
        print "</DL>\n";
        print $footer;
    }elsif(/^-embook$/){
        print "embook:\n";
        print "author: \n著、\n";
        print "editor: \n編、\n";
        print "translation: \n訳、「\n";
        print "title: \n」、\n";
        print "pubname: \n、\n";
        print "paperback: \n";
        print "pbnumber: \n、\n";
        print "date: \n第\n";
        print "revision: \n版第\n";
        print "reprint: \n刷(\n";
        print "firstprint: \n)、(\n";
        print "original: \n)、\n";
        print "page: \nページ、\n";
        print "price: \n、ISBN\n";
        print "isbn: \n";
        print "/embook:\n";
        next;
    }elsif(/^-libook$/){
        print "libook:\n筆者:\t\n";
        print "author: \n監修・編:\t\n";
        print "editor: \n翻訳:\t\n";
        print "translation: \n題名:\t\n";
        print "title: \n発行:\t\n";
        print "pubname: \n新書・文庫:\t\n";
        print "paperback: \n新書・文庫番号:\t\n";
        print "pbnumber: \n発行日:\t\n";
        print "date: \n版数:\t\n";
        print "revision: \n刷数:\t\n";
        print "reprint: \n初版データ:\t\n";
        print "firstprint: \n原著:\t\n";
        print "original: \nページ数:\t\n";
        print "page: \n価格:\t\n";
        print "price: \nISBN:\t\n";
        print "isbn: \n";
        print "/libook:\n";
        next;
    }elsif(/^-vp$/){
        print "vp:\n";
        print "image: \n";
        print "title: \n";
        print "date: \n";
        print "description: \n";
        print "lat: \n";
        print "lng: \n";
        print "scale: 16\n";
        print "mapid: map*\n";
        print "width: 360\n";
        print "height: 240\n";
        print "/vp:\n";
        next;
    }elsif(/^\[[^[]+\]$/ && $bq != 1 && $ps != 1 && $pre != 1 &&
 $ul != 1 && $ol != 1 && $uls != 1 && $dl != 1 &&
 $ta != 1 && $class != 1){
        $_ = &linking($_);
        print;next;
    }
    if($bq == 1 || $ps == 1){
        $str = &linking($_);
        print "$str\n";
    }elsif($pre == 1){
        s/&/&/g;
        s/</</g;
        s/>/>/g;
        s/"/"/g;
        print $_, "\n";
    }elsif($ul == 1 || $ol == 1){
        $str = &linking($_);
        print "<li>$str\n";
    }elsif($uls == 1){
        $_ = &linking($_);
        if(/^(\t*)[^\t].*$/){
            $level = length $1;
            if($blevel > $level){
                $diff = $blevel - $level;
                if($diff >= 1){
                    for($i = 1;$i<=$diff;$i++){
                        $ts = "\t" x ($blevel - $i + 1);
                        print "$ts</ul>\n";
                    }
                }
                s/^(\t*)([^\t].*)$/$1<li>$2/;
            }elsif($blevel == $level){
                s/^(\t*)([^\t].*)$/$1<li>$2/;
            }elsif($blevel < $level){
                $diff = $level - $blevel;
                if($diff >= 1){
                    for($i = 1;$i<=$diff;$i++){
                        $ts = "\t" x ($blevel + $i);
                        print "$ts<ul>\n";
                    }
                }
                s/^(\t*)([^\t].*)$/$1<li>$2/;
            }
        }
        $blevel = $level;
        print $_,"\n";
    }elsif($dl == 1){
        if(/^([^:]+:) (.*)$/){
            $term = $1;
            $definition = &linking($2);
            print "\t<dt>$term\n\t<dd>$definition\n";
        }
    }elsif($ta == 1){
        # タブ区切りの規則的なテキストデータから
        # 1行ずつ変換して出力します。
        $_ = &linking($_);
        @fd = split(/\t/);
        print "<TR>\n<TD>";
        for($i=0; $i<$#fd; $i++){
            print "$fd[$i]</TD><TD>";
        }
        print "$fd[$#fd]</TD>\n</TR>\n";
    }elsif($class == 1 && $style =~ /em/i){
        if(/^([^:]+): (.+)$/i){
            print "<span class=\"$1\">$2</span>";
        }elsif(/^(.+)$/){
            print $1;
        }
    }elsif($class == 1 && $style =~ /li/i){
        if(/^([^:]+): (.+)$/i){
            print "<span class=\"$1\">$2</span><br>\n";
        }elsif(/^(.+)$/){
            print $1;
        }
    }elsif($class == 1 && $style =~ /^vp$/i){
        if(/^image: (.+)$/){
            print "<span id=\"$1\" class=\"viewpoint\">\n";
            print "<IMG SRC=\"$1\" ALIGN=LEFT HSPACE=20 ALT=\"$1\">\n";
        }elsif(/^(title|date|description): (.+)$/){
            print "<span class=\"$1\">$2</span><br>\n";
        }elsif(/^(lat|lng): (.+)$/){
            $aary{$1} = $2;
            print "<span class=\"$1\">$2</span>\n";
        }elsif(/^(scale|mapid|width|height): (.+)$/){
            $aary{$1} = $2;
            if($aary{'height'}){
                print "<a href=\"javascript:void(0)\" onclick=\"
                toggleVis('embed*');onLoad($aary{'lat'}, $aary{'lng'}, 
                $aary{'scale'}, '$aary{'mapid'}'
                );\" class=\"toggle\">地図の表示・非表示</a>\n";
                print "<BR CLEAR=ALL><br>\n<div id=\"embed*\" class=\"
                invisible\">\n";
                print "<div id=\"$aary{'mapid'}\" style=\"width: $aary
                {'width'}px; height: $aary{'height'}px\"></div>\n";
                print "</div><br>\n</span>\n";
                %aary = ();
            }
        }
    }
}
close(OUT);

sub linking{
    my($str) = @_;
    $str =~ s/\[U:\s+([^;]+);T:\s+([^\]]+)\]/<a href="$1">$2<\/a>/gi;
    $str =~ s/\[N:\s+([^;]+);T:\s+([^\]]+)\]/<a name="$1">$2<\/a>/gi;
    $str =~ s/\[U:\s+([^;\]]+)\]/<a href="$1">$1<\/a>/gi;
    $str =~ s/\[I:\s+([^;\]]+)\]/<IMG SRC="$1" ALIGN=LEFT HSPACE=20
 ALT="$1">/gi;
    $str =~ s/^\[I:\s+([^;]+);T:\s+([^\]]+)\]$/<IMG SRC="$1"
 ALIGN=LEFT HSPACE=20 ALT="$1">$2<BR CLEAR=ALL><BR>/i;
    $str =~ s/^\[I:\s+([^;]+);U:\s+([^;]+);T:\s+([^\]]+)\]$/
<IMG SRC="$1" ALIGN=LEFT HSPACE=20 ALT="$1"><a href="$2">$3
<\/a><BR CLEAR=ALL><BR>/i;
    $str =~ s/^\[U:\s+([^;]+);I:\s+([^;]+);T:\s+([^\]]+)\]$/<a href="$1">
    <IMG SRC="$2" ALIGN=LEFT HSPACE=20 ALT="$2"><\/a>$3<BR CLEAR=ALL><BR>/i;
    $str =~ s/^\[U:\s+([^;]+);I:\s+([^;]+);U:\s+([^;]+);T:\s+([^\]]+)\]$/
    <a href="$1"><IMG SRC="$2" ALIGN=LEFT HSPACE=20 ALT="$2"><\/a>
<a href="$3">$4<\/a><BR CLEAR=ALL><BR>/i;
    $str =~ s/\[rb: ([^;]+);([^]]+)\]/<ruby><rb>$1<\/rb><rp>
(<\/rp><rt>$2<\/rt><rp>)<\/rp><\/ruby>/ig;
    return $str;
}

スクリプトを動作させるために

 「ddl2html.pl」を動作させるために必要なものを確認しましょう。

添付するもの

  1. 日記のカテゴリーのキー文字列と表示文字列をタブで区切った「category.txt」
  2. 筆者の使っているスタイルシート「tsnetwork.css」

 「category.txt」は$docroot$memodirを調整して、ファイルが置かれているディレクトリを指定してください。「tsnetwork.css」は日記のHTMLと同じディレクトリに格納してください。

Firefoxにおけるルビ表示

 Firefoxでルビ変換の機能を試すためには、Firefoxルビ表示用スタイルシートの設定を行う必要があります。「Mozillaでルビ表示3 - 徒書」からスタイルシートを取得し、「firefox.css」というファイル名で「tsnetwork.css」と一緒に格納してください。

microformatsについて

 microformatsはメタデータをHTMLに埋め込む方法として注目されています。HTMLに埋め込まれたメタデータを活用するアイデアが別に必要ですが、本稿では単にDDLのテンプレートから必要な項目を入力して、HTMLに変換するだけにとどめます。

 microformatsは、divタグやspanタグのclass属性とid属性を使って、メタデータを書き込みます。class属性は、同一のHTMLのページの中で同じ値を持つ複数のdivタグやspanタグを持つことができます。id属性は同一のHTMLのページの中で必ず異なる値を持ち、name属性と同じように使えます。class属性をXML要素名としてXMLに変換していくことは簡単でしょう。id属性の値は、複数の同一カテゴリー要素を区別するために用いることもできます。

 現在、筆者のWeb日記では、テスト的に書籍データをbookというmicroformatsに格納しつつあります。また、地点情報をviewpointというmicroformatsに格納しています。こちらのほうは、Google Mapsと連動する仕組みを作る予定です。現在では、写真撮影した地点を個別のGoogle Mapsに表示するために使っています。地図の表示・非表示はトグルになっています。このGoogle Mapsは、Firefoxでは表示できますが、インターネットエクスプローラでは表示できないようです(現時点では原因は不明です)。

筆者 Web日記のviewpoint表示 - 地図非表示画面
筆者 Web日記のviewpoint表示 - 地図非表示画面
筆者 Web日記のviewpoint表示 - 地図表示画面
筆者 Web日記のviewpoint表示 - 地図表示画面

 Google Mapsを使用する場合は、各人でキーを取得して、スクリプトのDDLHEADERにあるGoogle Maps key setupの部分に書き込んでください。サイトごとにひとつのキーが付与されます。Google マップ ヘルプを参考にしてください。

 microformatsについては、「Microformats」を参照してください。メーリングリストも活発ですし、ブログも用意されています。

次のページ
知識循環型データベースとWeb日記検索結果のHTML出力 - renewal_r2html.pl

この記事は参考になりましたか?

PerlでデスクトップCGI連載記事一覧

もっと読む

この記事の著者

jscripter(ジェイスクリプター)

NIFTY SERVEのFGALEL(エントリーラボ)、FGALTS(テキスト&スクリプト)に参画し、FGALTSでシスオペを務める。現在、TSNETメンバー。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/570 2007/02/22 12:31

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー