node.js

javascript(electron/nodejs)でカタカナ(濁点あり)の文字が不一致(ジ==ジがfalse)になる

ElectronでGoogleSpreadSheetやGoogleDriveからデータを取得して、文字列の一致判定で濁点が含まれる文字の場合、見た目は同じだけど別の文字として判定されてしまうケースが出てしまった。

見た目は一緒だけど不一致になる濁点カタカタ

ElectronからGoogle SpreadSheetに結果を出力すると、確かに見た目は一緒だけど、SpreadSheetの文字検索でも一致とはならないし、コピーしてVSCodeやSublimeでテキスト検索を試しても別の文字として判定される。

試しにjsで試すとこんな感じ

Welcome to Node.js v15.4.0.
Type ".help" for more information.
> let a = 'ジ'
undefined
> let b = 'ジ'
undefined
> a == b
false

濁点文字の文字コード

調べてみると、UTFとSJISとかのい問題ではなく、UTFの中でも濁点ありには複数あって、濁点を含んだ文字か、濁点が追加された文字か?の違いだった。

escapeでコードを確認すると「し」の濁点ありでも、コードも長さも異なる。

> escape(a)
'%u30B8'
> escape(b)
'%u30B7%u3099'
> a.length
1
> b.length
2

String.normalize( NFC/NFD )

これはstring.normalizeで統一することができるようで、NFCとNFDを指定することで任意の型式に変換できる。

今回はSpreadSheetやDriveからの文字取得時にはNFCに統一する型式にして回避

> escape(a.normalize('NFC'))
'%u30B8'
> escape(b.normalize('NFC'))
'%u30B8'
> escape(a.normalize('NFD'))
'%u30B7%u3099'
> escape(b.normalize('NFD'))
'%u30B7%u3099'

nodemailerでISO-2022-JPのメールを送信したい( Mailparserじゃなくて送信)

メールの受信プログラムのテストでマルチパートやUTFだけじゃなく、iso-2022-jpのメールも正しく受信できるか試したかったので、nodemailerで送信しようと思いましたが、どうやらnodemailerがiso-2022-jpに対応していない。。

解析するmailparserの例は見つかるけど、iso-2022-jpで送信するサンプルが見つからないので、試した結果、なんんとかiso-2022-jpで送信でき、Yahooメール、Gmailなど主要なサービスでも正しく表示されました。

iconvで変換したiso-2022-jpの文字列をsubjectなどに渡しても、「Content-Type: text/plain; charset=ISO-2022-JP」にはなりません、Content-TypeはUTF8で本文などがiso-2022-jpで文字化けします。

なのでrawを使って強制的にパラメータを指定します。

let message = {
    envelope: {
        from: "from@gmail.com",
        to: ["to@gmail.com"]
    },
    raw: `Content-Type: text/plain; charset=ISO-2022-JP
From: =?ISO-2022-JP?B?` + new Buffer(iconv.convert("nodemailerです").toString()).toString("base64") + `=?= <from@gmail.com>,
To: to@gmail.com
Subject: =?ISO-2022-JP?B?` + new Buffer(iconv.convert("ISO-2022-JPのメール").toString()).toString("base64") + `=?=
` + iconv.convert("nodemailerでISO-2022-JPのメールを送信したい( Mailparserじゃなくて送信)").toString()
};

smtpTransport.sendMail( message , function(error, r) {

これで送信すると、「Content-Type: text/plain; charset=ISO-2022-JP」で送信され、文字化けもなくクライアント側でもISO-2022-JPとして認識されて本文などが表示されます。

npm installでエラー


npm install

Unhandled rejection Error: Command failed: /usr/local/bin/git submodule update -q --init --recursive
warning: templates not found in /var/folders/bv/0637g83s7bz_00000380000gn/T/pacote-git-template-tmp/git-clone-ea88f8b6
warning: templates not found in /var/folders/bv/0637g83s7bz_00000380000gn/T/pacote-git-template-tmp/git-clone-ea88f8b6
git-lfs smudge 'data/merkleTree.json': git-lfs: command not found
error: external filter 'git-lfs smudge %f' failed 127
error: external filter 'git-lfs smudge %f' failed
fatal: data/merkleTree.json: smudge filter lfs failed

brew install git-lfs
git lfs install

Node.jsのイベントループ

Node.jsのイベントループについて

JavaScriptはシングルスレッドなので「並行処理」を行うために、「ノンブロッキングI/O」で「非同期処理」を実現しており
Node.jsはイベントループと呼ばれるモデルを採用しています。

イベントループのイベントキューにイベントが登録され、イベントハンドラを実行します。
イベントハンドラの完了した結果は、イベントキューにコールバック関数を登録することで受け取ります。

そのため、シングルスレッドでブロックするI/Oを行った場合、そのイベント完了後に次のイベントが実行されてしまう。
並行処理させるためには、ノンブロッキングI/Oを使ってイベントの完了を待たずに次のイベントハンドラを実行させます。

Node.jsはイベントを生成と処理の制御を行うので
ファイルのリード、DBへのクエリ発行、HTTPサーバがリクエストを受けた時などにイベントが生成されます。

例えばテストファイルを作って試すとわかります。

test.js

var oHttp = require('http');
var oFs = require('fs');
oHttp.createServer(function(req, res) {
    console.log("Start");
    oFs.readFile('index.txt', function(err, content) {
        res.writeHead(200, { 'Content-Type':'text/html; charset=utf-8' });
        res.end(content);
        console.log("Response");
    });
    console.log("End");
}).listen(8080, '127.0.0.1');
console.log('ListenStart');

読み込むファイル

<html/>
 <head/>
   <title/>index</title/>
 </head/>
  <body/> みれた? </body/>
</html/>

実行

[root@dti-vps-srv34 ~]#node test.js

ブラウザで「http://127.0.0.1:8080」にアクセス。

[root@dti-vps-srv34 ~]#node test.js
ListenStart
Start
End
Response

EndがResponseより先になっています。
これはreadFile()にコールバック関数を登録しているので、非同期となり
読み込み完了した時点で関数が呼び出されているからです。

プログラムの書き方によっては非同期の部分をロックしてしまうので注意。

Node.jsとは

Node.jsとはサーバサイドJavaScriptの一種

サーバサイドJavaScriptとはサーバサイドのアプリケーションの実装言語として、JavaScriptを使用する言語および環境のこと
ブラウザ(クライアント)サイドと同じ開発言語で、サーバサイドの開発が出来るので、開発効率が上がるといわれている。

サーバサイドJavaScriptにはこれらがある。

  • Node.js
    googleが開発したエンジン、V8で実行する環境
  • Rhino
    JavaVM上で実行するJavaScript言語処理系
  • Aptana Jaxer
    Aptana社がオープンソースとして配布している。

でもそれだけならそんなに自分は興味を持ちませんでした。
node.jsの何がすごいと感じたかというと

  • WebSocketが簡単そう。
  • サーバーの負荷分散に期待あり。

です。

WebSocketが簡単そうは「node.jsでTwitteのタイムライン取得」でも書きましたが
ほんとに簡単に実装が出来ました。
npmというパッケージマネージャーでフレームワークなどのライブラリを取り込めるのでいろいろ可能性があります。

負荷分散はC10k 問題で「apache vs nginx」=「スレッド対イベントループ」を取り上げています。
Apacheはスレッド、nginx はイベントループで、スレッドモデルは実行スタックをコピーするので
スレッド(アクセス)が増えるほどメモリ使用率が上昇するが、イベントループはプロセスは1個なので
メモリ使用量はあまり上がらないので、大量のアクセスに関してはイベントループが有利ということ。

そして、Node.jsは後者のイベントループ
これによって、特定の重い処理を待つことなく、次の入力を受け取れるので
トランザクションを重視するより、短縮URLのようなIOを優先されるような場合で活用できそう。

node.jsとnpmのトラブル

node.jsのパッケージマネージャーである、npmを使ってるが
いろんなマシンで試したけど、何かとトラブルがでる。

いろいろって言いながら、全部CentOSです。

・VMwere上のCentOS5.5
・VirtualBox上のCentOS5.5
・物理マシン上のCentOS5.4
・ServerMan上のCentOS5.5

仮想ソフトは関係ないと思うけど、いろいろトラブルからインストール時に
~develとかのパッケージを入れたり、試してみた。
今のところ一番うまく言ったのはVirtualBoxの仮想マシン。
本当はServerManでうまくいってほしかった。

絶対起こるのはhttp-basic-authのインストールでJSONのパースエラー
JSON書き直してローカルインストールすればOKそうだけど、とりあえず、http-basic-auth.jsを直でおいて呼び出して回避

[root@dti-vps-srv34 ~]# npm install http-basic-auth
npm info it worked if it ends with ok
npm info using npm@0.3.15
npm info using node@v0.4.2
npm info fetch http://registry.npmjs.org/http-basic-auth/-/http-basic-auth-0.1.0.tgz
npm ERR! couldn't read package.json in /tmp/npm-1300626776833/1300626776833-0.38027541153132915/contents/package
npm ERR! Error installing http-basic-auth@0.1.0
npm ERR! Error: Failed to parse json
npm ERR! Unexpected token ILLEGAL
npm ERR!     at jsonParseFail (/usr/local/lib/node/.npm/npm/0.3.15/package/lib/utils/read-json.js:89:11)
npm ERR!     at /usr/local/lib/node/.npm/npm/0.3.15/package/lib/utils/read-json.js:82:14
npm ERR!     at P (/usr/local/lib/node/.npm/npm/0.3.15/package/lib/utils/read-json.js:62:40)
npm ERR!     at cb (/usr/local/lib/node/.npm/npm/0.3.15/package/lib/utils/graceful-fs.js:31:9)
npm ERR!     at [object Object]. (fs.js:86:5)
npm ERR!     at [object Object].emit (events.js:39:17)
npm ERR!     at afterRead (fs.js:843:12)
npm ERR! JSON.parse 
npm ERR! JSON.parse Failed to parse package.json data.
npm ERR! JSON.parse Note that package.json must be actual JSON, not
npm ERR! JSON.parse just a JavaScript object.
npm ERR! JSON.parse 
npm ERR! JSON.parse This changed in npm 0.3.0, and is not a bug in npm.
npm ERR! JSON.parse Tell the package author to fix their package.json file.
npm ERR! JSON.parse
npm ERR! System Linux 2.6.18-194.3.1.el5.028stab069.6
npm ERR! argv { remain: [ 'http-basic-auth' ],
npm ERR! argv   cooked: [ 'install', 'http-basic-auth' ],
npm ERR! argv   original: [ 'install', 'http-basic-auth' ] }
npm not ok
[root@dti-vps-srv34 ~]# 

次にServerManで初めてでたbase64が無いよエラー
今までbase64をあえてインストールした記憶無いのに。

[root@dti-vps-srv34 node]# node StreamTwitter.js

node.js:116
        throw e; // process.nextTick error, or 'error' event on first tick
        ^
Error: Cannot find module 'base64'
    at Function._resolveFilename (module.js:299:11)
    at Function._load (module.js:245:25)
    at require (module.js:327:19)
    at Object. (/var/www/node/http-basic-auth.js:3:14)
    at Module._compile (module.js:383:26)
    at Object..js (module.js:389:10)
    at Module.load (module.js:315:31)
    at Function._load (module.js:276:12)
    at require (module.js:327:19)
    at Object. (/var/www/node/StreamTwitter.js:16:12)
[root@dti-vps-srv34 node]# 

インストールしたけど、なんかエラーがでてできなかった。
「cxx: base64.cc -> build/default/base64_1.o」
base64はID,PASSのencodeだけだから適当なツールで変換してソースにべた書きしました。

なんで失敗するのかと思って、npmのフォルダに無理やり上手くいったOSのファイル持ってきたり
いろいろしたけど、当然だめでした。
でも、ある20日に再チャレンジしたらなんか上手くいった。

[root@dti-vps-srv34 node]# npm install base64
npm info it worked if it ends with ok
npm info using npm@0.3.15
npm info using node@v0.4.2
npm info fetch http://registry.npmjs.org/base64/-/base64-2.0.3.tgz
npm info calculating sha1 /usr/local/lib/node/.npm/.cache/base64/2.0.3/package.tgz
npm info shasum d58b08f621b4be0c6b768258821d4d5f402c35b8
npm info preinstall base64@2.0.3
npm info install base64@2.0.3
Checking for program g++ or c++          : /usr/bin/g++ 
Checking for program cpp                 : /usr/bin/cpp 
Checking for program ar                  : /usr/bin/ar 
Checking for program ranlib              : /usr/bin/ranlib 
Checking for g++                         : ok  
Checking for node path                   : not found 
Checking for node prefix                 : ok /usr/local 
'configure' finished successfully (0.391s)
Waf: Entering directory `/usr/local/lib/node/.npm/base64/2.0.3/package/build'
[1/2] cxx: base64.cc -> build/default/base64_1.o
[2/2] cxx_link: build/default/base64_1.o -> build/default/base64.node
Waf: Leaving directory `/usr/local/lib/node/.npm/base64/2.0.3/package/build'
'build' finished successfully (1.856s)
npm info postinstall base64@2.0.3
npm info preactivate base64@2.0.3
npm info activate base64@2.0.3
npm info postactivate base64@2.0.3
npm info build Success: base64@2.0.3
npm ok
[root@dti-vps-srv34 node]# 

最後にこれも調べ途中だけど、node.jsのWebsocketって何種類かあるっぽい

これをつかったけど、VMwere上のCentOS5.5はエラーは出ないけど
websocketのコネクションははれなかった。

[root@dti-vps-srv34 node]#npm install websocket-server

他の環境はこっちを試したら上手くいてる。

[root@dti-vps-srv34 node]#npm install http://github.com/miksago/node-websocket-server/tarball/master 

まだまだ、日本語の情報も少ない。

node.jsでTwitterのタイムライン取得

Twitterのパブリックタイムラインをプッシュでリアルタイムに情報を受け取ることのできるAPI

http://dev.twitter.com/pages/streaming_api

よくサンプルで接続している先
http://stream.twitter.com/1/statuses/sample.json

さらに、statuses/filterを使用すると指定したキーワードのパブリックステータスだけを送ってもらうことができます。

trackパラメータ
キーワード指定はtrackパラメータ
試した感じ、大文字と小文字を区別しませんでした。
キーワードは「,」で区切れば、複数のOR検索が出来ます。

followパラメータ
ユーザーを指定はfollowパラメータ
ユーザーのIDを指定します。
ユーザーか、指定ユーザのReTweetかが対象となります。
キーワードは「,」で区切れば、複数のOR検索が出来ます。

locationsパラメータ
場所の指定を行う場合は、
locations=南西,北東を経度緯度で指定して
特定の地域を選択できます。

var	sys		=	require('sys');
var	http	=	require('./http-basic-auth');

var	account	=	{username: '****',password: '****'};
var	client	=	http.createClient(80, 'stream.twitter.com', false, false, account);
var	request	=	client.request('GET', '/1/statuses/filter.json?locations=122.87,24.84,153.01,46.80', {'host': 'stream.twitter.com'});

request.on('response', function (response) {
	response.on('data', function(chunk) {
		try {
			var json =JSON.parse(chunk);
			var name = json['user']['name'];
			var text = json['text'];
			var profile_background_image_url = json['user']['profile_background_image_url'];
			var created_at = json['created_at'];
			var coordinates = json['coordinates']['coordinates'];
			var geo = json['geo']['coordinates'];
			console.log("-------------------------------------------");
			console.log("name: " + name);
			console.log("text: " + text);
			console.log("created_at: " + new Date( created_at + new Date().getTimezoneOffset() ) );
			console.log("profile_background_image_url: " + profile_background_image_url );
			console.log("coordinates: " + coordinates );
			console.log("geo[ido]/[keid]: " + geo );
			}
		} catch (e) {
// 				console.log(e);
		}
	});
});

request.end();

node.jsをCentOSにインストールしてみた。

最近話題のnode.jsをインストールしてみた。

サーバを準備すのも面倒なので、VirtualBoxにVMとしてCentOSを準備。

準備が出来たら、こんな感じで

 
# su - 
# cd /usr/local/src 
# wget http://nodejs.org/dist/node-v0.4.2.tar.gz 
# tar xzvf node-v0.4.2.tar.gz 
# cd node-v0.4.2 
# ./configure 
処理が流れる 
#make install 

インストールが終わった。下記バージョンがでればOK。
ちなみにネットで「gcc-c++」「openssl-devel」とかが入っていなくて失敗という記事をよく見たので
あらかじめインストールでdevelなんちゃらは入れておきました。

 
#node -v 
v0.4.2 

つぎに「npm」をインストールする。
npmはPHPのPEAR的なもので、いろんなモジュール(フレームワークも)簡単にインストールできる。
インストールは簡単

 
#curl http://npmjs.org/install.sh | sh 

これでインストールは終わったので、フレームワークとかを一通りインストール
やりたいことのが
・DB連携させてみたい。
・websocketを試したい。
だったので、これらを入れてみた。

 
#npm install express
#npm install mysql 
#npm install socket.io 
#npm install http://github.com/miksago/node-websocket-server/tarball/master 
#npm install http-basic-auth 

http-basic-authはJSONの解析エラーがでたので
http://registry.npmjs.org/http-basic-auth/-/http-basic-auth-0.1.0.tgz
を落としてきて、ファイルを直でおいて使ってます。

 
var http = require('./http-basic-auth'); 

とりあえず環境はそろったので、これからソースを準備。