サーバーサイドでSJISにエンコードすること自体どうにでもなる。
しかし、デスクトップにHTMLとJavaScriptだけ貼ってローカルにあるテキストをCSVファイルとしてダウンロードさせようとすると、
JavaScriptのテキストはUnicode(16Bit)なので、そのまま出力するとEXCELで読むと文字化けする。
JavaScriptでは、SJISをUnicode(16Bit)に変換する機能はあるが、Unicode(16Bit)をSJISにする機能は無い。
JScriptならWindowsのドコかにStrSonvがあれば拾って来ればいいのかもしれない。
今風なら既に【外部のJSファイル(延々と長いコード表が載っている)】があるのだから、これを借りるのが普通。
しかし、今のブラウザは大抵のコードページならUnicode(16Bit)に変換できる能力を持っているのでコレを使って逆変換テーブルを作ってもいい。
SJISの文字の入ったバイナリーなイメージを作り、Readerに読ませ、Unicode(16Bit)に変換させた結果を、Unicode(16Bit)のコードをキーワードにSJISのコードを得られるArrayに詰め込めばいいハズだ。難しいのはUnicode(16Bit)しか使えないJavaScriptの上でどうやってSJISのイメージを作るのかというあたりと、SJISのコード表で有効な範囲だけイメージを作ること。
0x80など未定義になっている部分のイメージが混ざると・・・続くバイトを全角の2バイトと思って変換され文字化けてしまうし、また変換できなかった文字が空白に変換された場合、最後に変換できなかった文字がコード表の「空白」を上書きしてしまい派手な文字化けの原因になる。
それでも、MakeConvert16bitTable や file_reader.readAsText(b,”SJIS”); をゴニョればeuc-jpもできるはずだ。
16ビット領域の文字コード表を作るとループしまくるのでとても遅くなりそうな気がしたけど、今のパソコンならどうということはないらしい。
これでデータベース用のコード変換表もできるハズ。
//Unicode16 - SJIS文字コード表
var utf16Ar=null;
//画面表示直後(onload)に文字コード表を作るMakeSJISConvertの呼び出しが消えないオマジナイ
if( window.addEventListener ) {
window.addEventListener( 'load', MakeSJISConvert, false );
} else if( window.attachEvent ) {
window.attachEvent( 'onload', MakeSJISConvert );
} else {
window.onload = MakeSJISConvert;
}
// Unicode を SJISに変換する
function UTF16toSJIS(utf16) {
try{
//コード表作成時に未定義文字をしていした時のオマジナイ
utf16Ar[" "] = " ";
//コード表作成時に未定義文字をしていした時のオマジナイ
utf16Ar[undefined] = " ";
var a = utf16.split('');
var buf = new Uint8Array(a.length * 2);
var j=0;
for(var i=0; i< a.length; i++) {
var ch = utf16Ar[a[i]];
if(ch === undefined) {
buf[j++] = (0x20);
} else {
var chH = ((ch>>8) & 0x0ff);
var chL = ( ch & 0x0ff);
buf[j++] = chL;
if( chH != 0) {
buf[j++] = chH;
}
}
}
var rc = new Uint8Array(j);
for(var i=0; i<j; i++) {
rc[i] = buf[i];
}
return rc;
} catch(e) {
alert(e);
}
}
//Unicode16 - SJIS文字コード表(utf16Ar)の作成
function MakeSJISConvert() {
MakeConvert8bitTable();
MakeConvert16bitTable();
}
//Unicode16 - SJIS文字コード表(utf16Ar)の16bit文字コード分の作成
function MakeConvert16bitTable() {
//SJISで文字コードに指定している領域のみ作成すること
MakeConvert16bitSubTable(0x0081,0x009f,0x0040,0x007e);
MakeConvert16bitSubTable(0x00e0,0x00ef,0x0040,0x007e);
MakeConvert16bitSubTable(0x0081,0x009f,0x0080,0x00fc);
MakeConvert16bitSubTable(0x00e0,0x00ef,0x0080,0x00fc);
}
//変換処理の呼び出し回数
var callCount=0;
var transferCharCodeCount=0;
var undefined8BitCount=0;
var undefined16BitCount=0;
var throw16BitCount=0;
//Unicode16 - SJIS文字コード表(utf16Ar)の8bit文字コード分の作成
function MakeConvert8bitTable() {
try {
// 8bit area
var buffer1 = new Uint8Array(256);
for(var i=0; i<256; i++) {
if(0x00<=i && i <= 0x1f) {
switch(i) {
case 9 : case 10 : case 13 ://制御コードはCSVで使用するTAB,CR,LFだけ登録
buffer1[i] = i;
break;
default://制御コードはCSVで使用するTAB,CR,LF以外は空白として登録
buffer1[i]=" ";
break;
}
} else if(0x80<=i && i <=0xa0) {//CSVで使わないコードは空白にする
buffer1[i]=" ";
} else if(0xe0<=i && i <=0xff) {//CSVで使わないコードは空白にする
buffer1[i]=" ";
} else {
buffer1[i] = i;
}
}
var b = new Blob( [buffer1], {type: "application/octet-stream"});
var file_reader = new FileReader();
file_reader.onload = function(e) {
var sjisAr = e.target.result;
utf16Ar = new Array();
for(var i=0;i<256;i++) {
var utf16 = sjisAr.substr(i,1);
if(utf16!==undefined){
if( utf16Ar[utf16] != buffer1[i] ) transferCharCodeCount++;
utf16Ar[utf16] = buffer1[i];
} else {
undefined8BitCount++;
}
}
}
file_reader.readAsText(b,"SJIS");
} catch(e) {
alert(e);
}
}
//Unicode16 - SJIS文字コード表(utf16Ar)の8bit文字コード分の作成
function MakeConvert16bitSubTable(hiBegin,hiEnd,lowBegin,lowEnd) {
try {
// 16bit area
callCount++;
var sizeHi = ( hiEnd - hiBegin + 1 );
if(isNaN(sizeHi)) { alert("Nan sizeHi"); }
var sizeLow = ( lowEnd - lowBegin + 1 );
if(isNaN(sizeLow)) { alert("Nan sizeLow"); }
var sizeAr = sizeHi * sizeLow;
if(isNaN(sizeAr)) { alert("Nan sizeHi,sizeHi("+sizeHi+") * sizeLow("+sizeLow+")"); }
var buffer1 = new Uint16Array(sizeAr);
var n1=0;
var tx = "";
for(var hi=hiBegin; hi<=hiEnd; hi++) {
for(var low=lowBegin; low<=lowEnd; low++) {
buffer1[n1++] = (hi & 0x0ff) + ((low <<8) & 0x0ff00);
}
}
var b = new Blob( [buffer1], {type: "application/octet-stream"});
var file_reader = new FileReader();
file_reader.onload = function(e) {
var sjisAr = e.target.result;
var i=0;
for(var hi=hiBegin; hi<=hiEnd; hi++) {
for(var low=lowBegin; low<=lowEnd; low++) {
var sjis = (hi & 0x0ff) + ((low <<8) & 0x0ff00);
var utf16 = sjisAr.substr(i++,1);
if(utf16!==undefined){
try {//Unicode16で扱えない文字かどうかチェック
if(utf16.charCodeAt(0).toString(16)!="3000") {
var test = "【" + utf16 + "】" + utf16.charCodeAt(0).toString(16);
}
if( utf16Ar[utf16] != sjis ) transferCharCodeCount++;//登録した文字数を数えてる
utf16Ar[utf16] = sjis;
} catch(e) {//Unicode16で扱えない文字は無視
alert(e+"sjis="+sjis);
throw16BitCount++;
}
} else {
undefined16BitCount++;
}
}
}
}
file_reader.readAsText(b,"SJIS");
} catch(e) {
alert("MakeConvert16bitTable():"+e);
}
}
CSVをダウンロードするコードは、
//JavaScriptはUnicode(16bit)なので、
//htmlに<input type='button' onclick='exportData("漢字\tあいうえお\tカキクケコ\r\n")'>と書いて呼び出す。
//\tがデリミタとして判定されない場合は , に変える。
function exportData(unicode16CSV) {
var sjis = UTF16toSJIS(unicode16CSV);
var blob = new Blob([sjis], {type: "application/octet-stream"});
var url = URL.createObjectURL(blob);
//ここはHTMLにダウンロード用のaタグを貼っておいて再利用した方がいい。
var a = document.querySelector("#results");
a.href = url;
a.download = "シフトJISコードで作ったCSVファイル.csv";
a.text="re-download";
a.click();
}
日本語のファイル名でダウンロードするとファイル名が文字化けするブラウザもあるだろう
画面でCSVデータを編集してCSVにするサンプル
↑2018/10/2修正済(あるいは改悪済)
〇 FireFox(62.0.2 (64ビット)),Chrome(バージョン: 69.0.3497.100(Official Build) (64 ビット)),IE Ege(バージョン不明)で治ってるように見えた。
× IEではダウンロード不可だった。
ps.2018/1/4
今では文字化けしている。サロゲートペアを考慮していないせいだと思うけど・・・
※サロゲートペア:16ビット固定の文字表現であったUnicodeので未使用でだった0xD800~0xDBFFを上位サロゲート、0xDC00~0xDFFFを下位サロゲートとし、上位サロゲート+下位サロゲートの4バイトで文字を表現する文字を拡張したものがUTF-16らしい。
※2018/10/2 FireFoxで8ビットコードが化けまくっていたので以下修正。
おかしくなっていた原因は、
(1)16ビット文字のマップを作るつもりが、英字が半角文字に変換されていた。⇒マップに登録済みの文字は無視する。
⇒まだ、上位ビットがFF(1111)の文字だけ、うまく変換できない。
「a」(0xff41)は、なぜか成功するけど
「U」(0x0ff35)は「ガ」に、
「1」(0x0ff11)が「・」に
なってしまう」。
(2)nullコードは’\u0000’で判定しないとダメだった。
(3)ダウンロード用に作成したSJIS(バイナリー)データにnullが混じっていた⇒null部分はカット。
var utf16Ar;
function UTF16toSJIS(utf16) {
try{
var i = 0;
var a = utf16.split('');
//alert(a.length);
var rc = new Uint8Array(a.length*2);
var countSP=0;
var ch16=null;
var j=0;
for(var i=0; i< a.length; i++) {
var ch = a[i];
if(utf16Ar[ch.charCodeAt(0)] === undefined)
rc[j++] = (0x20);
else
ch16 = (utf16Ar[ch.charCodeAt(0)]);
if((ch16 >> 8)==0) {
rc[j++] = ch16;
countSP++;
} else {
rc[j++] = ch16 & 0x0ff;
rc[j++] = ch16 >> 8;
}
}
var rc8 = new Uint8Array(j);
for(var i=0; i< j; i++) {
rc8[i] = rc[i];
}
return rc8;
} catch(e) {
alert(e);
}
}
function MakeConvert8bitTable() {
try {
// 8bit area
var buffer1 = new Uint8Array(256);
for(var i=0; i<256; i++) {
if(0x00<=i && i <= 0x1f) {
switch(i) {
case 9 : case 10 : case 13 :
buffer1[i] = i;
break;
default:
buffer1[i]=0;
break;
}
} else if(0x80<=i && i <=0xa0) {
buffer1[i]=0;
} else if(0xe0<=i && i <=0xff) {
buffer1[i]=0;
} else {
buffer1[i] = i;
}
}
// download sjis code list file
if(downloadSjisCodeList) {
var b = new Blob( [buffer1], {type: "application/octet-stream"});
var url = URL.createObjectURL(b);
var a = document.querySelector("#results");
a.href = url;
a.download = "sjis8.txt";
a.text="re-download";
a.click();
}
var b = new Blob( [buffer1], {type: "application/octet-stream"});
var file_reader = new FileReader();
file_reader.onload = function(e) {
var sjisAr = e.target.result;
utf16Ar = new Array();
for(var i=0;i<256;i++) {
var utf16 = sjisAr.substr(i,1);
if( utf16 != '\u0000' && utf16Ar[utf16.charCodeAt(0)] === undefined ){
utf16Ar[utf16.charCodeAt(0)] = buffer1[i];
}
}
}
file_reader.readAsText(b,"SJIS");
} catch(e) {
alert(e);
}
}
var callCount=0;
function MakeConvert16bitSubTable(hiBegin,hiEnd,lowBegin,lowEnd) {
try {
// 16bit area
callCount++;
var sizeHi = ( hiEnd - hiBegin + 1 );
if(isNaN(sizeHi)) { alert("Nan sizeHi"); }
var sizeLow = ( lowEnd - lowBegin + 1 );
if(isNaN(sizeLow)) { alert("Nan sizeLow"); }
var sizeAr = sizeHi * sizeLow;
if(isNaN(sizeAr)) { alert("Nan sizeHi,sizeHi("+sizeHi+") * sizeLow("+sizeLow+")"); }
var buffer1 = new Uint16Array(sizeAr);
var n1=0;
var tx = "";
for(var hi=hiBegin; hi<=hiEnd; hi++) {
for(var low=lowBegin; low<=lowEnd; low++) {
buffer1[n1++] = (hi & 0x0ff) + ((low <<8) & 0x0ff00);
}
}
// download sjis code list file
if(downloadSjisCodeList) {
var b = new Blob( [buffer1], {type: "application/octet-stream"});
var url = URL.createObjectURL(b);
var a = document.querySelector("#results");
a.href = url;
a.download = "sjis16(" + callCount + ").txt";
a.text="re-download";
a.click();
}
var b = new Blob( [buffer1], {type: "application/octet-stream"});
var file_reader = new FileReader();
file_reader.onload = function(e) {
var sjisAr = e.target.result;
var i=0;
var count=0;
for(var hi=hiBegin; hi<=hiEnd; hi++) {
for(var low=lowBegin; low<=lowEnd; low++) {
var sjis = (hi & 0x0ff) + ((low <<8) & 0x0ff00);
var utf16 = sjisAr.substr(i++,1);
try {
if( utf16 != '\u0000' && utf16Ar[utf16.charCodeAt(0) ] === undefined ){
utf16Ar[utf16.charCodeAt(0) ] = sjis;
}
} catch(e) {
// invalidated char
}
}
}
}
file_reader.readAsText(b,"SJIS");
} catch(e) {
alert(e);
}
}
※2023/3/7 クラシック版エディタの記事だったので差替え