non-ASCII 文字を JSON にエンコードすると1文字6バイトになってデータ量多いよねっていうトークをした時、「じゃあエンコ前に全部 ASCII にすればいいんじゃね?」と考えて調べたら Modified UTF-7 という手頃な文字コードがあったのでエンコーダー作ってみました。
探せば既に誰かやってそうだと思ってましたがざっとググったところ UTF-7 専門エンコーダーが見当たらなかったので自作。
(一番肝心なところが最後の数行にしかないのはあまり突っ込まないでください)
ソース
/** ビットの FIFO */
var BitQueue = function() {};
BitQueue.prototype.value_ = 0;
BitQueue.prototype.length_ = 0;
BitQueue.prototype.getLength = function() { return this.length_; }
BitQueue.prototype.enqueue = function(length, value) {
this.value_ =
(this.value_ << length) |
(((1 << length) - 1) & value);
this.length_ += length;
};
BitQueue.prototype.dequeue = function(length) {
var result;
if(length > 0) {
this.length_ -= length;
result =
(this.value_ >> this.length_) &
((1 << length) - 1);
this.value_ &= (1 << this.length_) - 1;
}
else {
result = this.value_;
this.value_ = this.length_ = 0;
}
return result;
};
/** Base64<=>文字列 のエンコーダー */
var Base64 = {
'CODE_CHAR':
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
'PADDING': '=',
'encode': function(str) {
var bitQueue = new BitQueue();
var result = '';
for(var i = 0; i < str.length; ++i) {
// UTF-16 1文字をエンキュー
bitQueue.enqueue(16, str.charCodeAt(i));
// 6bit単位でデキュー
while(bitQueue.getLength() >= 6) {
var ch = bitQueue.dequeue(6);
result += Base64.CODE_CHAR.charAt(ch);
}
}
// 余りには0を詰めて結果に入れる
if(bitQueue.getLength()) {
var remain = bitQueue.getLength();
var ch = bitQueue.dequeue(remain) << (6 - remain);
result += Base64.CODE_CHAR.charAt(ch);
}
// 結果が4n文字になるよう詰め物を入れる
while(result.length % 4)
result += Base64.PADDING;
return result;
},
'decode': function(str) {
var bitQueue = new BitQueue();
var result = '';
for(i = 0; i < str.length; ++i) {
var ch = str.charAt(i);
// パディングに当たったらやめちゃう
if(ch === Base64.PADDING)
break;
// 文字をコードに変えてエンキュー
bitQueue.enqueue(6, Base64.CODE_CHAR.indexOf(ch));
// 16bitたまったらデキュー
while(bitQueue.getLength() >= 16) {
var ch = bitQueue.dequeue(16);
result += String.fromCharCode(ch);
}
}
// エンコの時とは異なり余りは無視しちゃう
return result;
}
};
/** ModifiedUTF-7<=>文字列 のエンコーダー */
var MUTF7 = {
'encode': function(plain_str) {
return plain_str
.replace(/&/g, '&-')
.replace(/[\0- \u007f-\uffff]+/g, function(str) {
return '&' + Base64.encode(str) + '-';
});
},
'decode': function(utf7_str) {
return utf7_str
.replace(/&([A-Za-z0-9\+\/]+=*)-/g, function(str, $1) {
return Base64.decode($1);
}).replace(/&-/g, '&');
}
};
使い方
MUTF7.encode(str)
でエンコ、
MUTF7.decode(str)
でデコ。
結果は関数の戻り値になります。
Base64 とか BitQueue とかで既にいい感じのライブラリを導入済みであれば適当に書き換えてください。
参考
- Wikipedia (2015-11-05 閲覧) https://ja.wikipedia.org/wiki/UTF-7
- RFC2060 https://tools.ietf.org/html/rfc2060