SSH Port FowardでMySQLを使う


気づいたら年が明けていますが俺は元気です。

タイトル通りなんだけど、あるMySQLサーバへつなぐためには踏み台を通らないといけなくて、いちいち

localhost$ ssh -t gateway ssh dbserver
dbserver$ mysql -uhoge -p

とかやらないといけなくて、めんどい。というかこれだとローカルの開発環境で、一時的にdbserverで動作してるMySQLのデータを使いたいときとか困る。ダンプしてローカルに持ってくればいいけど、データがものすごいデカかったりしてだるい。どうにかしたい。

というわけで、Port Forwardすればいいじゃないかと思って

$ ssh -Nf -L 13306:dbserver:3306 gateway

とかして、

$ mysql -uhoge -p -P13306

これで勝つる!!!と思ったら、これでは繋がらない。でもtelnet localhost 13306とか叩いてみると繋がるように見える。

$ telnet localhost 13306
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
L
5.5.8-log
         $;2)s.%n!
                  ?QV;dgXbeUZBymysql_native_password

なにこれどういうことなの、と思って色々試したり調べたりしていたら、答えは意外なところに。

What is SSH Port Forwarding and How It Worksってページに書いてあった。

When the remote SSH host is on a different machine than the MySQL server, replace 127.0.0.1 with the IP address of the MySQL server (relative to the SSH host). Never use “localhost” for hostname.

Never use “localhost” for hostname、だと……??

$ mysql -uhoge -p -P13306 -h127.0.0.1
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 1058649
Server version: 5.5.8-log MySQL Community Server (GPL)

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

をををを……そしてSSH ポートフォワードで MySQL サーバにログインするときのメモ – 酔いどれコード に同じことが書いてあった。damn。localhost指定(-hオプションなしのデフォルト)だと、Unixドメインソケットを使って通信するので、当然ネットワーク越しには繋がらないよというオチでした。2時間くらいハマってしまったのでメモ。

ターミナル勉強会で話してきました


http://kokucheese.com/event/index/16461/

一時はどうなることかと思いましたが、@Mr_K_O の活躍により無事LT枠も埋まり、盛況のうちに終わりました。関係者の方々、来てくださった方々ありがとうございました。

資料はこちらにおいてあります。
http://www.slideshare.net/NobutoshiOgata/zsh-9223115

思いの外30分はあっという間に過ぎてしまうもので、幾つか言い足りなかったこともあったのでここに補足させて頂きます。

  • typoの話: ありがちなtypoをaliasでカバー
  • ヒストリ共有の話: プロセス間でヒストリを共有するオプションがあります、便利><
  • hint: あえて無意味なコマンドを入れることで、ヒストリを検索するときに楽をする技: C-rで検索するときに一発で見つかるように、echo “hint”>/dev/null;hogehoge みたいにしとく…ってこれzshとか関係ないですが
  • wgetでパラメタ指定すると悲しい話: ?がglobで展開されちゃうので面倒なんですが、autoload -Uz url-quote-magic;zle -N self-insert url-quote-magicしとくと幸せだよ

まぁ言い出すときりがないのでこのへんにしときます。次回は是非、dotfile晒し大会的なことをやりたいですね。

さっそくまとめてくださった方が。ありがとうございます! http://d.hatena.ne.jp/cointoss1973/20110912/1315840029

グリーに入社しました


2011年8月1日付でグリー株式会社へ入社しました。

前職でお世話になった方々に、この場を借りて御礼申し上げます。
取り急ぎご報告まで。

にゃんこ二匹目


そういえば書いてなかったけど二匹目が来ています。

名前はスプーン

5月生まれのノルウェージャンフォレストキャット♀、スプーンという名前になりました(前の記事に書いたときは一匹目の名前も決まってなかったけど、シロップという名前になりました)。コーヒー淹れてる時に思いついたらしい。

くりくりおめめ

まだスプーンは2回目のワクチンが終わってないので、それまではシロップとは分けて生活させて、少しずつ少しずつ慣らしてるところ。最初はシロップがフーフーと相当興奮して威嚇モードになっていたけど、最近はケージ越しにじゃれたり、少しだけ一緒に遊ばせたりしてみてる。どうなるかと心配したけど、猫は猫でうまくやってくれそうな感じ。

この子はとても大人しく、爪切りもブラッシングも普通にやらせてくれて楽ちん。でもトイレが下手で、うんちに砂がうまくかからず、踏んづけて手足が汚れることが多いので困る。

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。

にゃんこが我が家にやってきた!


嫁さんとペットショップ行ったら一目惚れして、気がついたらカードを切っていた。が、後悔も反省もしていないっ!!

先週購入して慌てて部屋の掃除やら片付けやらをはじめて、昨日6/11の夜に迎えにいってきた。2-3日は環境が変わって慣れてないので、あんまり遊ばせたりしないで寝かせてあげてくださいと言われたので、まだあんまり構わないようにしている。

名前はまだない。まぁ近いうちに決める。種類はメインクーンで、雄、3/30生まれ、毛色はブラウンタビー&ホワイト。かなりでかくなる猫だということだが、さてさてどの程度の大きさになるのやら。最初の1年でぐっと大きくなって、そこからさらに1-2年かけて少しずつ大きくなるらしい。そういえば体重計ってないや。今度計る。

まだちょっとした物音に過剰に反応して怖がったり、部屋に出すと走り回って壁に激突したり、えさの食べ方が下手で口の周りが汚れがちだったりしております。


さくらのVPSでCentOSからUbuntuに鞍替えまとめ


@macchaからリクエストがあったので、適当にまとめました。タイトルの通りです。

OS再インストールの準備

リモートコンソールの起動にJDK必須なので、作業するマシンにJDKが入ってなければいれとく。あとはTightVNCとか、必要ならどうぞ。あとバックアップ忘れずに。とりあえずwordpressだけ動いてる状態だったので

$ mysqldump -uroot -p wp -Q -c --opt > dumpfile
$ tar zcvf wordpress.tar.gz /var/www/wordpress
$ tar zcvf ssh.tar.gz ~/.ssh

みたいな感じでバックアップ取って作業マシンにscpなりlftpなりで持ってきておく。必要なら/etc/my.cnfとか/etc/php.iniとか、ホームのドットファイル類も。

OS再インストール

VPSコントロールパネルにログインして、OS再インストール→カスタムOSインストール→ubuntu 10.04 amd64を選択→カスタムOSインストールガイド : Ubuntu 10.04の通り進める。10分くらいありゃ終わる。

SSHの設定

何はともあれrootのパスワードとSSHの設定をリモートコンソールからやる。scpなりで、バックアップファイルを転送しとくこと。

$ sudo passwd
$ tar zxf wordpress.tar.gz
$ tar zxf ssh.tar.gz
$ chmod 700 .ssh
$ chmod 600 .ssh/*
$ sudo vi /etc/ssh/sshd_config
- Port 22
+ Port 適当なポート

- PermitRootLogin yes
+ PermitRootLogin no

- #PasswordAuthentication yes
+ PasswordAuthentication no

ポートは変えなくてもいいけどお好みで。ポート変えたら/etc/servicesのSSHのとこを合わせて変更するのも忘れずに。

$ sudo sshd -t

でチェックして、エラーが出なければOKなので再起動

$ sudo service ssh restart

ここで、もう一個ターミナル開いて問題なく接続できることを必ず確認。以降は普通に端末からSSHでつないで作業

諸々設定

もうあとはお好みでどうぞなんだけど、最低やることだけ書いとく

$ sudo apt-get update
.
.
.
$ sudo apt-get upgrade
.
.
.
$ sudo ufw status
Status: inactive
$ sudo ufw default deny
Default incoming policy changed to 'deny'
(be sure to update your rules accordingly)
$ sudo ufw allow SSHのポート番号
Rules updated
$ sudo ufw allow 80
Rules updated
$ sudo ufw enable
Command may disrupt existing ssh connections. Proceed with operation (y|n)? y
Firewall is active and enabled on system startup
$ sudo ufw status
Status: active
To                         Action      From
--                           ------      ----
SSHのポート番号   ALLOW       Anywhere
80                          ALLOW       Anywhere
$ sudo apt-get install denyhosts sysv-rc-conf build-essential zsh screen vim tmux curl wget git-core apache2 php5 libapache2-mod-php5 mysql-server php5-mysql #まぁこの辺は余計なのも入ってるのでお好きなように
$ sudo a2enmod rewrite # rewrite有効に
$ sudo vi /etc/apache2/sites-available/default #VirtualHostなりなんなり、お好きにどうぞ
$ sudo service apache2 restart
$ sudo sysv-rc-conf #自動起動したいやつを適当に選択
$ cp -a wordpress/* /var/www/wordpress #wordpressのバックアップ復元
$ mysql -uroot -p -e"create database wp" #とりあえずDBつくる
$ sudo cp my.cnf.backup /etc/my.cnf
$ sudo service mysql restart
$ mysql -uroot -p wp < dumpfile #dumpファイルからデータ復元

ここまでやればwordpressはもう普通に動いてるはず。あとはchshするなり、ドットファイル復元するなり、好きにすればいいかと。

自分の音楽にまつわる歴史をまとめてみた


気が向いたので、なんとなくまとめてみた。誰も得をしないまとめ。

10歳: なんか知らんけどBOØWYに影響を受けてベースを弾き始める。なんでベースだったのかは未だによくわからない。なんかベースの音がよく聞こえて。単にギターは調子こいてる風で嫌だったっていう天邪鬼説が有力。HOLLYとかいう謎のやっすいプレベタイプのベースと、15Wくらいのアンプと、シールドと音叉と変な教則本とテープがセットになってて、3万だか4万だかのものだったと記憶している。買った初日に、音叉の使い方がわからなくて、チーンて叩いた音にあわせたらいきなり弦が切れて泣きそうになった記憶がある。また楽器屋に戻って、音叉は叩いた後どこかにくっつけるものだと初めて知るw しばらくついてきた教則本に沿って練習するも、ハンマリングとプリングの複合練習みたいなやつで行き詰って2ヶ月ぐらい放置。気が向いてその後やってみたらなぜか弾けるようになっていた。

11歳: 姉貴の影響でLed Zeppelinを聴いてぶっ飛ぶ。天国への階段が入ってるやつを輸入版で買った。あまりにも衝撃を受けて、それを機に洋楽の道へ。クラスメイトが光GENJIやら少年隊やらいってる時期だった気がするけど、VAN HALEN・WHITE SNAKE・Deep Purpleあたりを聴いていた気がする。

12歳: 近所のレンタル屋でいろいろ借りまくる。洋楽はちょっと身を潜めて、邦楽へ。筋肉少女隊・X・DOOM・TM NETWORK・ブルーハーツとか聴いてた。

13歳: 中学に入ってUNICORNが好きになり、ぼちぼちコピーをはじめる。この頃、さすがにHOLLYは我慢できなくなり、グレコのベースを75000円(なぜか覚えてる)で購入。もう型番わかんないけど、アクティブPJタイプで24フレットのやつ。なにげにスルーネックだった気がする。この頃、通販でナルチョのベース講座みたいなやつを買って練習してた。テープが20本くらいついてて、分厚い教本がついてるやつ。フレーズ紹介みたいなのが入ってて、色々聴いてみてた。T-SQUARE・CASIOPEA・Jaco Pastorius・Tower of Power・Larry Graham・Level42・金子マリ&バックスバニー・カルメンマキ&OZとか聴いてた。プリプリも流行ってて、よくコピーしてた記憶がある。あとは友達の兄貴とかから影響されて、WINGERとかVAN HALENとか。あと深夜番組「イカ天」は必ず見てた。マルコシアスバンプとかRABBITとか好きだったな。そういや元RABBITの人捕まったな…w

15歳: 卒業ライブでUNICORN・ブルーハーツ・JUN SKY WALKERSのコピバンをやる。LAメタルにハマって、GUNS・Mötley Crüe・Doken・Poison・Lion・RATTあたり良く聴いていた気がする。Iron Maiden・Rainbowもこの頃かな。RATと、なんか3000円くらいのやっすいコーラスを持ってた気がする。中学の文化祭で、俺がアコギ、友人がボーカルとブルースハープでUNICORNのミルクをやって、のど自慢大会的なやつで優勝。賞品はしょうもない紅白まんじゅう…

16歳: Killerのクロスを買ってXのコピバンをはじめる。この頃からぼちぼちオリジナルやり始めていたような。Metallica・Megadeth・Yngwie・Steve Vai・EXTREME・Motorhead・Judas Priestとか聴きつつ、値段が安かったレコードを買いあさってQUEENのブートレッグとか、T-REX・KISS・King Crimson・RAINBOW・Yardbirds・Jeff Beck・BBA・Helloween・Ozzyあたりを聴いてた。曲作り用に、2万くらいの正体不明のギターを買った。未だに実家にある。ペラッペラのくそみたいな音が出るw YAMAHAの十代しか出られないバンドコンテストにでて、地区大会的なやつでベストベーシスト賞を受賞。

17歳: エフェクターに目覚めていろいろ買い始める。パチンコで15万くらい勝って、ジムダンのワウ(ギター用)・サンズアンプ(初期型のスイッチが一杯ついてるやつ)・オクターバーとか購入。無駄にワイヤレスとか買ったのもこの頃だったような。多分このときにクロスのピックアップをセイモアダンカンに取り替えたり、トーン取っ払ってフルテン仕様に変えたり、2弦と3弦の間にテンションピン付けたりしてた。あと腐ってたグレコのベースのフレットを自力で引っこ抜いて、隙間をアロンアルファで埋めてヤスリ掛けしてフレットレスに改造したりしたのもこの頃。Saber Tiger・Royal Humt・Stratovarius・TOTO・U2・Vandenberg・

18歳: YAMAHAのTEENS’ MUSIC FESTIVALってイベントにXのコピバンとしてでて、地元の大会でベストベーシスト賞をもらった。メジャーデビューしたてのValentine D.C.のベーシストが審査員に来ててえらい褒められたらしいが、俺は授賞式すっぽかして早々に帰ったので知らないw 高校の学祭用バンドでhideやったりしてた。Pantera聴いてコンクリートでぶん殴られたような衝撃を受けたのもこの頃だったような気がする。

19歳: 大学入ってKillerのCRIMINAL TWIN-JBを購入。元はフェニックスのインレイが入っていたんだけど、TAIJI過ぎるので買ったときに真っ黒にリフィニッシュ。このベースの音を聴いて、「Killerを誤解していた」という人が沢山いた。Anthem・44マグナム・Loudness・ジュディマリ・ラルク・LUNA SEA・Slayer・Impellitteri・Gustank・Gamma Ray・Flat Backer・EZO・Manowar・Metal Church・MSG・Mr.Big・Police・Pink Floyd・Rage Against the Machineあたりを聴いてたかな。

20歳: 一番ベーシストとしてぶっ壊れてた時期。ワーミー・メタルゾーン・ワウというもろTMスティーブンス仕様にして、いかにギターより目立つベースを弾くかに主眼が置かれていた時期w

22歳: ちょっと落ち着いて、まじめにスケールやコードの勉強をしていた時期。聴いていた音楽もメタルだけじゃなく、J-POPはもちろん、モータウン・ジャズ・クラシック一通り・ボサノバ・フラメンコと色々聴いてた。が、すったもんだあってバンド活動をしばらく封印。

26歳: 人より3年ほど遅れて就職し、空白の4年のうちにCRIMINAL以外ほぼ全部うっぱらってしまったので、とりあえずヤフオクでKB-CROSSを買い戻し。ギターもKillerのやっすいやつ(パイレーツだっけ)をヤフオクで購入。

27歳: まじめにギターを練習したくなって、IbanezのRZプレステージ(型番忘れた)とV-AMP2を購入。

29歳: 5弦が欲しくなってG&L L-2500をお茶の水で中古で購入。

30歳くらい: ヒューケトのTUBE FACTOR、HaTeNa Active Spice、HAO Rust Rideとか買ったのはこの頃のような。

32歳?: POD XT Live購入。未だにいまいち使い方がよくわからないwww

34歳: Warwick ThumbBass購入。WarwickのStreamer Jazzman、アトリエZ青木さんモデル、スペクターと悩んで決めた。決め手は音作りの幅広さと、音のふくよかさ。足下はEBSのマルチコンプ、サンズアンプ・MXRの10BAND EQで安定。

最近はあんま新しい音楽聴いてない。Brian Brombergのアルバム買ったくらいか。未だにIn FlamesやらチルボドやらARCH ENEMYやらAmorphisやらHammerFallやらRhapsodyやら聴いてます。DragonForceも一時期好きだったけど最近飽きてきた。

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でやったほうが速い。もっともっと大規模なデータを解析するのに使わないと。

JavaScriptでいい感じにURLとかScreenNameとかをリンクにする正規表現


URLに@が入ってたりすると、ScreenNameのリンクとごっちゃになって割と対応に手間取ったのでメモとして。日本語のハッシュタグなんか最近見かけないからどうでもいいような気もするけど、まぁ一応対応。

var text="@xyz aaa http://aaa.com:8080/@abc#asdf http://日本語.jp/?abc&amp;asdf http://example.jp/a.b.c@d.e bbb @ccc/@ddd/@eee #hash #日本語だめ #日本語おk_ http://a.com/日本語";
text.replace(/(https?:\/\/(?:[^!"#$%&amp;'\(\)*\+,-\./:;&lt;=&gt;\?@\[\\\]^_`\{\|\}~\s][\.-](?=[^!"#$%&amp;'\(\)*\+,-\./:;&lt;=&gt;\?@\[\\\]^_`\{\|\}~\s])|[^!"#$%&amp;'\(\)*\+,-\./:;&lt;=&gt;\?@\[\\\]^_`\{\|\}~\s]){1,}\.[a-z]{2,}(?::[0-9]+)?(?:\/(?:[\x21-\x7e]+)?)?)|(?:[@@])([a-z0-9_]{1,20})|(?:#|\uFF03)([^\s\/ \x21-\x2f\x3a-\x40\x5b-\x5e\x60\x7b-\x7e]+[a-z0-9_])/mgi, function(){
 if(arguments[1]!==""){
  return "&lt;a class='url' href='"+arguments[1]+"'&gt;"+arguments[1]+"&lt;/a&gt;";
 }else if(arguments[2]=="" &amp;&amp; arguments[3]==""){
  return arguments[1];
 }
 if(arguments[2]!==""){
  return "&lt;a class='screen_name' href='http://twitter.com/"+arguments[2]+"'&gt;@"+arguments[2]+"&lt;/a&gt;";
 }else if (arguments[3]==""){
  return arguments[2];
 }
 if (arguments[3]!==""){
  return "&lt;a class='hashtag' href='http://search.twitter.com/search?q=%23"+arguments[3]+"'&gt;#"+arguments[3]+"&lt;/a&gt;";
 } else {
  return arguments[3];
 }
}
)

実行すると

"&lt;a class=’screen_name’ href=’http://twitter.com/xyz’&gt;@xyz&lt;/a&gt; aaa &lt;a class=’url’ href=’http://aaa.com:8080/@abc#asdf’&gt;http://aaa.com:8080/@abc#asdf&lt;/a&gt; &lt;a class=’url’ href=’http://日本語.jp/?abc&amp;asdf’&gt;http://日本語.jp/?abc&amp;asdf&lt;/a&gt; &lt;a class=’url’ href=’http://example.jp/a.b.c@d.e’&gt;http://example.jp/a.b.c@d.e&lt;/a&gt; bbb &lt;a class=’screen_name’ href=’http://twitter.com/ccc’&gt;@ccc&lt;/a&gt;/&lt;a class=’screen_name’ href=’http://twitter.com/ddd’&gt;@ddd&lt;/a&gt;/&lt;a class=’screen_name’ href=’http://twitter.com/eee’&gt;@eee&lt;/a&gt; &lt;a class=’hashtag’ href=’http://search.twitter.com/search?q=%23hash’&gt;#hash&lt;/a&gt; #日本語だめ &lt;a class=’hashtag’ href=’http://search.twitter.com/search?q=%23日本語おk_’&gt;#日本語おk_&lt;/a&gt; &lt;a class=’url’ href=’http://a.com/’&gt;http://a.com/&lt;/a&gt;日本語"

という文字列になり、以下のようにほぼ思ったとおりのリンクになる

@xyz aaa http://aaa.com:8080/@abc#asdf http://日本語.jp/?abc&asdf http://example.jp/a.b.c@d.e bbb @ccc/@ddd/@eee #hash #日本語だめ #日本語おk_ http://a.com/日本語

ドメインじゃなくてPATH部分に日本語が含まれる、WikipediaのURLみたいなのには対応してない。やりゃできるけど、TwitterでURLを投げるときに、URLの後ろにスペースいれないで日本語を続けて入れる人も割といそうなので…とりあえずこれでいいかな、とか。

なんかもっと美しく簡単にできそうな気もするんだけど…誰か添削してください。