Category Archives: program

Twitterの日本語ハッシュタグ抽出する正規表現


正式に日本語ハッシュタグが使えるようになったので、色々ためした結果、以下の正規表現でほぼ公式と同じ動作になった。「ほぼ」なのはちゃんとテストしたわけじゃないので。使うなら自己責任でどうぞ。

(?:#|\uFF03)([a-zA-Z0-9_\u3041-\u3094\u3099-\u309C\u30A1-\u30FA\u3400-\uD7FF\uFF10-\uFF19\uFF20-\uFF3A\uFF41-\uFF5A\uFF66-\uFF9E]+)

iPhoneの公式クライアントだとなんか動きが違ったりするし、half-widthのカタカナ半濁点はだめだったりとかなんかバグなのか仕様なのかよくわからず。半濁点も通すようにしたければ、FF9EをFF9Fにすればおk。

mahoutで類似ユーザをリコメンドする


mahout使って類似ユーザの抽出をやってみたのでまとめ。

そこら辺に転がってるサンプルは、ほとんどがユーザがあるアイテムについて点数をつける前提になってて、その点数をベースに似ているユーザをリコメンドしたり、あるいはあるユーザが興味をもちそうなアイテムをリコメンドしたりといったものが殆どだった。点数がないようなときにどうするか多少悩んだので記録に残しておくことにした。とりあえず今回はMySQLにデータが入ってる前提でやってみた。あるテーブルにUSER_IDとITEM_IDが入ってて、同じITEM_IDを多く選んだ人を探したいとすると、以下のようにする。

package test;

import test.DataSouceManager;
import org.apache.mahout.cf.taste.common.TasteException;
import org.apache.mahout.cf.taste.impl.model.jdbc.MySQLBooleanPrefJDBCDataModel;
import org.apache.mahout.cf.taste.impl.neighborhood.NearestNUserNeighborhood;
import org.apache.mahout.cf.taste.impl.recommender.GenericUserBasedRecommender;
import org.apache.mahout.cf.taste.impl.similarity.LogLikelihoodSimilarity;
import org.apache.mahout.cf.taste.model.DataModel;
import org.apache.mahout.cf.taste.neighborhood.UserNeighborhood;
import org.apache.mahout.cf.taste.similarity.UserSimilarity;

public class RecommendTest {
	public static void main(String[] args) {
		DataModel model = new MySQLBooleanPrefJDBCDataModel(
			DataSourceManager.getInstance().getDataSouce(),
			"TABLE_NAME",
			"USER_ID_COLUMN_NAME",
			"ITEM_ID_COLUMN_NAME",
			null //TIMESTAMP_COLUMN_NAME
		);
		UserSimilarity similarity = null;
		UserNeighborhood neighborhood = null;
		try {
			similarity = new LogLikelihoodSimilarity(model);
			neighborhood = new NearestNUserNeighborhood(100, similarity, model);
		} catch (TasteException e) {
			e.printStackTrace();
		}
		GenericUserBasedRecommender recommender = new GenericUserBasedRecommender(model, neighborhood, similarity);
		try {
			long[] recommendations = recommender.mostSimilarUserIDs(1, 1);
			for (int i = 0; i < recommendations.length; i++) {
				resp.getWriter().println(recommendations[i]);
			}
		} catch (TasteException e) {
			e.printStackTrace();
		}
	}
}

統計学の知識がないのでドキュメント読んでもよくわからなかったので、これであってるのかどうかはよくわからん。色々試して、LogLikelihoodSimilarityじゃないと点数のフィールドがねーから使えないよと怒られた。GenericUserBasedRecommenderはRecommenderってIFで受けるのが筋なんだろうけど、そうするとmostSimilarUserIDSメソッドが使えないのでそのままの型で受けてる。

mostSimilarUserIDsの第一引数が比較したいユーザID、第二引数が何人リコメンドするか。少ないデータで試して、期待する結果が返ってくるのは確認済み。んで大量のデータだとどうなんだろうと思って、以下のスクリプトで140万件のデータを作ってみた。

日付・ユーザID・アイテムIDで、ユーザIDは一日につき一回しかでてこないようにデータを生成。

import string
import random
import sys

if __name__ == '__main__':
  for d in xrange(20110201, 20110229):
    idmap = []
    for i in xrange(50000):
      while(1):
        id = random.randint(1,300000)
        try:
          idmap.index(id)
        except ValueError:
          break
        continue
      idmap.append(id)
      print "insert into tbl values ('" + str(d) + "'," + str(id) + "," + str(random.randint(1,3*28)) + ");"

こいつでSQLをつくってMySQLに食わせる。

$ python generate_date.py|mysql

MEMORYエンジンでテーブル作って試してみたけど、DataModelをつくるところですでに時間掛かりすぎ。リアルタイムで返事を返すようなときには全然向いてないものみたいで、やっぱHadoopとかを併用して裏で動かしておく感じで使う物みたいだ。ちなみにかかった時間はおよそ10分弱。こんな単純なデータで単純なことやりたいのであれば、mahoutじゃなくて素直にSQLでやったほうが速い。もっともっと大規模なデータを解析するのに使わないと。

cmecab-javaとLuceneでMoreLikeThis


類似検索やりたくて試したんだけどなんかうまくいかねーです。
Lucene詳しいかた、教えてくだしあ><

2009/10/07: 解決したので後ろに追記しました。

package hoge;
import java.io.*;
import net.moraleboost.lucene.analysis.ja.StandardMeCabAnalyzer;
import org.apache.lucene.*;

public class MeCabTest {
    public static final String DIC_ENCODING = System.getProperty("net.moraleboost.mecab.encoding");
    private static StandardMeCabAnalyzer analyzer = null;
    private static FSDirectory directory = null;
    private IndexWriter writer  = null;
    private static final String DIR = "/path/to/index";

    public void setUp() throws Exception {
        writer  = new IndexWriter(directory, analyzer, new MaxFieldLength(4096));
        Document doc = new Document();
        addField(doc, "text", "ほげほげ");
        ....
        ....
        writer.commit();
        writer.optimize();
        writer.close();
    }
    private void addField(Document doc, String name, String value) throws Exception {
        Field field = new Field(name, value, Store.YES, Field.Index.ANALYZED, Field.TermVector.YES);
        doc.add(field);
        writer.addDocument(doc);
    }

    public static void main(String[] args) {
        MeCabTest t = new MeCabTest();
        IndexSearcher searcher = null;
        IndexReader reader = null;
        analyzer = new StandardMeCabAnalyzer(DIC_ENCODING, "");
        directory = FSDirectory.open(new File(DIR));
        t.setUp();
        reader = IndexReader.open(DIR, false);
        searcher = new IndexSearcher(directory, true);
        QueryParser parser = new QueryParser("text", analyzer);
        parser.setDefaultOperator(QueryParser.Operator.OR);
        MoreLikeThis mlt = new MoreLikeThis(reader);
        mlt.setAnalyzer(analyzer);
        mlt.setMinTermFreq(1);
        mlt.setFieldNames(new String[]{"text"});
        Query query = mlt.like(new ByteArrayInputStream("ほげ".getBytes()));
        TopDocs topDocs = searcher.search(query, 100);
        if (topDocs.totalHits > 0) {
            for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
                Document doc = searcher.doc(scoreDoc.doc);
                System.out.println(doc.get("text"));
            }
        }
    }
}

try〜catchとか省略。addField(…);のところ、実際にはニュースサイトから本文引っ張ってきて、いくつかいれた。7個くらい適当に選んで、3個くらいは同じニュース(台風18号関連)を違うサイトから引用した。

MoreLikeThis#like()には、台風18号関連のさらに別のニュースを入れてみたが、topDocs.totalHitsがindexに入れたニュース数と同数、つまり全部ヒットになっちゃった。しかも1件目に入れたやつしか表示されない…なんのこっちゃい??

明日以降もうちょっと調べて、わかったら追記しよう…

[追記]
すげーポカやってるし…。単純にDocumentクラスを使い回しちゃダメってことみたい。そりゃそうだよね、別のDocumentとして扱いたいんだから、インスタンスも別だわな。というわけでaddFieldメソッドにDocument渡すのやめて、メソッド内でDocumentクラスのインスタンスを作るようにしたら、上手く動くようになりました。

mixiとblogをそのまま同期させると、del.icio.usのdaily postがmixiでは役立たずになってウザそうだからどうにかする


mixiにそのまま投稿しちゃうと、本来リンクじゃないと困るものが、ただのテキストになってしまうわけで。どっちみち携帯から見てる人には無用なエントリになっちゃうし、いっそのことmixiには投稿しないようにしようと。一番てっとり早いのはP::P::MixiDiary.pm内で、単純にif文でほげほげすることなんだけど。ちょっと気が向いたのでplugin初挑戦してみた。

package Plagger::Plugin::Filter::StripDeliciousDailyPost;
use strict;
use base qw( Plagger::Plugin );

sub register {
    my($self, $context) = @_;
    $context->register_hook(
        $self,
        'update.entry.fixup' => \&filter,
    );
}

sub filter {
    my($self, $context, $args) = @_;

    my $title = $args->{entry}->title;
    for my $entry ($args->{feed}->entries) {
        if ($entry->title =~ /^links for \d{4}-\d{2}-\d{2}$/i) {
            $context->log(info => "Delete Delicious daily post entry " . $entry->link);
            $args->{feed}->delete_entry($entry);
        }
    }
}

1;
__END__

やはりperlは書き慣れんな。yamlを以下のようにして動作確認。

plugins:
  - module: Subscription::Config
    config:
      feed:
        - url: http://nobu666.com/rss?feed=rss2
  - module: Filter::Rule
    rule:
      module: Deduped
      path: /tmp/blog2mixi.tmp
      compare_body: 1
  - module: Filter::Reverse
  - module: Filter::FindEnclosures
  - module: Filter::FetchEnclosure
    config:
      dir: /tmp/fetch-image
  - module: Filter::FormatText
  - module: Filter::EntryFullText
  - module: Filter::StripDeliciousDailyPost
  - module: Publish::MixiDiary
    config:
      username: メールアドレス
      password: パスワード
      interval: 10
      originally_link: 1

wordpressのformatterがキモいタグを生成するので、どうにかする – v2.3.3の場合


前回のwordpressのformatterがキモいタグを生成するので、どうにかするはME2.2.3以外でpatchすると、管理画面で激しくwarningが出てエライことになる。ので、v2.3.3用のpatch。

--- www/wp-includes/formatting.php.orig Thu Feb 28 16:37:29 2008
+++ www/wp-includes/formatting.php      Thu Feb 28 16:39:27 2008
@@ -63,7 +63,7 @@
        $pee = $pee . "\n"; // just to make things a little easier, pad the end
        $pee = preg_replace('|<br />\s*<br />|', "\n\n", $pee);
        // Space things out a little
-        $allblocks = '(?:table|thead|tfoot|caption|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li|pre|select|form|map|area|blockquote|address|math|style|input|p|h[1-6]|hr)';
+       $allblocks = '(?:code|table|thead|tfoot|caption|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li|pre|select|form|map|area|blockquote|address|math|style|input|p|h[1-6]|hr)';
        $pee = preg_replace('!(<' . $allblocks . '[^>]*>)!', "\n$1", $pee);
        $pee = preg_replace('!(</' . $allblocks . '>)!', "$1\n\n", $pee);
        $pee = str_replace(array("\r\n", "\r"), "\n", $pee); // cross-platform newlines
@@ -88,6 +88,10 @@
        if (strpos($pee, '<pre') !== false)
                $pee = preg_replace_callback('!(<pre.*?>)(.*?)</pre>!is', 'clean_pre', $pee );
        $pee = preg_replace( "|\n</p>$|", '</p>', $pee );
+        $pee = preg_replace('| {4}|', str_repeat(' ',4), $pee);
+        $pee = str_replace('\\', '\\\\', $pee);
+        $pee = preg_replace('|<code>.*?</code>|se', "str_replace(array('<p>', '</p>'), array('', '<br /><br />'), '$0')", $pee);
+        $pee = StripSlashes($pee);

        return $pee;
 }

wordpressのformatterがキモいタグを生成するので、どうにかする – ME2.2.3の場合


<p></code></p>とかいう謎タグが生成されて激しく気持ち悪いので、phpに手を入れる。As You Like It ? Blog Archive ? WordPressのエディタとの付き合い方を参照して、その通りに書き換えるだけ。patchにしとく。

--- formatting.php.org  Fri Feb 22 18:23:52 2008
+++ formatting.php      Fri Feb 22 18:21:16 2008
@@ -55,7 +55,7 @@
        $pee = $pee . "\n"; // just to make things a little easier, pad the end
        $pee = preg_replace('|<br />\s*<br />|', "\n\n", $pee);
        // Space things out a little
-       $allblocks = '(?:table|thead|tfoot|caption|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li|pre|select|form|map|area|blockquote|address|math|style|input|p|h[1-6]|hr)';
+       $allblocks = '(?:code|table|thead|tfoot|caption|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li|pre|select|form|map|area|blockquote|address|math|style|input|p|h[1-6]|hr)';
        $pee = preg_replace('!(<' . $allblocks . '[^>]*>)!', "\n$1", $pee);
        $pee = preg_replace('!(</' . $allblocks . '>)!', "$1\n\n", $pee);
        $pee = str_replace(array("\r\n", "\r"), "\n", $pee); // cross-platform newlines
@@ -80,6 +80,13 @@
        if (strpos($pee, '<pre') !== false)
                $pee = preg_replace('!(<pre.*?>)(.*?)</pre>!ise', " stripslashes('$1') .  stripslashes(clean_pre('$2'))  . '</pre>' ", $pee);
        $pee = preg_replace( "|\n</p>$|", '</p>', $pee );
+        $pee = preg_replace('| {4}|', str_repeat('&amp;nbsp;',4), $pee);
+        $pee = str_replace('\\', '\\\\', $pee);
+        $pee = preg_replace(
+            '|<code>.*?</code>|se'
+            , "str_replace(array('<p>', '</p>'), array('', '<br /><br />'), '$0')"
+            , $pee);
+        $pee = StripSlashes($pee);

        return $pee;
 }

MySQLのHEAPテーブル


5.0からHEAPテーブルでも可変長レコードがサポートされたのだが(BLOBとTEXTは未サポート)、VARCHARでフィールド作っても固定でメモリを食われている気がする…まだちゃんと調べてないけど。

とりあえず、MySQLのデフォルト文字コードをUTF-8にしている場合、HEAPテーブルを使う際には注意が必要だと言うことはわかった。注意が必要って言うか、要はテーブルとフィールドのデフォルト文字コードに気をつけようって言うだけ。日本語を扱わないのであれば、UTF-8のまま使っているとメモリをかなり喰う。30バイト以内のランダムな文字列 + URL、っていうテーブルをHEAPで使っていたのだが、最初はテーブルの定義をVARCHAR(100) + VARCHAR(2048)とかにしていたら、show table statusするとAvg_row_lengthが5kとかになっていた。それをVARCHAR(30) + VARCHAR(512)に変えると2kくらいに減少。ALTER TABLEでテーブルとフィールドの文字コードをUTF-8からASCIIに変えてあげたら550バイトくらいまで小さくなった。やっぱ日本語が絶対に入らないとわかっているのなら、UTF-8を使うのはメモリ(ディスク)の無駄だ。拡張性はあるんだけど、URLとかidとかはASCIIで済むのだから。

Development Environment Conferenceに行ってきた


Development Environment Conferenceに会社の先輩と二人で行ってきた。12:00に受付開始して12:03で定員に達したとか…俺の受付は12:02だったのでギリだったのかも知れないな。

っていうか受付とか特に何もなくて、紙を2枚もらうだけ。早めに行ったりしてたら、申し込みしてなくても潜り込めたんじゃないのかね…。とは言っても席に余裕がなかったのでアレだが。申し込み時のメールアドレスくらいは聞かれるかと思っていたのだが。

スピーカー陣が超豪華だったのだが、一番のお目当てはma.la氏萌ディタ話。開発環境の話なのでしないはずがないと踏んでいたのだが、案の定してくれました。変態過ぎる環境で仕事をしておられる様子で、大変素敵です。他の方もスピーチ内容は大体予想通りで、emacsとかvimとかscreenとかsvkとか。

以下メモ。

はてなと私の開発環境 : 伊藤直也氏

  • zsh+screen+emacs
    • bashは小学生まで
    • /u/l/b/p が /usr/local/bin/perl に展開されたりするよ
  • subversion
    • cvsは小学生まで
  • server.pl
  • perlsh

zshそんなに便利なのか。俺は小学生なのでbashだが。emacsは使いたくないがzshは試そうかな。emacsってデフォルトのキーバインドが変態過ぎて覚える気にならないのよね…しかも大体vimでも同じコトできたりするし。screenは基本すぎるので割愛。

Agile Web Development with 萌ディタ Reloaded : ma.la氏

  • ハード
    • 20inchワイド縦置きデュアル
    • HHKLite2
    • トラックボール
    • ショートカットキー専用キーボード
  • ソフト
    • 萌ディタ / bluewind / AutoHotKey / 窓使いの憂鬱
  • RubyはWEBrick動かすためにある
  • それWEBrickでできるよ、WEBrickでどうやるかは訊いてねぇよ、というやりとりがしたい
  • それAutoHotKeyで(以下略
  • window.statusデバッグ / document.titleデバッグ
  • DebugScreen自分で作ったけど使ってない
  • IEの気持ちがわかればalertで十分
  • livedoorは技術者を募集しています

萌ディタは自分も開発追っかけて使ってたなぁ。久しぶりにサイト見に行ったら経過報告みたいな文章がアップされててビックリした。いつアップしたのか知らないけど…作者さん死んではいなかったのねw

Windows Environment & Vim : secondlife氏

  • Windowsスキー && ゲイツスキー
  • fub
    • migemoが標準で
  • 窓使いの憂鬱 / bluewind / Avesta / Eijiro / シフトムーブ / kbdacc / migemize explorer / Alt-Tab Replacement / htmlhelp
  • Vimは至高のエディタである
    • 移動は/?*#
    • 最短手順よりも思考速度と手順のバランスで自分にマッチする手順で
      • 5wとかしないでwwwwwってやる
    • httpとかscpとかスキームも開ける
    • C-a, C-xで数値のインクリメント・デクリメント
    • earlier, laterで時間軸でのundo, redo
  • dotfileはsubversionで管理してsymlink

scp開けるとか、C-aとかC-xとか知らなかった。text-objectsはなんかさっぱりわからず。難しいよ…

Binary Hacks in Action : 高林哲氏

  • straceとかgdbの基本的な話
  • ソースがなくてもバイナリを直接編集
    • 文字列を短くするのは簡単だけど、長くするのは難しい
      • ライブドアをはてなにするのは簡単だけど、はてなをライブドアにするのは難しいよ
  • Binary Hacksが10月にオライリーから出版されるよ

Cやってる(やってた)人には割と当たり前な話だった。ていうか開発環境の話じゃないしw あ、Binary Hacksは出たら買います…

Vox/Plaggerの裏側見せます : 宮川達彦氏

  • Consolasフォントいいよ
  • dotfileはsubversionとsymlink
  • Vox
    • Zen+CentOSで1人ずつバーチャルな環境を持ってる
    • アプリのインフラはrpmで
  • Plagger
    • IRCとTrac
    • Test::Base
    • DRY
    • svk
  • CPAN mini
    • 最新版だけローカルにミラーしておく
  • 飛行機の中暇だからwikipediaもローカルに置きたい
  • Aliasでtypoをフォロー

Consolasは是非試す。よさげ。svkもsmerge?試そう。svnのマージはうざい。

オレポータビリティ : 青木峰朗氏

  • ヒューマンインターフェースには金をかけたいので良い椅子(でもマウスは\1500)
  • フルキーボードのテンキーは、使わないのでノコギリで切った
  • 様々なプラットフォーム、様々なOSで計20台が置いてある
  • 極力ソフトはインストールしない、カスタマイズしない
    • Ruby, Cコンパイラ, zsh, 素のvi, CVS
  • ホームは /usr に合わせて全マシン共通にする
    • ツールの作成が楽になる

一杯マシンあったけどSGIはなかったな…

終わったときにはもう22時近かったのでそのまま退散。懇親会とか行きたかったけど(あったのかどうかすら知りませんが)、帰れなくなるので…。しかも晩飯食う時間がなかったので死にそうだった。とはいいつつ、せっかくだから名刺交換くらいしておけば良かったな。

otsuneさんのはてなダイアリーを購読してるとPlaggerがこける問題は修正されたっぽ


otsuneさんのはてなダイアリーを購読してるとPlaggerがこける件、http://plagger.org/trac/changeset/1672修正されていました。単にnullチェック漏れだったっぽい。

なんかPlagger動いてないと思ったら、otsuneさんのはてなダイアリーを購読してるとPlaggerがこけるっぽい


なんかgmailが寂しいと思ったので、LogLevelをdebugにして動かしてみたら以下のようになった。


Plagger::Plugin::Subscription::LivedoorReader [info] You have 614 unread item(s) on livedoor Reader.
Plagger [info] plugin Plagger::Plugin::Aggregator::Simple loaded.
Plagger::Plugin::Subscription::LivedoorReader [debug] Logging in to Livedoor Reader
Plagger::Plugin::Subscription::LivedoorReader [debug] get unread items of 1967834
Plagger::Plugin::Subscription::LivedoorReader [debug] get unread items of 2023794
The 'epoch' parameter (undef) to DateTime::from_epoch was an 'undef', which is not one of the allowed types: scalar
at /home/nobu666/perl/lib/site_perl/5.8.8/i386-freebsd/DateTime.pm line 430
DateTime::from_epoch('undef', 'epoch', 'undef', 'time_zone', 'local') called at /home/nobu666/perl/lib/site_perl/5.8.8/Plagger/Date.pm line 63
Plagger::Date::from_epoch('Plagger::Date', 'undef') called at /home/nobu666/perl/lib/site_perl/5.8.8/Plagger/Plugin/Subscription/LivedoorReader.pm line 97
Plagger::Plugin::Subscription::LivedoorReader::sync('Plagger::Plugin::Subscription::LivedoorReader=HASH(0x8a3d72c)', 'Plagger=HASH(0x87b4d44)', 'HASH(0x901eae0)') called at /home/nobu666/perl/lib/site_perl/5.8.8/Plagger/Plugin/Subscription/LivedoorReader.pm line 58
Plagger::Plugin::Subscription::LivedoorReader::__ANON__('Plagger=HASH(0x87b4d44)', 'HASH(0x901eae0)') called at /home/nobu666/perl/lib/site_perl/5.8.8/Plagger.pm line 328
Plagger::run('Plagger=HASH(0x87b4d44)') called at /home/nobu666/perl/lib/site_perl/5.8.8/Plagger.pm line 69
Plagger::bootstrap('Plagger', 'config', '/home/nobu666/plagger/ldr2gmail.yaml') called at /home/nobu666/perl/bin/plagger line 24

日付絡みで落ちているっぽい?2023794はsubscription_idっぽいので、LDRのAPIを http://reader.livedoor.com/api/all?subscribe_id=?2023794 こんな感じで叩いてみた。返ってきたJSON抜粋。


{"ads":[{"url":"http://www.gyao.jp/rss/","title":"完全無料パソコンテレビ「GyaO」","description":"番組情報をジャンル別にRSS配信。新着情報をチェックすれば見逃しも防げる!"},{"url":"http://blog.livedoor.com/cgm.html","title":"ライブドアのブログ・クチコミ","description":"ウィキ、ソーシャルブックマーク、SNS、写真共有、キーワードすべてあります!"}],"subscribe_id":"?2023794","channel":{"error_count":"0","link":"http://d.hatena.ne.jp/otsune/","description":"import otsune from Hatena","image":null,"title":"import otsune from Hatena","feedlink":"http://d.hatena.ne.jp/otsune/rss","subscribers_count":"178","expires":1156265399},"items":[{"enclosure":null,"link":"http://d.hatena.ne.jp/otsune/99990101/p1","enclosure_type":null,"author":"otsune","body":"","created_on":null,"modified_on":null,"id":"554908","title":"いそいで口で吸え","category":""},{"enclosure":null,"link":"http://d.hatena.ne.jp/otsune/20060821/AsyncMedia","enclosure_type":null,"author":"otsune","body":"\n\t\t<div>\n\t\t\t<p><a href=\"http://blogpal.seesaa.net/article/22560511.html\">FIFTH EDITION: メディア・ゲーム・ネットで進む非同期化</a></p>\n\t\t\t<blockquote>\n\t\t\t<p>すいません。ちょっと分かりにくいところが有るので質問します。</p>......

長いので途中で省略。「いそいで口で吸え」というエントリでcreated_onとmodified_onがnullなせいでしょうか。投稿日付が9999-01-01になってるのがそもそもの原因なの?よくわからない。Perlわかんねー…勉強せねば。

つーか「いそいで口で吸え」というと、「かんたまがきゆいのです」とか「やんこまりたい」とか「めんたまにきんそれーたむを塗ると、きつにじもちがいい」とか「ポール・マッカートニー取調室の菊池です」とか思い出しました。どうでもいいですね。