2012年04月10日
javascriptでのCSV取り扱い[ Tips ]
javascriptでCSVを扱うと書いていますが、実際はjavascript encodeで、windows上でバッチファイル形式で走らせる時に遭遇したものです。
昔はDOS(windows)上で簡単なプログラムを走らせようとするとbatファイルを作るくらいしか無かった様に思います。
しかし現在は、JSE(javascript encode)や、WSH(Windows Script Host)、VBS(VBScript)等、簡単にプログラムを作ることができます。
中でもJSEはjavascriptそのものと言っていいので、使えるようにしておくと便利だと思います。
JSEを使い始めると、ファイルの取り扱い、CSVファイルを読み込んだり/書き込んだりして、各種のデータにアクセスすることが多くなると思います。
するとまず、CSVを読み込まなければならなくなりますが…
Googleで、javascript csvファイル読み込み等で検索すると、ファイルを読み込んで「,」(カンマ)で分割する方法が出てきます。
しかし、よくよくみてみるとここで出てくる「,」分割は、数値として「1,234」という様な数字があった場合、1と234に分割されてしまうコーディングだったりします。
正確には「"1,234"」の様にダブルコーテーションで括られているものは文字列として扱い、その中の「,」は分割しないという考慮をしなければなりません。
きちんとCSVの仕様を考慮してデータを読み込んでいる例を紹介しているサイトがすぐに見つかりません。
Google先生のクオリティの低下はやはり著しいのでしょうか。
さて、きちんとCSVの仕様を考慮しているサイトを紹介しておきましょう(すべてのサイトを検証した訳ではありませんので、動かない例もあるかもしれません。念のため)
Ask Ben: Parsing CSV Strings With Javascript Exec() Regular Expression Command
PASTEBIN - parseCSV
function parseCSV(s,sep) {
// http://stackoverflow.com/questions/1155678/javascript-string-newline-character
var universalNewline = /\r\n|\r|\n/g;
var a = s.split(universalNewline);
for(var i in a){
for (var f = a[i].split(sep = sep || ","), x = f.length - 1, tl; x >= 0; x--) {
if (f[x].replace(/"\s+$/, '"').charAt(f[x].length - 1) == '"') {
if ((tl = f[x].replace(/^\s+"/, '"')).length > 1 && tl.charAt(0) == '"') {
f[x] = f[x].replace(/^\s*"|"\s*$/g, '').replace(/""/g, '"');
} else if (x) {
f.splice(x - 1, 2, [f[x - 1], f[x]].join(sep));
} else f = f.shift().split(sep).concat(f);
} else f[x].replace(/""/g, '"');
} a[i] = f;
}
return a
}
LiosK-free Blog - 続・正規表現を使ったCSVパーサ
function parseCSV2(text, delim) {
if (!delim) delim = ',';
var tokenizer = new RegExp(delim + '|\r?\n|[^' + delim + '"\r\n][^' + delim + '\r\n]*|"(?:[^"]|"")*"', 'g');
var record = 0, field = 0, data = [['']], qq = /""/g;
text.replace(/\r?\n$/, '').replace(tokenizer, function(token) {
switch (token) {
case delim:
data[record][++field] = '';
break;
case '\n': case '\r\n':
data[++record] = [''];
field = 0;
break;
default:
data[record][field] = (token.charAt(0) != '"') ? token : token.slice(1, -1).replace(qq, '"');
}
});
return data;
}
杉並WEB研究所(工事中) - JavaScriptでExcelのCSVを読み込む実験(カンマ対応版)
//CSVデータの配列化とHTML化処理
function parseText(str){
var resultText="<table border=1>";
//改行コードを変数にし分割処理をする
var CR = String.fromCharCode(13);
var LF = String.fromCharCode(10);
lineData = str.split(CR);
var ldLength = lineData.length;
var wqRank = new Array( ldLength);
for (var h=0; h<lineData.length; h++){
var wqCount = 0; var etcCount = 0;
var wqAnalyse = 0; var etcAnalyse = 0;
var wqArray = new Array();
//各行のダブルクォートの個数カウント
for (s=0; s<lineData[h].length; s++){
wqAnalyse = wqCount;
etcAnalyse = etcCount;
if (lineData[h].charAt(s) == "\""){
//ダブルクォートのカウントを1つ増やす
wqCount++;
} else {
//その他文字列のカウントを1つ増やす
etcCount++;
}
//連続してあるクォートを認識するための初期化処理
if ((wqAnalyse != wqCount) && (etcAnalyse == etcCount)){
etcCount = 0;
}
if ((wqAnalyse == wqCount) && (etcAnalyse != etcCount)){
wqCount = 0;
}
wqArray[s] = wqCount;
}
//Math.max関数でダブルクォートの最大値解析
wqRank[h] = Math.max.apply(Math,wqArray);
wqMaxCount = Math.max.apply(Math,wqRank);
//置換用にRegExpインスタンスを作成
for (l=1; l<=wqMaxCount; l++){
eval( "var repWq"+l+"= new RegExp('\"{"+l+",}','g');");
}
repWqRe = new RegExp('変換ダブルクォート',"g");
repCm = new RegExp('\,',"g");
repCmRe = new RegExp('変換カンマ',"g");
}
for (var i=0; i<lineData.length; i++){
//ダブルクォートの個数に応じて置換
matStr = lineData[i].replace(eval("repWq"+wqMaxCount),'\"');
for (var n=wqRank.length-1; n>=2; n--){
if (n != 2){
matStr = matStr.replace(eval("repWq"+wqRank[n]),'\"');
} else {
matStr = matStr.replace(repWq2,'変換ダブルクォート');
}
}
//正規表現で半角ダブルクォートに囲まれる文字列を取得
matStr = matStr.match(/"(\\["ntr\\]|[^"])*"|[^,]+/g);
//各文字列に含まれるカンマを「変換カンマ」に一時置換、その後半角ダブルクォートを取り除き、再びカンマで結合
for (var h=0; h<matStr.length; h++){
matStr[h] = matStr[h].replace(repCm,"変換カンマ");
matStr[h] = matStr[h].replace(repWq1,"");
lineData[i] = matStr.join();
}
//カンマで分割
strText = lineData[i].split(",");
resultText += "<tr>";
//HTMLのtableを繰り返し処理で作成し、セルにデータを入れていく
for (var j=0; j<strText.length; j++){
//変換カンマと変換ダブルクォートを元の半角に戻す
strText[j] = strText[j].replace(repCmRe,"\,");
strText[j] = strText[j].replace(repWqRe,"\"");
if (i < ldLength){
if(j == 0){
resultText += "<td>"+strText[j]+"</td>";
}
if(j == 1){
if (i == 0){
resultText += "<td>";
}else{
resultText += "<td><a href="+strText[j]+">";
}
}
if(j == 2){
if (i == 0){
resultText += strText[j]+"</td>";
}else{
resultText += strText[j] +"</a></td>";
}
}
}
}
resultText += "</tr>";
}
resultText += "</table>";
return resultText;
}
なお、私は行数も短い三番目の例を採用しました。