« Text::TagsでTagging | メイン | Class::DBI::SweetでのJOIN »

はてなブックマークのコメント欄のようなものをParseするモジュール

はてなブックマークのコメント欄のようなものをParseするモジュールを組んでみた。

本家の完璧なエミュレートではないです。

package Text::HatenaBookmarkLike::Comment::Parser;

use strict;
use warnings;

sub new{
    bless {},shift;
}

sub parse{
    my $self = shift;
    my $string = shift;

    my @tags;
    while(length $string && $string =~ m!^\s*\[([^\]]+)\]!){
        my $tag = $1;
        $string =~ s!^\s*\[([^\]]+)\]!!;
        next unless length $tag;
        push(@tags,$tag);
    }
    
    return Text::HatenaBookmarkLike::Comment::Parser::Result->new({
        tags=>\@tags,
        comment=>$string
    });
}

1;

package Text::HatenaBookmarkLike::Comment::Parser::Result;

use strict;
use warnings;
use base qw(Class::Accessor::Fast);
__PACKAGE__->mk_ro_accessors(qw(tags comment));

sub tags{
    my $self = shift;
    return wantarray ? @{$self->{tags}||[]} : $self->{tags};
}

sub uniq_tags{
    my $self = shift;
    my %seen;
    my @tags = grep {!$seen{$_}++} $self->tags;
    return wantarray ? @tags : \@tags;
}

1;


使い方は、

my $ret = Text::HatenaBookmarkLike::Comment::Parser->parse("[foo][bar][baz][foo]適当です");
print join(",",$ret->tags),"\n";
print join(",",$ret->uniq_tags),"\n";#ユニークなTagだけ
print $ret->comment,"\n";


今分かっている本家と違うところは、

  • Tagの数、コメントの文字数に制限がない
  • ? / % [ ] などの記号が使える

あたりが違う。

Tagの抜き出しの方法として、正規表現の\Gを使うことも考えられるのですが、上の方法の方が速かったです。

↓確認ベンチマークスクリプト

use strict;
use Benchmark;

my $str="[foo][bar][baz][foo]適当です";

Benchmark::timethese(300000, {
        'USE_G' =>\&use_g,
        'USE_S' => \&use_s,
});

sub use_g{
	my $str = shift;
	my @tags;
	while($str =~ /\G\s*\[([^\]]+)\]/gc){
		my $tag = $1;
		push(@tags,$tag) if length $tag;
	}
	$str =~ /\G\s*(.*)/g;
	my $comment = $1;
}

sub use_s{
	my $str = shift;
	my @tags;
	while($str =~ /^\s*\[([^\]]+)\]/){
		my $tag = $1;
		push(@tags,$tag) if length $tag;
		$str =~ s/^\s*\[([^\]]+)\]//i;
	}
	my $comment = $str;
}


実行結果

$ perl tag_seprate.pl 
Benchmark: timing 300000 iterations of USE_G, USE_S...
     USE_G:  2 wallclock secs ( 1.49 usr +  0.01 sys =  1.50 CPU) @ 200000.00/s (n=300000)
     USE_S:  1 wallclock secs ( 0.47 usr +  0.00 sys =  0.47 CPU) @ 638297.87/s (n=300000)