意外にJavaScriptにおける文字コード変換の情報は少ないです。そもそも、SJISやらEUCの文字をどうやってJavaScriptに与えるのかという問題があって、できるとしても使い道がないということが理由にあげられるかと思います。
しかし、XMLHttpRequest(XHR)でデータを取り込んだときに、それが必要となる状況が発生することがありました。具体的には次のような条件で文字列を正しく読んでくれないという状況でした。
こういうサイトは結構多くて、ほとんどの場合はHTMLのMETAタグで文字コード指定をしているため、ブラウザで見る分にはその文字コードが使われて正しく解釈されるので問題となっていません。ところが、XHRで同じコンテンツを取り込んだ場合は、画面上で見る場合と違ってコンテンツの中身を見てMETAタグどうこうなんて判断をしてくれません。そもそも取りこまれたデータはHTMLだと思ってないわけですから。
かなり特殊な状況ではありますが、上手く読めないのはくやしいので、JavaScriptの文字コード変換を実装してみました。ほとんどがコード変換表で、巨大で申し訳ないです。実際にこの変換を行うためには
xmlhttp.overrideMimeType("text/plain; charset=x-user-defined");
xmlhttp.open("GET", url, false);
xmlhttp.send(null);
:
というふうにoverrideMimeType()を呼んでからデータを取り込まねばならない1)のですが、IEではこの関数はサポートしていないようなので、結局のところあまり実用的でもありませんでした。
そして、適切な文字コードを知るためにコンテンツの中身からMETAタグを探すのなら、そこで指定されているContent-Typeをこの関数に指定してからもう一度GETすればブラウザ側でちゃんと読み込んでくれるじゃないか、ということに気がつきました。やっぱり使い道がないですね。
String.prototype.convert = function(charset) {
if (charset.match(/(sjis|Shift_JIS)/i)) {
return this.replace(/[\u0100-\uffff]/g,
function(c) {
//全角文字の第1バイトは無意味になるので取り除く。
return String.fromCharCode(c.charCodeAt(0)&0xff);
}
).replace(/(([\x20-\x7E\xA1-\xDF])|(([\x81-\x9F\xE0-\xEF])([\x40-\x7E\x80-\xFC])))/g,
function(whole, b1, b2, b3, b4, b5) {
if (b2) {
//半角カナ
var c = String.__map_jisx0201[b2.charCodeAt(0)&0xff];
return c ? String.fromCharCode(c) : b2;
} else if (b3) {
//全角
var j1 = s1 = b4.charCodeAt(0) & 0xff;
var j2 = s2 = b5.charCodeAt(0) & 0xff;
if (j1 <= 0x9f) j1 -= 0x71;
else j1 -= 0xb1;
j1 = j1 * 2 + 1;
if (j2 >= 0x7f) j2 -= 1;
if (j2 >= 0x9e) j2 -= 0x7d, j1++;
else j2 -= 0x1f;
var c = String.__map_jisx0208[(j1<<8) + (j2&0xff)];
return c ? String.fromCharCode(c) : b3;
}
}
);
}
if (charset.match(/euc-jp/i)) {
return this.replace(/[\u0100-\uffff]/g,
function(c) {
//全角文字は第1バイトは無意味になるので取り除く。
return String.fromCharCode(c.charCodeAt(0)&0xff);
}
).replace(/(([\x8E]([\xA1-\xDF]))|(([\xA1-\xFE])([\xA1-\xFE])))/g,
function(whole, b1, b2, b3, b4, b5, b6) {
if (b3) {
//半角カナ
var c = String.__map_jisx0201[b3.charCodeAt(0)&0xff];
return c ? String.fromCharCode(c) : b2;
} else if (b4) {
//全角
var j1 = b5.charCodeAt(0) & 0x7f;
var j2 = b6.charCodeAt(0) & 0x7f;
var c = String.__map_jisx0208[(j1<<8) + (j2&0xff)];
return c ? String.fromCharCode(c) : b4;
}
}
);
}
return this.toString();
};
String.__map_jisx0201 = {
//JISX0201->Unicode
32:32,33:33,34:34,35:35,36:36,37:37,38:38,39:39,40:40,41:41,42:42,43:43,
44:44,45:45,46:46,47:47,48:48,49:49,50:50,51:51,52:52,53:53,54:54,55:55,56:56,
57:57,58:58,59:59,60:60,61:61,62:62,63:63,64:64,65:65,66:66,67:67,68:68,69:69,
70:70,71:71,72:72,73:73,74:74,75:75,76:76,77:77,78:78,79:79,80:80,81:81,82:82,
83:83,84:84,85:85,86:86,87:87,88:88,89:89,90:90,91:91,92:165,93:93,94:94,
95:95,96:96,97:97,98:98,99:99,100:100,101:101,102:102,103:103,104:104,105:105,
106:106,107:107,108:108,109:109,110:110,111:111,112:112,113:113,114:114,115:115,
116:116,117:117,118:118,119:119,120:120,121:121,122:122,123:123,124:124,125:125,
126:8254,161:65377,162:65378,163:65379,164:65380,165:65381,166:65382,167:65383,
168:65384,169:65385,170:65386,171:65387,172:65388,173:65389,174:65390,175:65391,
176:65392,177:65393,178:65394,179:65395,180:65396,181:65397,182:65398,183:65399,
184:65400,185:65401,186:65402,187:65403,188:65404,189:65405,190:65406,191:65407,
192:65408,193:65409,194:65410,195:65411,196:65412,197:65413,198:65414,199:65415,
200:65416,201:65417,202:65418,203:65419,204:65420,205:65421,206:65422,207:65423,
208:65424,209:65425,210:65426,211:65427,212:65428,213:65429,214:65430,215:65431,
216:65432,217:65433,218:65434,219:65435,220:65436,221:65437,222:65438,223:65439
};
String.__map_jisx0208 = {
//JISX0208->Unicode
8481:12288,8482:12289,8483:12290,8484:65292,8485:65294,8486:12539,8487:65306,
8488:65307,8489:65311,8490:65281,8491:12443,8492:12444,8493:180,8494:65344,
:
(長いので以下略)
};