Node.jsでファイルを2行ずつ処理する

昨日、1行ずつ処理する方法はわかったけど、複数行、例えば2行ずつ処理したいこともある。ググってみたら Qiita で見つけた。

 cf. ストリームを行ごとに処理~readline編~ – Qiita

真似して書いてみた。

var readline = require('readline');
var fs = require('fs');

function LineReader(stream) {
  var self = this;
  self.rl = readline.createInterface({
    "input": stream,
    "output": {}
  });
  self.list = [];
}

LineReader.prototype.forEach = function(unit, fn, callback) {
  var self = this;

  self.rl.on("line", function(line) {
    self.list.push(line);
    if (self.list.length == unit) {
      fn(self.list);
      self.list = [];
    }
  });

  self.rl.on("close", function(line) {
    if (self.list.length) {
      fn(self.list);
    }
    callback();
  });
}

var infile = process.argv[2];
var rs = fs.createReadStream(infile);
var linereader = new LineReader(rs);

linereader.forEach(2, function(list) {
  console.log(list.join(":"));
}, function() {
});
takatoh@nightschool $ cat input.txt
3
1 2 3
4
2 4 8 16
4
2 3 5 7
takatoh@nightschool $ nodejs readline_unit.js input.txt
3:1 2 3
4:2 4 8 16
4:2 3 5 7

上の実行例では2行ずつだけど、forEach の第1引数でほかの値を指定すれば任意行ずつ処理ができる。
ただ、指定した行(とその端数)ずつしか処理できないんだよな。何行ずつ処理するかを動的には決められない。Ruby の gets みたいなのがあればいいのに。

Node.jsでファイルを1行ずつ処理する

Web で見つけてきた JavaScript の動作検証をしようと、ファイルからデータ(1行に1データ)を読み込んで処理する方法を探したら見つかったのでメモ。

 cf. Node.jsでテキストを1行ずつ処理する – console.lealog();

var fs = require('fs');
var rl = require('readline');

var inputStream = fs.createReadStream('./input.txt');
var inputReadLine = rl.createInterface({'input': inputStream, 'output': {}});

inputReadLine
  .on('line', function(line){
    console.log(line * 10.0);
  })
  .on('close', function() {
  });
takatoh@nightschool $ cat input.txt
1
2
3
4
5
6
7
8
9
10
takatoh@nightschool $ nodejs readline.js
10
20
30
40
50
60
70
80
90
100

さくらVPSにnode.jsをインストールした

普通に yum コマンドでインストールできた。

[takatoh@www2465uo ~]$ sudo yum install nodejs
[sudo] password for takatoh: 
Loaded plugins: fastestmirror, security
Loading mirror speeds from cached hostfile
epel/metalink                                            | 5.6 kB     00:01     
 * base: ftp.tsukuba.wide.ad.jp
 * epel: ftp.riken.jp
 * extras: ftp.tsukuba.wide.ad.jp
 * updates: ftp.tsukuba.wide.ad.jp
base                                                     | 3.7 kB     00:00     
epel                                                     | 4.4 kB     00:00     
extras                                                   | 3.4 kB     00:00     
updates                                                  | 3.4 kB     00:00     
updates/primary_db                                       | 4.7 MB     00:21     
Setting up Install Process
Resolving Dependencies
--> Running transaction check
---> Package nodejs.x86_64 0:0.10.29-1.el6 will be installed
--> Processing Dependency: v8(x86-64) < 1:3.15 for package: nodejs-0.10.29-1.el6.x86_64
--> Processing Dependency: v8(x86-64) >= 1:3.14.5.7 for package: nodejs-0.10.29-1.el6.x86_64
--> Processing Dependency: libv8.so.3()(64bit) for package: nodejs-0.10.29-1.el6.x86_64
--> Processing Dependency: libuv.so.0.10()(64bit) for package: nodejs-0.10.29-1.el6.x86_64
--> Processing Dependency: libhttp_parser.so.2()(64bit) for package: nodejs-0.10.29-1.el6.x86_64
--> Processing Dependency: libcares19.so.2()(64bit) for package: nodejs-0.10.29-1.el6.x86_64
--> Running transaction check
---> Package c-ares19.x86_64 0:1.9.1-5.el6.3 will be installed
---> Package http-parser.x86_64 0:2.0-4.20121128gitcd01361.el6 will be installed
---> Package libuv.x86_64 1:0.10.27-1.el6 will be installed
---> Package v8.x86_64 1:3.14.5.10-9.el6 will be installed
--> Processing Dependency: libicuuc.so.42()(64bit) for package: 1:v8-3.14.5.10-9.el6.x86_64
--> Processing Dependency: libicui18n.so.42()(64bit) for package: 1:v8-3.14.5.10-9.el6.x86_64
--> Processing Dependency: libicudata.so.42()(64bit) for package: 1:v8-3.14.5.10-9.el6.x86_64
--> Running transaction check
---> Package libicu.x86_64 0:4.2.1-9.1.el6_2 will be installed
--> Finished Dependency Resolution

Dependencies Resolved

================================================================================
 Package          Arch        Version                           Repository
                                                                           Size
================================================================================
Installing:
 nodejs           x86_64      0.10.29-1.el6                     epel      498 k
Installing for dependencies:
 c-ares19         x86_64      1.9.1-5.el6.3                     epel       73 k
 http-parser      x86_64      2.0-4.20121128gitcd01361.el6      epel       22 k
 libicu           x86_64      4.2.1-9.1.el6_2                   base      4.9 M
 libuv            x86_64      1:0.10.27-1.el6                   epel       55 k
 v8               x86_64      1:3.14.5.10-9.el6                 epel      3.0 M

Transaction Summary
================================================================================
Install       6 Package(s)

Total download size: 8.6 M
Installed size: 31 M
Is this ok [y/N]: y
Downloading Packages:
(1/6): c-ares19-1.9.1-5.el6.3.x86_64.rpm                 |  73 kB     00:00     
(2/6): http-parser-2.0-4.20121128gitcd01361.el6.x86_64.r |  22 kB     00:00     
(3/6): libicu-4.2.1-9.1.el6_2.x86_64.rpm                 | 4.9 MB     00:22     
(4/6): libuv-0.10.27-1.el6.x86_64.rpm                    |  55 kB     00:00     
(5/6): nodejs-0.10.29-1.el6.x86_64.rpm                   | 498 kB     00:02     
(6/6): v8-3.14.5.10-9.el6.x86_64.rpm                     | 3.0 MB     00:13     
--------------------------------------------------------------------------------
Total                                           206 kB/s | 8.6 MB     00:42     
Running rpm_check_debug
Running Transaction Test
Transaction Test Succeeded
Running Transaction
  Installing : http-parser-2.0-4.20121128gitcd01361.el6.x86_64              1/6 
  Installing : c-ares19-1.9.1-5.el6.3.x86_64                                2/6 
  Installing : libicu-4.2.1-9.1.el6_2.x86_64                                3/6 
  Installing : 1:v8-3.14.5.10-9.el6.x86_64                                  4/6 
  Installing : 1:libuv-0.10.27-1.el6.x86_64                                 5/6 
  Installing : nodejs-0.10.29-1.el6.x86_64                                  6/6 
  Verifying  : 1:v8-3.14.5.10-9.el6.x86_64                                  1/6 
  Verifying  : 1:libuv-0.10.27-1.el6.x86_64                                 2/6 
  Verifying  : libicu-4.2.1-9.1.el6_2.x86_64                                3/6 
  Verifying  : c-ares19-1.9.1-5.el6.3.x86_64                                4/6 
  Verifying  : http-parser-2.0-4.20121128gitcd01361.el6.x86_64              5/6 
  Verifying  : nodejs-0.10.29-1.el6.x86_64                                  6/6 

Installed:
  nodejs.x86_64 0:0.10.29-1.el6                                                 

Dependency Installed:
  c-ares19.x86_64 0:1.9.1-5.el6.3                                               
  http-parser.x86_64 0:2.0-4.20121128gitcd01361.el6                             
  libicu.x86_64 0:4.2.1-9.1.el6_2                                               
  libuv.x86_64 1:0.10.27-1.el6                                                  
  v8.x86_64 1:3.14.5.10-9.el6                                                   

Complete!
[takatoh@www2465uo ~]$ node -v
v0.10.29

Node.jsでファイルをダウンロードする(2)

今日はちょっと進んで、JSON 形式のファイルから URL のリストを読み込んでダウンロードする。

var http = require('http');
var fs = require('fs');
var path = require('path');

var list = process.argv[2];

var urls = JSON.parse(fs.readFileSync(list, 'utf8'));
urls.forEach(function(url) {
  console.log(url);
  var filename = path.basename(url);

  // output stream
  var outFile = fs.createWriteStream(filename);

  var req = http.get(url, function(res) {
    res.pipe(outFile);
    res.on('end', function() {
      outFile.close();
    });
  });

  // error handler
  req.on('error', function(err) {
    console.log('Error: ' + err.message);
    return;
  });
});

Node.jsでファイルをダウンロードする

↓このページを参考に書いてみた。っていうかほとんどそのまんまだけど。

 cf. [NodeJS] ファイルをダウンロードする – YoheiM.NET

var http = require('http');
var fs = require('fs');
var path = require('path');

var url = process.argv[2];
console.log(url);
var filename = path.basename(url);

// output stream
var outFile = fs.createWriteStream(filename);

var req = http.get(url, function(res) {
  res.pipe(outFile);
  res.on('end', function() {
    outFile.close();
  });
});

// error handler
req.on('error', function(err) {
  console.log('Error: ' + err.message);
  return;
});

変えたのは URL から保存するファイル名を決めるとことだけ。

このスクリプトのキモは、res.pipe でデータをアウトプットストリームに流しこんでいるところ。で、end イベントが発生したらストリームを閉じる、と。

Node.jsをさわってみた

この間は Rails をやろう、みたいな事を書いたくせに、ふと気になって JavaScript の本を読んでいる。目的は jQuery なんだけど Node.js が面白そうだったのでさわってみることにした。

まずはダウンロードとインストールから。nodejs.orgにアクセスすると、ページの真ん中に INSTALL ボタンがあるので、クリックしてインストーラをダウンロード。バージョンは v0.10.29。ダウンロードされた node-0.10.29-x64.msi をダブルクリックしてインストールする。特に迷うこともなくインストールは終了。
試しにバージョンを表示してみる。

^o^ > node -v
v0.10.29

うまくインストールできてるようだ。

さて、Node.js には npm というパッケージマネージャが付属している。これでパッケージの管理ができるんだけど、プロジェクトを作るときにも使うらしい。
とりあえずは、単純にリクエストされたファイル(画像)を返すだけのサーバを作ってみることにした。
まずは、プロジェクトのフォルダを作ってその中に移動し、npm init で初期化する。

^o^ > mkdir image_server

^o^ > cd image_server

^o^ > npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sane defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install  --save` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
name: (image_server)
version: (0.0.0) 0.0.1
description:
entry point: (index.js) server.js
test command:
git repository:
keywords:
author: takatoh
license: (ISC)
About to write to C:\Users\hiro\Documents\w\image_server\package.json:

{
  "name": "image_server",
  "version": "0.0.1",
  "description": "",
  "main": "server.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "takatoh",
  "license": "ISC"
}


Is this ok? (yes) yes

いくつか質問に答えると、プロジェクトの雛形が完成。この時点でフォルダの構成はこうなっている。

^o^ > tree /F
フォルダー パスの一覧:  ボリューム OS
ボリューム シリアル番号は FE2A-F7C6 です
C:.
    package.json

サブフォルダーは存在しません

package.json というファイルがあるだけだね。
次は依存するパッケージのインストール。今回はリクエストに応じて送信するファイルの MIME TYPE を調べるために、mime というパッケージを利用する。これを package.json に追加する。

{
    "name": "image_server",
    "version": "0.0.1",
    "description": "",
    "main": "server.js",
    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
    },
    "dependencies": {
        "mime": "*"
    },
    "author": "takatoh",
    "license": "ISC"
}

色をつけたところが追記したところ。"*" は最新のバージョンをインストールすることを示している。ここに特定のバージョンを書けばバージョン指定もできるようだ。
追記が終わったら mime パッケージのインストール。npm install コマンドを使う。

^o^ > npm install
npm WARN package.json [email protected] No description
npm WARN package.json [email protected] No repository field.
npm WARN package.json [email protected] No README data
[email protected] node_modules\mime

npm install は package.json を参照して、まだインストールされていないパッケージをインストールしてくれる。パッケージはプロジェクトフォルダの中の node_modules フォルダにインストールされる。この時点でフォルダ構成はこうなっている。

^o^ > tree /F
フォルダー パスの一覧:  ボリューム OS
ボリューム シリアル番号は FE2A-F7C6 です
C:.
│  package.json
│
└─node_modules
    └─mime
        │  LICENSE
        │  mime.js
        │  package.json
        │  README.md
        │  test.js
        │
        └─types
                mime.types
                node.types

node_modules の下に mime がインストールされているのがわかる。

さあ、今度はサーバ本体だ。いろいろ調べながら出来上がったのがこれ。

var http = require("http");
var fs = require("fs");
var url = require("url");
var mime = require("mime");

server = http.createServer(function(request, response) {
  var path = url.parse(request.url).pathname;
  console.log(path);
  var mimetype = mime.lookup(path);
  var filepath = "./images" + path;
  fs.readFile(filepath, function (err, data) {
    if (err) {
      response.writeHead(404, {"Content-Type" : "text/plain"});
      response.write("File not found.");
      response.end();
    } else {
      response.writeHead(200, {"Content-Type" : mimetype});
      response.write(data);
      response.end();
    }
  });
});

console.log("Start server on port 8888.");
server.listen(8888);

require 関数でパッケージを読み込んで変数に代入している。mime 以外は Node.js に付属しているパッケージで、http はその名のとおり HTTP サーバ/クライアント。fs はローカルのファイルシステムにアクセスするため、url はリクエストの URL を解析するために読み込んでいる。
HTTP サーバは、http.createServer で作れる。引数にはリクエストに応答する関数を指定している。この関数には request と response のオブジェクトが引数として渡されるので、request を解析して response に書き込めばいい。今回は、要求されたファイルを ./images 以下から読み込んで送信している。もしエラーが発生したら、404 File not found を返している。
それじゃ、images フォルダにサンプルの画像を配置しよう。

^o^ > tree /F images
フォルダー パスの一覧:  ボリューム OS
ボリューム シリアル番号は FE2A-F7C6 です
C:\USERS\TAKATOH\DOCUMENTS\W\IMAGE_SERVER\IMAGES
│  Penguins.jpg
│
└─data
        Desert.jpg

これで準備は完了。サーバを起動しよう。次のようにする。

^o^ > node server.js
Start server on port 8888.
nodejs-penguins

これでポート 8888 で待ち受けている状態になった。ブラウザからアクセスしてみよう。

うまくいった!

参考にしたページ:
 cf. http://nodejs.jp/nodejs.org_ja/docs/v0.4/api/http.html#request.url
 cf. http://nodejs.jp/nodejs.org_ja/docs/v0.4/api/fs.html#fs.readFile
 cf. http://stackoverflow.com/questions/5316324/automatic-mimetypes-in-javascript-node-js

自前のWebアプリにLightbox風のjQueryプラグイン、Colorboxを導入してみた

この間、といっても先月の頭のことだけど、昔作った CGI プログラムを手を入れた、という話とちょろっとだけした(そのときのエントリの話題は CGI プログラムそのものじゃなくて markdown と RDiscount)。
で、今日はその CGI プログラムに Colorbox という Lightbox 風の jQuery プラグインを導入してみたので、そのメモ。
Lightbox 風のプラグインて数え切れないほどあるけど、はっきり言ってどれがいいのやらよくわからない。なのでたまたま最初のほうに出てきた Colorbox が悪くなさそうだったので使ってみた、というわけ。
Colorbox のサイトはここ。

 cf. Colorbox – a jQuery lightbox

参考にしたのはこのページ。

 cf. Lightbox風のjQueryプラグインColorBoxの使い方 – bl6.jp

基本的には上に書いたページに沿ってやっただけで、特別なカスタマイズはしてない。

まずは、スクリプトとスタイルシートを CGI のフォルダの下に js というフォルダを作って配置する。
で、表示に使うテンプレートのほうで読み込みと設定をすればいい。読み込む部分はこう。

<script type="text/javascript" src="js/jquery-1.11.1.min.js"></script>
<script type="text/javascript" src="js/colorbox/jquery.colorbox-min.js"></script>
<script type="text/javascript">
    $(document).ready(function(){
        $("a[rel='colorbox']").colorbox();
    });
 </script>

これは <head> 要素の中に書いた。これで rel 属性が ‘colorbox’ の <a>要素が Colorbox の対象になる。なので表示したい画像のリンクに rel=”colorbox” を追加する。

<a href="<%= escape(File.join(@src_prefix, i['path'])) %>" rel="colorbox">

href 属性の中身が escape(File.join.... となってるのは動的に変化する部分だから。

これで設定は終わり。これだけで、モーダルな画像表示ができてスライドショウもできる。なんだかちょっとだけかっこよくなったかも。

最後に jQuery のサイトへのリンクも張っておこう。

cf. jQuery

無名関数

名前を指定しないで関数を定義すると無名関数になる。

変数に代入する場合:

var f1 = function(arg1, arg2) {
    return arg1 * arg2;
}
f1(3,2); // => 6

直接使う場合:

var a = new Array(3, 2, -6, 8, -4);
a.sort(function (a,b) {return a - b;});
document.write(a.toString()); // => -6,-4,2,3,8

arguments

関数定義に仮引数を書かなくても,arguments という配列に格納される。引数の数が不定の場合に便利。

function hello() {
    for (var i=0; i<arguments.length; i++) {
        document.write("hello,=", arguments[i], "<br>");
    }
    return i;
}

hello("Andy", "Bill", "Charlie");