Y's note

Web技術・プロダクトマネジメント・そして経営について

本ブログの更新を停止しており、今後は下記Noteに記載していきます。
https://note.com/yutakikuchi/

Date/Timestamp変換のまとめ

概要

  • Mysqlからアプリケーションで日付情報を引くときDate型<=>Timestamp型の変換を行う事がしばしばある。
  • 今日はPHP/Python/JavascriptのDate/Timestampの型変換についてまとめる。
  • Javascriptの例ではSpiderMonkeyを利用している。

Date型の日付を出力する

php
<?php

//現在時刻をDate型で出力
//date関数を使う場合
echo date("Y-m-d H:i:s") . "\n";
//結果 2011-10-14 00:00:00

//strftime関数を使う場合
echo strftime("%Y-%m-%d %H:%M:%S") . "\n";
//結果 2011-10-14 00:00:00
python
#! /usr/bin/env python
# -*- coding:utf-8 -*-

from datetime import *

#現在時刻を表示
print datetime.now().strftime( '%Y-%m-%d %H:%M:%S' )
#結果  2011-10-14 00:00:00
Javascript
//Dateオブジェクトを利用
var d = new Date();
var year  = d.getFullYear();
var month = d.getMonth() + 1;
var day   = d.getDate();
var hour  = ( d.getHours()   < 10 ) ? '0' + d.getHours()   : d.getHours();
var min   = ( d.getMinutes() < 10 ) ? '0' + d.getMinutes() : d.getMinutes();
var sec   = ( d.getSeconds() < 10 ) ? '0' + d.getSeconds() : d.getSeconds();
print( year + '-' + month + '-' + day + ' ' + hour + ':' + min + ':' + sec );
//結果  2011-10-14 00:00:00

unixtimeを表示する

php
<?php

// timestamp取得
echo time() . "\n";
//結果 1318518000

// micro秒まで出力
echo microtime() . "\n";
//結果 0.89138500 1318518000
python
#! /usr/bin/env python
# -*- coding:utf-8 -*-

from datetime import *
import time

#timestamp表示
print int( time.mktime( datetime.now().timetuple() ) ) 
//結果 1318518000
Javascript
print( parseInt( new Date() /1000 ) );
//結果 1318518000

Date型の日付をUnixTimeに変換する

php
<?php

// Date型をtimestamp型に変換
echo strtotime( "2011-10-14 00:00:00" ) . "\n";
// 結果 1318518000
python
#! /usr/bin/env python
# -*- coding:utf-8 -*-

# from / import
from datetime import *
import time

#Date型をtimestamp型に変換
stime = "2011-10-14 00:00:00"
print int( time.mktime( datetime.strptime( stime, "%Y-%m-%d %H:%M:%S" ).timetuple() ) )
#結果 1318518000
Javascript
var ts = "2011-10-14 00:00:00";
print( Date.parse( ts.replace( /-/g, '/') ) / 1000 );
//結果 1318518000

timestampをdate型に変換

php
<?php

// timestampをDate型に変換
echo date( "Y-m-d H:i:s", "1318518000" ) . "\n";
// 結果 2011-10-14 00:00:00
python
#! /usr/bin/env python
# -*- coding:utf-8 -*-

from datetime import *

#timestamp型をDate型に変換
print datetime.fromtimestamp( 1318518000 )
# 結果 2011-10-14 00:00:00
Javascript
var ts = 1318518000;
var d = new Date( ts * 1000 );
var year  = d.getFullYear();
var month = d.getMonth() + 1;
var day  = d.getDate();
var hour = ( d.getHours()   < 10 ) ? '0' + d.getHours()   : d.getHours();
var min  = ( d.getMinutes() < 10 ) ? '0' + d.getMinutes() : d.getMinutes();
var sec   = ( d.getSeconds() < 10 ) ? '0' + d.getSeconds() : d.getSeconds();
print( year + '-' + month + '-' + day + ' ' + hour + ':' + min + ':' + sec );
//結果 2011-10-14 00:00:00

まとめ

  • phpはdate,time,strtotime関数だけを覚えておけば大体のことは済みそう。また関数が1個で完結するので直感的にも分かりやすい。
  • pythonはdatetimeオブジェクトの理解をしていないと使えない。オブジェクトとメソッドを指定するのが一番美しいと言えるかも。
  • JavascriptのDate型出力がとても煩雑。date.toString()などのメソッドでDate型が出力できれば楽ですが、ブラウザの仕様によって挙動が異なる様子。timestampの出力は楽。

SpiderMonkeyでのコマンドラインJavascript

Javascriptの実行はWebブラウザ上に限定されたものではない。ターミナル上でもスクリプト言語として実行可能だし、構文チェックも可能。今日はあまり知られていない非ブラウザでのJavascriptの話。SpiderMonkeyをターミナル上で動かすことを中心にまとめる。

SpiderMonkeyとは

install

Macにinstallする方法を挙げる。以下2つのどちらかの方法を採用すれば良い。
sourceからのbuildはmacの場合色々と設定が大変だったのでport installした。

1 portからinstallする

これが一番簡単。

$ sudo port install spidermonkey
2 sourceをdownloadしてbuild

※以下は実行していないが念のためダウンロードとインストールについて記述する。
SpiderMonkey Build Documentation ここを参考に設定する。
Mac OS X Build Prerequisites 設定が結構大変。
autoconf213が必要なのでまずはそれを入れておく。

sudo port install autoconf213

ソースの取得方法は次の通り。※murcurialからの取得が時間がかかる。

  • tarballでの取得
$ wget http://ftp.mozilla.org/pub/mozilla.org/js/js185-1.0.0.tar.gz
$ tar xzf js185-1.0.0.tar.gz
  • murcurialからの取得
$ hg clone http://hg.mozilla.org/mozilla-central/

※ murcurialを操作するhgコマンドが無いと取得できないので設定されていなければportからinstallする。

sudo port install mercurial
  • CVSからの取得
$ cvs -d :pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot login
$ cvs -d :pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot co -l mozilla/js/src mozilla/js/src/config mozilla/js/src/editline mozilla/js/src/fdlibm
  • 取得したsourceをbuildする。公式のドキュメントには色々と書いてあるがここでは最も簡単な方法でのbuildを行う。
$ cd js/src
$ autoconf-2.13
$ ./configure
$ make

コマンドラインでのスクリプト実行

SpiderMonkeyの設定が完了したらjsコマンドがインストールされていることを確認する
$ which js
/opt/local/bin/js
コマンドライン上で実行する。

eオプションを利用する。

$ js -e 'print( 'SpiderMonkey' );'
SpiderMonkey
Syntax Check

sオプションを利用する。構文エラー箇所を教えてくれる。

$ js -s ソースファイル名
ソースファイル名: strict warning: trailing comma is not legal in ECMA-262 object initializers:
ソースファイル名: strict warning: };
ソースファイル名: strict warning: ^
ソースコードを実行する。

jsコマンドの後にファイル名を指定するだけ。

$ js spidermonkey.js
SpiderMonkey

実行ソースコード

#!/opt/local/bin/js

var SpiderMonkey = function() {
    this.name = 'SpiderMonkey';
};

SpiderMonkey.prototype = { 
    echo : function() {
        print( this.name );
    }   
};

var spider = new SpiderMonkey();
spider.echo();
コマンドライン引数を渡す

実行ファイルにコマンドライン引数を渡す方法。js ファイル名 引数1 引数2 引数3

$ js arguments.js Javascript Python Java
Javascript
Python
Java

実行ソースコード

#!/opt/local/bin/js

for(var i=0; i<arguments.length; i++ ) {
    print( arguments[i] );
}

上のように実行ファイルに対して入出力が可能だと例えばHadoopのMap/ReduceにもJavascriptで書けてしまう。

vimにSyntaxCheckを仕込む

この章はおまけ。SpiderMonkeyを利用するとvimと連携してこんな事もできますという感じの内容。

JSLint.vimを利用する

JSLintをvimで使うプラグイン。構文のチェックをしてエラー箇所を教えてくれる。gitにソースが上がっているのでそれを落としてきてrake installする。~/.vimrcにfiletype plugin onを追加する。

$ git clone git://github.com/hallettj/jslint.vim.git
$ cd jslint.vim
$ rake install      
(in /Users/YutaKikuchi/work/src/jslint/jslint.vim)
Installed ftplugin/javascript/jslint.vim to /Users/YutaKikuchi/.vim/ftplugin/javascript/
Installed ftplugin/javascript/jslint/jslint-core.js to /Users/YutaKikuchi/.vim/ftplugin/javascript/jslint/
Installed ftplugin/javascript/jslint/runjslint.js to /Users/YutaKikuchi/.vim/ftplugin/javascript/jslint/
Installed ftplugin/javascript/jslint/runjslint.wsf to /Users/YutaKikuchi/.vim/ftplugin/javascript/jslint/
$ vim ~./vimrc
filetype plugin on

使用した感じ

SyntaxCheckのコマンドをmapする

Pluginを入れると重くなると感じる人もいるだろうし、ある程度コードしてから手動で確認したいという人はvimrcの設定だけで済ませるのもいいかも。次のmap設定をvimrcに仕込む。※この方法では完全なる文法チェックは行われない。

$ vim ~/.vimrc
"syntax check
map ,php <ESC>:!php -l %<CR>
map ,js <ESC>:!js -s %<CR>

vimを開いている時のコマンドモードで",js"と入力すると開いているファイルのSyntaxCheckを行う。以下は実行結果。

arguments.js:7: ReferenceError: test is not defined

jQuery Proven Performance Tips And Tricks (翻訳)

http://addyosmani.com/jqprovenperformance/
このサイトに書いてある内容をまとめます。英語は苦手なんで適当な意訳が多いです。

Performancetip1 バージョンのお話

  • 可能な限り常に最新バージョンを利用する。
  • アップデートする前にはかならずregression testを行う事。
  • 最新のバージョンは1.6.2や1.7で、この秋にリリースされる予定。
  • twitterなどもjquery1.3.0を利用している。
  • 1.6はパフォーマンス改善や 新しい特徴が組み込まれている。
  • 1.6.2はバグのパッチやregressionが修正される。
  • 古いバージョンではこれらお手軽なパフォーマンスを受けることができない。
  • 47%の有名なサイトがJqueryを使っているが、バージョン変更にテストが大変。
  • アップデートは通常、大変ではないプロセスである。
  • 1.4.2、1.4.4と1.6.2のSelectorを比較してもパフォーマンスの違いは明らか。
  • .attr( 'value' ), .val()(get)なども1.6でパフォーマンスが改善されている。
  • しかし特定のselectorは1.4.xより遅くなっている。
  • 使っているselectorのパフォーマンスに注意すれば、大丈夫。

Performancetip2 Selector

  • 全てのSelectorは平等に作られない。
  • Selectorの生成方法は沢山あるし、それぞれのSelectorは同一のパフォーマンスを出さない。
  • 最速と最遅いSelectorがどれか知っていますか?
  • IDとElementのSelectorは最速。$( '#Element, form, input' )
    • IDとElementでの指定は裏側でnativeDOMの操作を利用している。(getElementById())
  • Class Selectorは遅い。#( '.element' )
    • getElementsByClassNameはIE5-8ではサポートしていない
    • Firefox3、Safari4、Chrome4、Opera10.10以上はこれは速い。
  • 最も遅いのは正しくない指定とattributeでのselector。$( ':visible, :hidden' ); $( '[attribute=value]' );
    • 上のselectorはnative関数を呼び出す事ができない。
    • 最新のブラウザーにはquerySelector()やquerySelectorAll()などサポートされている。
  • querySelectorAll()はCSSベースでのSelectorとなる要素をDOMとして検索できる。
    • JQueryはquerySelectorAll()の利用を試みている。
    • querySelectorAllを使うSelectorの最適化と:first,:last,:eq等ではないSelectorの最適化との比較。
    • 有効なSelectorこれをより利用する機会がある。
  • 曖昧なselectorは有効ではあるが、使う時には注意が必要。
  • :hiddenを指定した要素が100個あったとき、JQueryは100個のアイテムをコールする。
  • :hiddenは有効であるが全ての曖昧な指定は全ての要素に対して実行を試みる。
  • できれば上の事は避けたい。
  • jQuery1.4.2と1.6のselectorについての比較は http://jsperf.com/dh-jquery-1-4-vs-1-6/6 に記載されている
  • jQuery1.2.xと1.4.xと1.6.xとquerySelectorAllとquerySelector()と他のフレームワークについては http://jsperf.com/jquery-vs-sizzle-vs-midori-vs-mootools-selectors-test/26に記載されている。

Performancetip3 parentとchildrenの理解

  • parentとchildrenの理解が必要。1.context,2.find,3.immediate children, 4.child combinator selector, 5.class selector, 6.created context。
  • 1. context: $( '.child', $parent ).show();
    • 上の文法を$.parent.find( 'child' ).show();と変更してしまうと遅くなってしまう。
    • 5-10%最速のオプションと比較すると遅くなる。
  • 2. .find(): $parent.find( '.child' ).slow();
    • これは最速。
  • 3. immediate children: $parent.children( '.child' ).slow();
    • この文法は内部的に$.siblingとネイティブ関数のnextSibling()を同じ木構造の他の要素を参照してしまう。
    • これは最速のオプションより最大で50%ほど遅くなってしまう。
  • 4. css child combinator selector : $( '#parent > .child' ).slow();
    • child combinator selectorを使う事は、右から左の要素に働きをかけてしまう。
    • childのチェックをする前にparentの直接のchildとしてmatchしてしまうので、良くない。
    • これは最速のオプションより最大で70%ほど遅くなってしまう。
  • 5. css class selector : $( '#parent .child' ).show();
    • class selectorを使う事は4と同じ制約を受ける。
    • これは内部的には.findを使うように変換される。
    • これは最速のオプションより最大で77%ほど遅くなってしまう。
  • 6. created context : $( '.child', $( '#parent' ) ).show();
    • これは内部的に$( '#parent' ).find( '.child' )を実行しているのとほぼ同じ。
    • これは最速のオプションより最大で23%ほど遅くなってしまう。
  • まとめると、最速のオプションは2の$parent.find( '.child' ).show();
    • parent selectorは既にキャッシュされるのでDOMから再取得する必要はない。
    • キャッシュが無いと16%ほど遅くなる。
    • ネイティブのgetElementByIdやgetElemetsByName,getElmentsByTagNameを直接使用する事は渡されたcontextを検索することである。
  • .findはtop-downでの全ての子要素を検索する時にパフォーマンスを発揮する。
  • やりたい事によって他のオプションはより最適だったり、パフォーマンスを発揮するかもしれない。
  • contextとselector,selectorと.find()とparent/child selectorとimmediate chidrenに関する記述は http://jsperf.com/jquery-selectors-context/2 に記載されている。

Performancetip4 絶対に理解すべき項目を抑えて、JQueryを使うべき。

  • 通常のJavascriptを使った方がよりパフォーマンスを出す時があることを覚えておくべき。
  • jQueryJavascriptである。
  • このことに使う人は何度遭遇するか。
  • jQueryの過剰な利用。
    • $( 'a' ).bind( 'click'. function() { console.log( 'You clicked:' + $(this).attr( 'id' ) ); });
    • ID selectorは生成されたjQueryオブジェクトと構文解析された後のselectorのdocument.getElementByIdを取得するだけである。
  • なぜ自身のDOM要素を利用しないのか。これはより速い。
    • $( 'a' ).bind( 'click'. function() { console.log( 'You clicked:' + thid.id ); });
    • overheadを解消するこのjQueryは常にベストな方法ではない。
  • this.idと$(this).attr('id')の両方ともに同じ値を返すが、以下の事を覚えておくべき。
    • 低いレベルではthis.getAttribute('id')は$(this).attr('id')と等価である。
    • しかしながらattributeを最新に保つためにthis.idを使う方がよりよい。
  • $(this).attr('id') とthis.idの比較は http://jsperf.com/el-attr-id-vs-el-id/2に記載されている。
  • $(thid).attr('id')は直接DOMの要素を参照するよりも80-95%ほど遅くなる。

Performancetip5 cachingは大変役立つ

  • cachingは後でselectorの結果を再利用することを意味する。
  • 以下のことを覚えておくべき。
    • それぞれの$( '.elem' )はDOMを検索した結果を再実行し、新しい結果を取得する。
    • cacheされた結果を取得すれば何でも実行できる。
    • cachingは選択の繰り返しを減らす。
  • cacheされた結果を使う事
    • var foo = $( '.item' ).bind( 'click', function({ foo.not(this).addClass( 'bar' ).removeClass( 'foobar' ).fadeOut(500); }) );
  • cached selectorと要素参照の繰り返しについての比較は http://jsperf.com/ns-jq-cached ここに記載されている。
  • cacheされていないselectorはcachedされたものを使うよりどの場合でも62%遅くなってしまう。

Performancetip6 chaining

  • 全てのjQuery methodはjQuery objectを返却しchainingをサポートしている。
  • これはselection上でmethodを続けて実行できることを意味する。
  • より少ないコードでより記述が簡単である。
  • No-chainigとchainigの比較。
  • chainedされた呼び出し方、それぞれの呼び出し方、cachedされたそれぞれの呼び出し方の比較は http://jsperf.com/jquery-chaining ここに記載されている。
  • chainingが最速で、それに次いでcacheされた個別の呼び出し方となる。

Performancetip7 event delegation

  • event delegationはDOM構造に対してeventを作成し、追加することを可能にする。
  • これにおいて重要なことはsingle event handlerを結びつける事である。
  • 実行時にDOMの中でelementsに対して動作する。またこれらは実行後に導入されるとも言える。
  • .bind()
    • .bindは要素に対してclickやmouseenterなどのイベント処理を結びつける。
    • 大量の設定を伴う場合、browserは全てのイベント処理を追跡しなければならないし、それはbindするために時間がかかってしまうことがある。
    • 要素に動的に設定する事は働かない。
  • .live()
    • event delegationのサポートする最もシンプルな形式。
    • これはselectorの今と後にマッチすることをイベント処理に対して加える。
    • 単純なシナリオについては最適に働くが、欠陥もある。( 最上位層の結合でなければならなく、横断すると失敗する)
    • 他のjQueryのmethodと違ってchainすることができない。
  • .delegate()
    • .delegateは今と将来に一致するselectionに対してハンドラを追加するときにbindするような特別なDOM要素に対して指定する事を認める。
    • 確実にelementのターゲットをキャプチャするために全ての方法でDOMに発散できない。(.live()と違って)
    • 同じイベント処理を他の多数の要素に追加する時に使うといい。
  • .live()や.delegate()やdelegate from body variationsについては http://jsperf.com/jquery-delegate-vs-live-table-test/2 に記述されている
  • .bind()や.click()や.live()や.delegate()については http://jsperf.com/bind-vs-click/12 に記述されている。
  • .live()や.live() contextや.delegate()やdelegating to document.bodyについては http://jsperf.com/jquery-live-vs-jquery-delegate/15 に記述されている。

Performancetip8 DOMはDatabaseではない

  • jQueryはDOMをDatabaseのように扱う事ができるが、Databaseではない。
  • それぞれのDOMに対して要素を追加する事はコストが掛かる事を覚えておく。
  • これは.append()や.insertBefore()や.insertAfter()の使用を最小限に保つことを意味する。
  • contentや.text()や.html()に格納されている情報の取得をする横断的なDOMはたいてい最適なアプローチではない。
  • .data()を代わりに使う事はDOM要素に対して様々な要素を安全に追加することを許可する。
  • Tip1 .append()の使い方。
    • メモリの中にHTML文字列を構築する事によって最小化を図り、単体の.append()を代わりに使う。
    • cacheされたselectorを追加する時ではなく、それらより20%遅くなる時に多数のappend()は90%遅くする。
  • Tip2 .detach()の使い方
    • nodeに対して重い追加を行う時に大変よく動作する。
    • 既に一度準備されている時にはDOMへのnode再追加を許可する。
    • undetachedのnodeよりも60%早く動作する。
  • Tip3 .data()のよりよい使い方
    • 通常のdataは以下のように使う。$( '#elem' ).data( key, value );
    • しかい実際は以下の方法がより速い。$.data( '#elem', key, value );
    • これはjQueryオブジェクトの生成にoverheadがある事と、最初にdataのパースを行うからである。
  • $.dataはより速いけれども、selectorに対して適用はできず、nodeに対してだけである。
  • これは$.data( elem, key,value )はelemが既にelementとして定義されている場合にのみ利用可能であることを意味する。
  • .detachとdetachingでは無い比較は http://jsperf.com/to-detach-or-not-to-detach ここに記載されている。
  • jQuery.dataとjQuery.fn.dataの比較は http://jsperf.com/jquery-data-vs-jqueryselection-data/11 ここに記載されている。
  • 多数のappendと単一のappendの違いについては http://jsperf.com/string-concat-single-append-vs-multiple-append ここに記載されている。

Performancetip9 LOOPを理解する

  • $.eachや$.fn.each()を使うよりもnativeのforやwhile loopを使う方がより速いことを知っていたか?
  • jQueryは指定されたcollectionsのiterateを簡単にするが、それらはいつも最適に働くとは限らない。
  • Ben Almanの$.each2()ようなPluginは時々$.fn.eachよりも良く動作する。
  • 出来る限りのLoopは避けるべきだが、難しいのはネストされたDOMのselectorsはよりよく動作する。
    • 必ず必要とは言い切れないが、Loopは避ける。これは全てのプログラミング言語を遅くする。
    • 可能であれば、selector engineを使う代わりに必要なelementsにアクセスする。
    • 当然ながら置換不可能なloopも存在するが、最適化のために最善の努力をすべき。
  • 何がいいたいかと言うと
    • 開発者はよくiterateを必要とする。
    • $.eachによって提供されるクロージャの範囲は通常他の理由により求められる。
    • Loopは使わなければ行けないところでのみにすべきで、しかしそれらは置き換え可能である事を覚えておくべき。
  • jQuery.eachとfor,while,reverse for, jQuery.fn.eachや他のloopのアプローチは http://jsperf.com/jquery-each-vs-for-loop/24 ここに記載されている。
  • jQuery.fn.eachとBen Alman'sの.each2()は http://jsperf.com/jquery-each-vs-quickeach/3 こちらに記載されている。

Performancetip10 必要の無いjQuery Objectを新たに生成することは避ける

  • $( 'a' ).map( function() { return $( this ).text(); } );
  • 開発者は共通で上のようななにかのtextにアクセスするような場合、jQuery Objectを生成してしまう。
  • $.fn.method()よりも$.method()を使う方がパフォーマンスを改善する助けとなる。
  • 全てのjQuery methodがそれぞれの単体node関数を所有しているわけではない。
  • JamesはjQuery.single()という解決方法を提案した。
  • jQuery.single()を全てのjQuery objectの呼び出しに利用したり、単体のDOM要素に対してのみ適用できる。

Bonus Tip コードのDRYは保つべき

  • 同じコードを繰り返す事はコードのサイズを増加するだけでなく、製品としての質を下げる事になる。
  • DRY( don't repeat yourself )はそれぞれの知識に対する一つの表現を推奨する。
  • コードを最小限に保つ事はchainigやcachingやその他のことで手助けする事を思い出させてくれる。

Node.jsでWebSocketを試してみる

Nodejs

  • サーバサイドJavascript
  • V8 Javascriptを利用。
  • シングルスレッドの非同期処理環境。
  • 処理を待たずにcallbackを実行するイベントループ、ノンブロッキングI/Oを実装。
  • nodejsの設定は簡単。パッケージ化されているし、buildしてもそれほど時間がかからない。

設定

macでinstallしてみる。以下のどちらか一方を行えば設定は可能だがportのversionは0.2.0、buildの最新は0.4.8。post installは少し時間がかかる。

port install
$ sudo port install nodejs
$ node -v
v0.2.0
make install
$ fetch http://nodejs.org/dist/node-v0.4.8.tar.gz
$ tar -xzf node-v0.4.8.tar.gz
$ cd node-v0.4.8
$ ./configure
$ sudo make install
$ ./node -v
v0.4.8

実行

日本語ドキュメントにあるサンプルをそのまま実行してみる。サンプルはHttp,Socketサーバ。以下のそれぞれのJavascriptっぽいコードをsample.jsなどのファイルとして保存してnodeコマンドにより実行する。

Http

スクリプト

var http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World\n');
}).listen(1337, "127.0.0.1");
console.log('Server running at http://127.0.0.1:1337/');

起動

$ node http.js

アクセス
ブラウザでhttp://127.0.0.1:1337/にアクセスしてみると Hello Worldが表示される。

Socket

スクリプト

var net = require('net');

var server = net.createServer(function (socket) {
  socket.write("Echo server\r\n");
  socket.pipe(socket);
});

server.listen(1337, "127.0.0.1");

起動

$ node socket.js

アクセス
telnetコマンドによりlistenしているポートにアクセスし、文字列を打つとechoされる。

$ telnet 127.0.0.1 1337 
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Echo server
NodeJS Test
NodeJS Test

npm

  • npmとはNode.js用のパッケージマネージャ。Pythonのeasy_installみたいなもの。
  • npmコマンドによりnode.jsのパッケージをinstallできる。
npmのinstall

まずはnpmコマンドのinstall。

$ fetch http://npmjs.org/install.sh
$ chmod +x ./install.sh
$ sudo ./install.sh
$ npm -v
1.0.6
パッケージのinstall

npmコマンドによりWebSocket実装可能なパッケージ(Socket.io)をinstallする。

$ sudo npm install socket.io

WebSocket

nodejsをつかってWebSocketを試してみる。

WebSocketとは
  • Cometに取って代わる技術と言われており、専用プロトコルでクライアント/サーバ間のConnectionを張りっぱなしにできる。
  • HTML5の仕様に組み込まれる予定だったが、現在では独自のプロトコルとして仕様が策定されている。
  • 対応ブラウザ:FF4、safari5、Chrome4、Opera11、IE(HTML5Labs)
  • nodejsだけでなくPHPでもWebSocketサーバは設定できる。http://code.google.com/p/phpwebsocket/
Socket.ioのサンプルを見てみる

npmによりinstallしたsocket.ioパッケージのsampleはnode_modules/socket.io/example/に配置される。node_modules/socket.io/example/server.jsというサーバのサンプルプログラムを見てみる。初心者で全くnodejsに詳しくないが、だいたい以下のような感じだと思う。

  • http.createServerでサーバオブジェクト作成。
  • server.listen(8080)でポートを指定して起動。
  • client.sendでclientにメッセージを送信。
  • client.broadcastで全てのclientにメッセージを送信。
/**
 * Important note: this application is not suitable for benchmarks!
 */

// 必要なモジュールのrequire
var http = require('http')
  , url = require('url')
  , fs = require('fs')
  , io = require('../')
  , sys = require(process.binding('natives').util ? 'util' : 'sys')
  , server;

//serverのcreate
server = http.createServer(function(req, res){
  // your normal server code
  var path = url.parse(req.url).pathname;
  switch (path){
    case '/':
      res.writeHead(200, {'Content-Type': 'text/html'});
      res.write('<h1>Welcome. Try the <a href="/chat.html">chat</a> example.</h1>');
      res.end();
      break;
      
    case '/json.js':
    case '/chat.html':
      fs.readFile(__dirname + path, function(err, data){
        if (err) return send404(res);
        res.writeHead(200, {'Content-Type': path == 'json.js' ? 'text/javascript' : 'text/html'})
        res.write(data, 'utf8');
        res.end();
      });
      break;
      
    default: send404(res);
  }
}),

//caseに当てはまらなかった時の404処理
send404 = function(res){
  res.writeHead(404);
  res.write('404');
  res.end();
};

//8080ポートで起動
server.listen(8080);

// socket.io, I choose you
// simplest chat application evar
var io = io.listen(server)
  , buffer = [];
 
//clientとconnectionが張られているとき
io.on('connection', function(client){
  //clientにbufferを送信
  client.send({ buffer: buffer });
  //全てのclientにclientidとつながっていることをアナウンス
  client.broadcast({ announcement: client.sessionId + ' connected' });
  
  //clientからメッセ時を受け取ったとき
  client.on('message', function(message){
    //メッセージ情報をbufferに格納。
    var msg = { message: [client.sessionId, message] };
    buffer.push(msg);
    if (buffer.length > 15) buffer.shift();
    //メッセージをその他clientにブロードキャストする
    client.broadcast(msg);
  });

  //clientとの接続が切れたとき
  client.on('disconnect', function(){
    //clientが切断されたことをその他clientにブロードキャストする。
    client.broadcast({ announcement: client.sessionId + ' disconnected' });
  });
});
クライアントhtml

json.jsとsocket.io/socket.io.jsを読み込む必要がある。以下はおれおれ解説。

  • new io.SocketでSocketオブジェクト作成。
  • socket.connectでサーバとのconnectionを確立。
  • socket.on('connect',(省略)でサーバとconnectionが確立されたらハンドリングする。
  • socket.on('message',(省略)でサーバからメッセージを受信した時にハンドリングする。
  • socket.onのその他 disconnect(切断)、reconnect(再接続後)、reconnecting(再接続中)の時のハンドリング。
<!doctype html>
<html>
  <head>
    <title>socket.io client test</title>
        
    <script src="/json.js"></script> <!-- for ie -->
    <script src="/socket.io/socket.io.js"></script>
  </head>
  <body>
    
    <script>
      //メッセージ出力関数。
      function message(obj){
        var el = document.createElement('p');
        if ('announcement' in obj) el.innerHTML = '<em>' + esc(obj.announcement) + '</em>';
        else if ('message' in obj) el.innerHTML = '<b>' + esc(obj.message[0]) + ':</b> ' + esc(obj.message[1]);
        
        if( obj.message && window.console && console.log ) console.log(obj.message[0], obj.message[1]);
        document.getElementById('chat').appendChild(el);
        document.getElementById('chat').scrollTop = 1000000;
      }
      
      //メッセージ送信
      function send(){
        var val = document.getElementById('text').value;
        socket.send(val);
        message({ message: ['you', val] });
        document.getElementById('text').value = '';
      }
      
      //HTML escape
      function esc(msg){
        return msg.replace(/</g, '&lt;').replace(/>/g, '&gt;');
      };
      
      // socketオブジェクト作成
      var socket = new io.Socket(null, {port: 8080, rememberTransport: false});
      // サーバとのconnection確立
      socket.connect();
      //メッセージ受信ハンドリング
      socket.on('message', function(obj){
        if ('buffer' in obj){
          document.getElementById('form').style.display='block';
          document.getElementById('chat').innerHTML = '';
          
          for (var i in obj.buffer) message(obj.buffer[i]);
        } else message(obj);
      });
      
      //connection確立ハンドリング
      socket.on('connect', function(){ message({ message: ['System', 'Connected']})});
      //connection切断ハンドリング
      socket.on('disconnect', function(){ message({ message: ['System', 'Disconnected']})});
      //connection再確立ハンドリング
      socket.on('reconnect', function(){ message({ message: ['System', 'Reconnected to server']})});
      //connection再接続中ハンドリング
      socket.on('reconnecting', function( nextRetry ){ message({ message: ['System', 'Attempting to re-connect to the server, next attempt in ' + nextRetry + 'ms']})});
      socket.on('reconnect_failed', function(){ message({ message: ['System', 'Reconnected to server FAILED.']})});
    </script>
    
    <h1>Sample chat client</h1>
    <div id="chat"><p>Connecting...</p></div>
    <form id="form" onSubmit="send(); return false">
      <input type="text" autocomplete="off" id="text"><input type="submit" value="Send">
    </form>
    
    <style>
      #chat { height: 300px; overflow: auto; width: 800px; border: 1px solid #eee; font: 13px Helvetica, Arial; }
      #chat p { padding: 8px; margin: 0; }
      #chat p:nth-child(odd) { background: #F6F6F6; }
      #form { width: 782px; background: #333; padding: 5px 10px; display: none; }
      #form input[type=text] { width: 700px; padding: 5px; background: #fff; border: 1px solid #fff; }
      #form input[type=submit] { cursor: pointer; background: #999; border: none; padding: 6px 8px; -moz-border-radius: 8px; -webkit-border-radius: 8px; margin-left: 5px; text-shadow: 0 1px 0 #fff; }
      #form input[type=submit]:hover { background: #A2A2A2; }
      #form input[type=submit]:active { position: relative; top: 2px; }
    </style>
    
  </body>
</html>

サンプルのSocketサーバの起動

$ node node_modules/socket.io/example/server.js

アクセス
複数のWebブラウザでアクセスしてコメントを投稿してみるとWebSocketのチャットが利用可能。
http://127.0.0.1:8080/chat.html

リファレンス

  • 日本語ドキュメント

http://nodejs.jp/nodejs.org_ja/

  • API日本語マニュアル

http://nodejs.jp/nodejs.org_ja/api/index.html

  • Node.jsでサーバサイドJavaScript開発入門

http://www.atmarkit.co.jp/fwcr/index/index_nodejs.html

  • npm registry

http://search.npmjs.org/#/_browse/all

超簡単! ボタン一つで登録可能なソーシャルメディアShareBookmarklet

概要

  • 上記アイコンのWebサービスに対してクリックや画面タッチで簡単にシェアができるBookmarkletを作成しました。
    • Webページには各ソーシャルサービスに対するシェアボタン/bookmarkボタンが無い場合が多い。シェアボタン/bookmarkボタンが無いとユーザは自分で各サービスのページに遷移し、内容を登録しなければならないが、それはとても面倒である。
    • 上記アイコンのサービスはそれぞれでシェア用のbookmarkletを作成している場合もあるが、サービスごとのbookmarklet管理はユーザの負担。
    • addthisというマルチシェアサービスには多サービスへの展開可能なbookmarkletが存在するが、アイコンが小さく使いづらいのとsmartphone非対応。

設定

  • 以下のいずれかの方法でbookmark登録してください。※対応しているブラウザはページ下部で確認してください。
javascript:void((function(){var%20s=document.createElement('script');s.setAttribute('src','http://mobiles-proxy.appspot.com/statics/js/multishare.js');s.setAttribute('id','multishare');document.body.appendChild(s)})())

動作イメージ

  • 閲覧しているWebページ上で登録したbookmarkletをクリックするとアイコンメニューがページ左に表示されます。

  • 各サービスアイコンのどれかをクリックします。下の画像はtwitterの例です。クリックするとtwitterの画面に遷移し、タイトルと短縮URLがつぶやきのエリアに入ります。その他のソーシャルサービスでも同じ様にbookmarkやshareが簡単にできます。

  • iPhoneでも同様の事が可能です。twitterのボタンをクリックするとmobileのtwitterページに遷移します。後は上の例と同じです。

シェア対象サービス

以下のサービスにシェアすることを可能としています。

動作確認

以下のブラウザで動作確認をしています。android端末は現在動作確認中です。

補足

  • URL短縮機能はtwitterのみ対応しています。
  • 追加してほしいソーシャルサービスがあればコメントに記述してください。またボタンアイコン配置のUIもちょっとイケテナイ感満載なので、改善希望もあればお願いします!

jQueryの参考にすべきSiteのまとめ

基礎

UI

Plugin

パフォーマンス

Javascriptによる正規表現まとめ

Index

JavaScriptで利用できる正規表現についてまとめました。
最後の後読み、否定後読み以外は利用可能かと思います。

  1. モード修飾子(i,m,g)
  2. \n,\r,\u2028,\u2029以外の任意の1文字
  3. 空白文字以外( \S )
  4. 数字( \d )
  5. 数字以外( \D )
  6. 先頭( ^ )
  7. 末尾( $ )
  8. 単語区切り( \b )
  9. 単語区切り以外( \B )
  10. 0以上の繰り返し( * )
  11. 1以上の繰り返し( + )
  12. 0または1回( ? )
  13. 最短マッチ( .*? )
  14. nの繰り返し( {n} )
  15. n以上の繰り返し( {n,} )
  16. n以上,m以下の繰り返し( {n,m} )
  17. いずれかの文字( [▲◎■] )
  18. いずれかのパターン( (▲|◎|■) )
  19. 後方参照( \n )
  20. エスケープ( \ )
  21. 先読み( ?=◎◎◎ )
  22. 先読み否定( ?!◎◎◎ )
  23. 後読み( ?<=◎◎◎ )
  24. 否定後読み( ?<!◎◎◎ )

モード修飾子(i,m,g)

正規表現の末尾にモード修飾子を付与する事で便利なオプションが利用できる。

修飾子 説明
i 大文字、小文字の区別をしない
m ^と$を文字列の改行の前後に一致させる
g 全ての検索

表記例

//大文字、小文字を区別しない
var text = 'JavaScript';
if( /javascript/i.test( text ) ) {
    alert( 'mode i' );
}

\n,\r,\u2028,\u2029以外の任意の1文字

( . )は任意の文字を示す。 修正:2011/3/7
( . )は\n,\r,\u2028,\u2029以外の任意の1文字を示す。

// 任意の文字 
var text = 'JavaScript';
if( /Java...../.test( text ) ) {
    alert( '. match' );
}

MDN DocCenter( Regex )
https://developer.mozilla.org/ja/JavaScript/Reference/Global_Objects/RegExp
Unicodeの改行
http://www.nilab.info/zurazure2/000739.html

単語の文字( \w )

( \w )は単語の文字を示す。( \w )は( [a-zA-Z0-9_] )と同義。

//単語の文字
var text = 'JavaScript';
if( /Java\w\w\w\w\w/.test( text ) ) {
    alert( '\\w match' );
}

単語の文字以外( \W )

( \W )は単語の文字以外を示す。

//単語の文字以外
var text = 'JavaScript!!';
if( /JavaScript\W\W/.test( text ) ) {
    alert( '\\W match' );
}

空白文字( \s )

( \s )は空白文字を示す。( \s )は( [\t\n\r\f] )と同義。
空白文字はタブ、改行、復帰、改ページを含む。

//空白文字
var text = 'Love JavaScript';
if( /Love\sJavaScript/.test( text ) ) {
    alert( '\\s match' );
}

スペースが全角の場合は一致しない。

//空白文字
var text = 'Love JavaScript';
if( /Love\sJavaScript/.test( text ) ) {
    alert( '\\s match' );
}

空白文字以外( \S )

( \S )は空白文字以外を示す。( \S )は( [^\t\n\r\f] )と同義。

//空白文字以外
var text = 'Love JavaScript';
if( /Love\SJavaScript/.test( text ) ) {
    alert( '\\S match' );
}

数字( \d )

( \d )は数字を示す。( \d )は( [0-9] )と同義。

//数字
var text = 'V8';
if( /\w\d/.test( text ) ) {
    alert( '\\d match' );
}

数字以外( \D )

( \D )は数字以外を示す。( \D )は( [^0-9] )と同義。

//数字以外
var text = 'JavaScript!';
if( /\D/.test( text ) ) {
    alert( '\\D match' );
}

先頭( ^ )

( ^ )は文字列の先頭に一致。

//先頭
var text = 'JavaScript';
if( /^J\w+/.test( text ) ) {
    alert( '^ match' );
}

末尾( $ )

( $ )は文字列の末尾に一致。

//終端
var text = 'JavaScript';
if( /\w+t$/.test( text ) ) {
    alert( '$ match' );
}

先頭、末尾を利用すると空行を検知可能。

//空行
var text = '';
if( /^\s*$/.test( text ) ) {
    alert( '^\\s*$ match' );
}

単語区切り( \b )

( \b )は単語の境界を示す。

//単語の区切り
var text = ' JavaScript ';
if( /\b\w+\b/.test( text ) ) {
    alert( '\\b match' );
}

※ \b*や\b+などの繰り返しはエラーになる。

単語区切り以外( \B )

( \B )は単語の境界以外を示す。

//単語の区切り以外
var text = 'JavaScript';
if( /\BScri\B/.test( text ) ) {
    alert( '\\B match' );
}

※ \B*や\B+などの繰り返しはエラーになる。

0以上の繰り返し( * )

( * )は0以上の繰り返しを示す。

//0以上の繰り返し
var text = 'JavaScript';
if( /^\w*$/.test( text ) ) {
    alert( '* match' );
}

単語の繰り返しとしても利用可能

//0以上の繰り返し(単語)
var text = 'JavaScript JavsScript';
if( /(JavaScript\s*)*/.test( text ) ) {
        alert( '* match' );
}

1以上の繰り返し( + )

( + )は1以上の繰り返しを示す。

//1回以上の繰り返し
var text = 'JavaScript';
if( /Java\w+cript/.test( text ) ) {
    alert( '+ match' );
}

0または1回( ? )

( ? )は0または1回の繰り返しを示す。

//0回
var text = 'JavaScript';
if( /Java\s?Script/.test( text ) ) {
    alert( '? match' );
}

//1回
var text = 'Java Script';
if( /Java\s?Script/.test( text ) ) {
    alert( '? match' );
}

最短マッチ( .*? )

( .*? )は最短のマッチを抽出する。

//最短マッチ
var text = "<img src='./javascript.png' /><img src='./perl.png' />";
if( /<img.*?src='(.*?)'\s*\/>/.test( text ) ) {
    alert( '.*? match' );
    alert( RegExp.$1 ); // ./JavaScript.pngが出力
}

nの繰り返し( {n} )

( {n} )はn回の繰り返しを示す。

//単語のn回の繰り返し
var text = 'JavaScript JavaScript';
if( /(JavaScript\s?){2}/.test( text ) ) {
    alert( '{n} match' );
}

n以上の繰り返し( {n,} )

( {n,} )はn以上の繰り返しを示す。

//単語のn回以上の繰り返し
var text = 'JavaScript JavaScript';
if( /(JavaScript\s?){2,}/.test( text ) ) {
    alert( '{n,} match' );
}

n以上,m以下の繰り返し( {n,m} )

( {n,m} )はn以上,m以下の繰り返しを示す。

//単語のn回以上,m以下の繰り返し
var text = 'JavaScript JavaScript';
if( /(JavaScript\s?){1,2}/.test( text ) ) {
    alert( '{n,m} match' );
}

いずれかの文字( [▲◎■] )

( [▲◎■] )はいずれかの文字に一致。

//列挙単語のグループ
var text = "JavaScript";
if( /[aciJprStv]{10}/.test( text ) ) {
    alert( '[] match' );
}

否定をする事が可能

//列挙単語の否定グループ
var text = "JavaScript";
if( /[^aciJprStv]?/.test( text ) ) {
    alert( '[^] match' );
}

いずれかのパターン( (▲|◎|■) )

( (▲|◎|■) )はいずれかのパターンに一致。

//いずれかのパターン
var text = "JavaScript vs Perl";
if( /\w+(\s|\b)?\w+(\s|\b)?\w+/.test( text ) ) {
    alert( '| match' );
    alert( RegExp.$1 ); //後方参照はスペースが出力
}

後方参照( \n )

( \n )は後方参照を示す。

//後方参照
var text = "JavaScript";
if( /^Java(\w+)$/.test( text ) ) {
    alert( '() match' );
    alert( RegExp.$1 ); //Scriptが表示 正規表現としては\1という意味
}

後方参照をしない事も可能

//後方参照しない
var text = "JavaScript";
if( /^Java(?:\w+)$/.test( text ) ) {
    alert( '() match' );
    alert( RegExp.$1 ); //Scriptが表示されない 後方参照しない
}

エスケープ( \ )

( \ )はメタキャラをエスケープさせて通常の文字として認識させる場合。

//メタキャラクタのエスケープ
var text = "*JavaScript$";
if( /^\*\w+\$$/.test( text ) ) {
    alert( '\\ match' );
}

先読み( ?=◎◎◎ )

( ?=◎◎◎ )は後に続くパターン。

//先読み
var text = "JavaScript V8";
var data = text.match( /\w+(?=\d{1})/ );
if( data !== null ) {
    alert( data ); //Vが表示される
}

先読み否定( ?!◎◎◎ )

( ?!◎◎◎ )とすると否定先読み可能。

//先読み
var text = "JavaScript V8";
var data = text.match( /\w+(?!\d{1})/ );
if( data !== null ) {
    alert( data ); //JavaScriptが表示される
}

後読み( ?<=◎◎◎ )

※後読みはJavaScriptでサポートされていない。
( ?<=◎◎◎ )前に続くパターン

//後読み
var text = "JavaScript V8";
var data = text.match( /(?<=\w)\d/ );
if( data !== null ) {
    alert( data ); //Vが表示される
}

否定後読み( ?<!◎◎◎ )

※否定後読みはJavaScriptでサポートされていない。
( ?<!◎◎◎ )前に続かないパターン

//後読み
var text = "JavaScript V8";
var data = text.match( /(?<!\w)\d/ );
if( data !== null ) {
    alert( data ); //JavaScriptが表示される。
}