Twitterの発言中に現れるハッシュタグを抜き出す関数

Twitter側がどのようなカタチをハッシュタグと認識するのかよくわからないけど, とりあえず書いてみた. ちなみにめんどくさいので正規表現とかは使ってません.

使い方は:

$ erl
1> twitter_status:hashtags("ほげほげ #banana_#mango#highschool").
["banana"]
2> twitter_status:hashtags("ふがふが #banana_ #mango #highschool").
["banana", "mango", "highschool"]

です. 最初の戻り値にmangoとhighschoolが含まれないのはそういう仕様です*1.

んで注意するのは, twitter_status:hashtags/1に与える引数をバイナリまたはUnicode表現のリスト*2にしなければいけない点.

当然と言えば当然なのかもしれませんが, 文字単位で処理を行いたかったので扱いはこっちの方が楽です. 正規表現使ってないし.

ただシェルから与えたリストはあらかじめUnicode表現である*3のに対して, ソース中に書かれたリストはUnicode表現になっていないので:

hoge() ->
    twitter_status:hashtags("#hoge").

とやっても["hoge"]は返ってきません. list_to_binary/1を使って:

hoge() ->
    twitter_status:hashtags(list_to_binary("#hoge")).

とすれば, ["hoge"]が返ってきます. バイナリを与えると, twitter_status:hashtags/1がunicode:characters_to_list/1を呼び出すからね!

基本的にバイナリのまま扱っていればいいので, こういうのは稀. とりあえず有事に備えてこの記事を備忘録としよう.

でソースですが, Gistに上がってるのでよければどうぞ.

http://gist.github.com/271074

一応ココにも書いておきます.

-module(twitter_status).
-author("KONDO Takahiro <heartery@gmail.com>").
 
-export([hashtags/1]).
 
-define(ALPHANUMERIC(C), ($a =< C andalso C =< $z orelse
                          $A =< C andalso C =< $Z orelse
                          $0 =< C andalso C =< $9)).
 
-define(HASHTAG_PREFIX(C), (C =:= $# orelse C =:= 65283)).
-define(HASHTAG_CHARS(C), (?ALPHANUMERIC(C) orelse C =:= $_)).
 
hashtags(Text) when is_binary(Text) ->
    hashtags(unicode:characters_to_list(Text));
 
hashtags(Text) ->
    lists:reverse(hashtags(Text, -1, "", [])).
 
hashtags([C|Text], -1, [], Hashtags) when ?HASHTAG_PREFIX(C) ->
    hashtags(Text, 0, [C], Hashtags);
 
hashtags([C|Text], -1, [LC|_] = Read, Hashtags) when ?HASHTAG_PREFIX(C), not ?ALPHANUMERIC(LC) ->
    hashtags(Text, 0, [C|Read], Hashtags);
 
hashtags([C|Text], Size, Read, Hashtags) when ?HASHTAG_CHARS(C), Size > -1 ->
    hashtags(Text, Size + 1, [C|Read], Hashtags);
 
hashtags([C|Text], Size, Read, Hashtags) when Size > 0 ->
    hashtags(Text, -1, [C|Read], [hashtag(Read, Size)|Hashtags]);
 
hashtags([C|Text], Size, Read, Hashtags) ->
    hashtags(Text, Size, [C|Read], Hashtags);
 
hashtags([], Size, Read, Hashtags) when Size > 0 ->
    [hashtag(Read, Size)|Hashtags];
 
hashtags([], _, _, Hashtags) ->
    Hashtags.
 
hashtag(Read, Size) -> hashtag(Read, Size, []).
hashtag(_, 0, Acc) -> Acc;
hashtag([C|Read], Size, Acc) -> hashtag(Read, Size - 1, [C|Acc]).

*1:Twitter側で試してみればわかります. ただ#banana_#mango#highschoolと書いて, フォロワーに変な目で見られても, 責任は負いかねます><

*2:"あ"が[227, 129, 130]ではなく[12354]となる状態

*3:端末のエンコーディングにもよると思いますが. erlとの関係何かあるのかな……? 少なくともボクの環境(Mac OS X 10.5.8, Erlang R13B03)ではそうだった, と書いておきます