PHPでTwitterのハッシュタグとスクリーンネームをリンクに置き換える正規表現

Twitter系のサービスなどを作られることがなければ、それほど使われる機会はないかと思いますが、文中に存在するハッシュタグやアカウント名(スクリーンネーム)を抽出してはリンクに置き換える正規表現のサンプルです。条件によっては置き換わって欲しくないケースもあるので、戻り読み言明の否定形を活用したものを紹介します。

ハッシュタグをリンクに置き換える正規表現例

Twitterの正式なハッシュタグの仕様は新しいUIに変わってからは若干の変更点もあり、また公開されているわけでもないので詳しことはわかりませんが、調べてみるかぎりでわかっているハッシュタグになる条件としては、

アルファベットと数字以外のあとに続くハッシュ(#)ではじまる、アルファベット(A-Za-z)と数字(0-9)とアンダーバー(_)で構成される文字列であり、その文字列には必ずアルファベット(A-Za-z)かアンダーバー(_)が含まれていること(数字のみで構成されるハッシュはじまりの文字列ではダメ)。

ということで、ハッシュタグは #(\w*[a-zA-Z_]) という感じの正規表現になるでしょうか。これにアルファベットと数字以外のあとに続くという条件を加えなければならないのですが、実際のところ、アルファベットと数字以外にもマッチしない条件を加えたいこともあるので、今回はTwitterの正式な置換とはあえて違うようにしてあるところもありますが、次のような正規表現にしてみました。理由は追々述べていきます。

$test = preg_replace("/(?<![0-9a-zA-Z'\"#@=:;])#(\w*[a-zA-Z_])/u",
 "<a href=\"http://search.twitter.com/search?q=\\1\">#\\1</a>", $test);

ポイントは(?<![0-9a-zA-Z'\"#@=:;])の部分である戻り読み言明の否定形を使っているところです。

  • まず、言明について。言明 (assertion) は、マッチング位置の直前・直後の文字に対してテストが行えます。複雑な言明はサブパターンとして記述し、サブパターンには対象文字列の先を読むもの(先読み言明)と、後ろを読むもの(戻り読み言明)の2種類があります。
  • (?< の部分が戻り読み言明で、(?<= だと肯定、(?<! だと否定を意味します。ここでは (?<! を使っているので戻り読み言明の否定を意味します。つまりここでは (?<! 以降で始まる文字クラス以外にマッチすることを意味します。
  • ここで指定している文字クラスは、0-9A-Za-zの英数字とシングルクォーテーション(')、ダブルクォーテション(")、ハッシュ(#)、アット(@)、イコール(=)、コロン(:)、セミコロン(;)が任意の文字クラスです。これらの文字列がハッシュタグとなるハッシュ(#)の前にあっても、リンクには置換されないことを意味します。

たとえば、

#test ←これはリンクになる
'#test' ←シングルクォーテションが前にあるのでリンクにならない
"#test" ←ダブルクォーテションが前にあるのでリンクにならない
:#test ←コロン(:)が前にあるのでリンクにならない
##test ←ハッシュ(#)が前にあるのでリンクにならない
$#test ←戻り読み言明の文字クラス中には存在しないので、これはリンクになる

とまあ、アルファベットと数字以外にも16進数カラーコードであったりと、ハッシュタグとして認識するには相応しくない文字列もあるため、否定したい条件を戻り読み言明の否定で上手く組み合わせてみました。

アカウント名(スクリーンネーム)をリンクに置き換える正規表現例

ハッシュタグと同様で、アカウント名(スクリーンネーム)も正式な情報はわかりませんが、調べてみるかぎりでわかっているアカウント名(スクリーンネーム)がリンクになる条件としては、

アルファベットと数字以外のあとに続くアット(@)ではじまる、アルファベット(A-Za-z)と数字(0-9)とアンダーバー(_)で構成される1文字から15文字の文字列。

ということで、アカウント名(スクリーンネーム)の正規表現は @([0-9a-zA-Z_]{1,15}) という感じの正規表現になるでしょうか。これにアルファベットと数字以外のあとに続くという条件を加えれば完成ですが、ここではハッシュタグのときと同じ条件を加えることにします。

$test = preg_replace("/(?<![0-9a-zA-Z'\"#@=:;])@([0-9a-zA-Z_]{1,15})/u",
 "@<a href=\"http://twitter.com/\\1\">\\1</a>", $test);

@w3gjp ←これはリンクになる
1@w3gjp ←数字が前にあるのでリンクにならない
@@w3gjp ←アット(@)が前にあるのでリンクにならない
_@w3gjp ←戻り読み言明の文字クラス中には存在しないので、これはリンクになる

以上、Twitterのハッシュタグやアカウント名(スクリーンネーム)をリンクに置き換える正規表現例でした。戻り読み言明の否定に加えている文字列などはサイトの趣向などにあわせて変更されると、より精度の高い正規表現になるかと思います。

Updated / Published