変奏現実

パソコンやMMORPGのことなどを思いつくまま・・・記載されている会社名・製品名・システム名などは、各社の商標、または登録商標です。

この画面は、簡易表示です

javascript

[JavaScript]finallyが壊れている様な気がする

イミフなスタックオーバーフローに悩まされた。

static dataDumpNest = [];
static DATADUMPNEXT_MAX = 100;
static dataDump(objectName, obj, pOptions) {
  try {
    ・・・
    Helper.dataDumpNest.push(obj);
    rc = true;
    ・・・
    return true;
    ・・・
    //  再帰呼び出し時はtrueを返す
    if (Helper.dataDumpNest.length > 1) {
      return true;
    }
    //  それ以外は result を結合し返す
    return Helper.result.join("\n");
  } catch (e) {
    throw e;
  } finally {
    Helper.dataDumpNest.pop();
  }
}

finallyで無条件でpopしているので、

重複チェックで弾かれてpushしてない時に

無駄にjoinしてしまうのが気になり

執拗にF10でトレースしていると・・・

finallyブロックの終わりで呼び出し元のメソッドに戻るべきところが、

  1. ソース画面が何行か戻ってしまう。
  2. スタックトレースが妙に長くなっていた。

という摩訶不可解な現象にぶち当たった。

どうやら、return文無しで、finallyブロックの末尾にぶち当たると、ヤバイ事が起きている。

1万個の配列を何度もjoinさせ、しかも結果は捨てているのでブチ切れさせてしまったかもね。(大笑

普通、ドコかのreturn文の後に処理するハズのfinally句なのに。

延々と待たされた挙句にスタックオーバーフローになるソースを

finally句でtryの外の変数rcをreturnする様にしてみた。

static dataDumpNest = [];
static DATADUMPNEXT_MAX = 100;
static dataDump(objectName, obj, pOptions) {
  let rc = false;
  try {
    ・・・
    Helper.dataDumpNest.push(obj);
    rc = true;
    ・・・
    rc = true;
    return rc;
    ・・・
    //  再帰呼び出し時はtrueを返す
    if (Helper.dataDumpNest.length > 1) {
      rc = true;
      return rc;
    }
    //  それ以外は result を結合し返す
    rc = Helper.result.join("\n");
    return rc;
  } catch (e) {
    throw e;
  } finally {
  if (rc != false) {
    Helper.dataDumpNest.pop();
  }
    return rc;
  }
}

と変えたら、即終わったった

setTimeoutでワーカースレッドっぽい状態なんで、

finallyが、Promiseと勘違いしてスタックを遡ってみると配列がいっぱい積んであるので

Array[ Array[1], Array[2] ] あたりが、

new Promise((resolve, reject) {
・・・
}

に、そっくりだったのかもしれない。

しかし、違うと気が付いて、throw new Error(“Don’t mind.”);

して、普通のfinally になるつもりが、ソコのfinallyでまた躓いて、無限ループしてそう。

でも、そのうち治るでしょ?

やっと、本題の文法解析に戻れそう。

ん?三国英雄たちの夜明けがフリーズしやすいのは

もしやコレなのか?



[javascript]パーサ・コンビネータって

Java パーサコンビネータ 超入門というのを見つけた。

使い方を説明してくれるのでとても助かるけど、

JavaScriptでパーサコンビネータのコンセプトを理解する(「正規表現だけに頼ってはいけない」の続き)

の方がパーサ・コンビネータの仕組みを理解でき、且つ、使わなくなったけどJavascriptの便利な手法を思い出させてくれた。

function 関数(...) {
  return [1,2,3,4, ...];
}

と配列等で容易に複数の値を変えすことができるし、そんな関数を

let [a, b, c, d, ...] = 関数(...)

と呼び出すと、すんなり配列中の a, b, c, d, … に値を入れてくれるトコが地味に嬉しい。

いつのまにかいっぱい返す事になる未来が待っていそうな気がするから new classを返すことが多いけど。

また

function token(str) {
  var len = str.length;
  return function(target, position) {
    if (target.substr(position, len) === str) {
      return [true, str, position + len];
    } else {
      return [false, null, position];
    }
  };
}

の様にreturn function {…} することで、関数ジェネレータを簡易に記述できるのは便利、

但し、呼び出す側が、

token('foobar')('foobar', 0); // => [true, 'foobar', 6]を返す

な感じになってしまうとワークスペース内のファイルの関数を全部チェックする開発環境ではなく、

sakuraエディタで チョコ チョコ叩いている場合は、【見た目文法エラー!】なのが難点。

やはり、

const func = token('foobar');
func('foobar', 0); // => [true, 'foobar', 6]を返す

と書いて関数が戻ってくる雰囲気を醸し出した方が無難な気がする。

many(token('hoge'))('hogehoge', 0); // => [true, ['hoge', 'hoge'], 8] を返す

も、token(‘hoge’)部分がmanyな場合は、あーもーな感じになりそうなんで

let parsers = [];
parsers.push(token('hoge1'));
...
parsers.push(token('hogeN'));
let text = 'hoge1hoge2...hogeN';
let myParser =seq(parsers);
myParser(text, 0); // => [true, ['hoge1', ['hoge2', ...['hogeN']...]]], 8] を返す

と1行がいっぱい膨れ上がってしまいそう。

てか、実際に’hoge1hoge2’と直書きすることは滅多に無いから

もっと、もっと膨れ上がる。

先の記事のソースは「あくまで説明しやすく表記」したものなのは明白だけど、

テストコードってこんな風に書いてたりするよね?。(笑

ps.

先の「理解する」のページのコードをまとめてみた。

簡易のテストケースもパーサーの種類ごとに1つダケ書いてみたら、結構長くなってしまった。

作りかけだからFalseしまくってるのはいいとして

Errorオブジェクトから発生原因を判りやすくしてほしいなぁ。

字句解析とか構文解析は、「,」1つの修正だけでも

見えないところがトンデモない結果になり果ててしまいがちなので、テストはガチでやった方がいいからね。

ソースの説明

  • index.html   テスト画面
  • parcom.js パーサの記事からコードをコピペしてチョコっと加工したもの
  • sample.js パーサのテストパターンを起動し、結果を表示するスクリプト
  • testparcom.js  パーサのテストパターン
  • testbase.js    テストパターンを多少楽にするための testparcom.js の基底クラス
  • libs.js オブジェクトのダンプやErrorオブジェクトから情報を引き出したりするD級パーツ群
  • sample.css テスト画面用CSS

※ん-。まだまだな感じがする。

そもそも、各パーサはサブクラス化した方が良いんじゃにのかな?

更にパーサ コンテナ クラスで括った方が・・・。

アカン。どんどん膨れ上がる一方だ。

ps.2021/12/3

BNFっぽい文法”Call = [ Call ] procedureName [ (argumentList) ] ”と書かれたテキストを

解析するコードを作成中。やっと単純な文法をクリアしたものの、2つ目の文法に進まない。

デバッグでハマりすぎ長いログをスクロールするのが大変。

  • 機能を大幅改善
    • ログ末尾のstartとendを検出しリストタグ(UL,OL,LIとか)で手軽にインデント
      • 以前見つけたTreeViewのコードは流用できた
      • FIXTED:cssは全部見直しするハメになった
      • TODO:try…catch…filannyで確実にendを出力させないと右雪崩が発生する
    • TreeView風に[+][ーで折りたためる
      • TODO: +、ー小さすぎた!
      • TODO: endの行に+を配置するのは良くない。
    • trueやfalseを色分け
      • FIXED: あるあるな後付けデグレードと既存バグ発覚
        • objDumpで処理済みのテキストを再度objDumpしてが気が付かなかった
        • replaceで単語にタグ付けする場合は二度漬け禁止のハズが・・・
          • <span> ⇒ &lt;span> 的な出来事
    • FOXED:ログが雪だまり式に増えていた
      • Case2にCase1のログが
      • Case3にCase2とCase1のログが
      • 以下、増殖
        • 各テストCaseでログクリアする
    • TODO:typeof xxx === “undefined”はしてるけど、xxx === null チェックが甘い

コードの見直しが多すぎて、メソッド単体テストが壊滅状態。

修正してみたら、半壊状態。←いまこのへん。

全文法(287行中7行)をクリアするのはまだまだ先の話。

本当に終わりが見えないなぁ(大笑

            //      syntax = [ rule ]  ;
            setSyntax("syntax", many(refSyntax("defsymbol")));
            // defsymbol: { "\n" } IDENTIFIER "=" rule
            //###setSyntax("defsymbol", seq(optEol,  identifire,  asign,  refSyntax("rule")));
            setSyntax("defsymbol", seq(optEolWs, identifire, asign, refSyntax("rule")));
            // rule: list { "|" list }
            setSyntax("rule", seq(refSyntax("list"), repeat(seq(vl, refSyntax("list")))));
            // list: { seq | block | repeat | option }  "\n" { notes }
            // TODO: repeat(...)の中にoption系が挟まると無限ループするので、何か対策案を考える
            setSyntax("list", seq(repeat(choice(refSyntax("seqList"), refSyntax("block"), refSyntax("repeat"), refSyntax("option"))), eol, opt(refSyntax("notes"))));
            // seqList = seq { seqList }
            setSyntax("seqList", seq(refSyntax("seq"), repeat(seq(/*ws,*/ refSyntax("seqList")))));
            // seq = (regExp | IDENTIFIER | STRING | INTEGER | , )
            setSyntax("seq", choice(reExFmt, identifire, string, number, comma));
            // block: "(" rule ")"
            setSyntax("block", seq(lpr, refSyntax("rule"), rpr));
            // repeat: "{" rule "}"
            setSyntax("repeat", seq(lbr, refSyntax("rule"), rbr));
            // option: "[" rule "]"
            setSyntax("option", seq(lbk, refSyntax("rule"), rbk));
            // notes: "#Notes" "Syntax" eol rule "}}" "#Notes" "End" eol
            setSyntax("notes", seq(notes, ws, syntax, eol, refSyntax("syntax"), notes, ws, end, eol));

見直すのはコレだけなのに。

  • ログの大半がホワイトスペースだから何とかしたい
    • パーサから空白を読み飛ばす字句解析結果のを参照する?
    • パーサで作成したtoken(文字と正規表現)を適切な順に並べる
      • 勝手に空白を読み飛ばすので楽なハズ

クリアしたら、

  • 残りのテストを・・・
  • パーサコンテナを作る
  • 各パーサをサブクラス化
  • コードを読む部分を作る
  • 何かの言語に変換する部分を作る

先はまだまだ長い。

ps.2021/12/6

サブクラス化したもののソースは1000行を越えた。何か間違えている様だ。

サブクラス化 の過程で

  • リテラルにはプロパティが付けられない
    • String(“hohegoge”)ではダメ。
    • new String(“hohegoge”) でOK.
    • その辺は throw Error(“hogehoge”);も同じなんだろう。
  • RegExpにgオプションを付けlastIndexプロパティに位置を指定しても文字列の最初から検索する
    • 一度素振り(検索)させ自力でlastIndexを初期化しないとダメ
    • gは空振り(null)すると、とても機嫌が悪い。
  • RegExpのinputプロパティにオリジナルの文字列のコピーが作られる
    • メモリが心配
  • Refer.call を使ってみた
    • 横に長くなる
      • 伸びた部分だけ見れば良いので見やすいと思う人もいるかも
      • 元に戻す
  • super.メソッドみたいな呼び方ができる
    • なんちゃってオーバーライド風に書いてみると、無限ループする

18行1列まで読めた。 行末の _ 結合演算子がムズイ。

ps.2021/12/7

事前にザックリと空白、改行、その他でトークン化したついでにコメント文を空白化。後は _ かな。

全文からワード単位で字句解析するようになってブラウザの動作は軽くなった気がする。

ワード単位で取り出す時に、文末が” _”なら、次の改行を無視すればいいのかな?

ps.2021/12/8

手直しをしたら余計悪化。

0回ループOKなRepeatがネストしたら無限ループしてしまうので、Sequenceで評価がTrueでもpositionが進まない時は、評価をFalseにしたけど、まだループ。

仕方が無いので、catch exceptionで強制停止。300万行とか凄い行数になってたので、1000行までに制限。

やっと途中経過が可視化できた。ログのネストを深く掘り下げると

ParserInfo (ParserInfo)で、字句解析まで見れるけど。文法解析まで見れない。

ps.2021/12/9

手直し。 ダンプの仕方を見直し。

もう、本題を忘れてダンプに力が入る。

ps.2021/12/11

手直。 ダンプの仕方を見直し。  TreeView.markingをPromiseってみた。設定できてるけど、処理が終わったと返ってこない。

ps.2021/12/13

関数のダンプが修正漏れでエラっていたを訂正。



[JavaScript]dropイベント

MDNにフォルダをドロップして中身をリストアップするサンプルを見つけた。

FireFoxではうまく動くが、ChromeやEdgeでは・・・・・・・・・

code: 0
message: "A URI supplied to the API was malformed, or the resulting Data URL has exceeded the URL length limitations for Data URLs."
name: "EncodingError"

が出てしまう。

「APIに提供されたURIの形式が正しくないか、結果のデータURLがデータURLのURL長の制限を超えています。」

ってファイル名のせい?

しかし、C:¥aaaでも同じ結果だった。

原因は判らないけど、コードをアップしてみると・・・何故かうまくいく(アップ先)。

どうやら、httpなURLなパスにあるHTMLファイルをブラウザで開いている場合はOKらしい。

微妙だ。どっちかと云えば、

PCにコピったHTMLファイルでウマくいく方が安心なのに

得体のしれないのURLの画面のページに自分のPCのフォルダをドラッグするのは・・・

素で怖いw

と思う。

後、先の参考URLの注釈通りにreadEntries は、100個までしか読んでくれない。

forEachはPromiseを返さないので、非同期処理を抱えてる部分を含めて、

ローカル関数に書き換えたが forEachの最終ループで再起すると

どう見ても無限ループにしか見えない

ここは、forEachをmapにして暗黙の同期化を発動する。

ついでにmapの戻り値から readEntries が空っぽを渡たさなかった場合のみ再起するようにした。

readEntries  も非同期っぽいから、ローカル関数のままか。

フォルダに203個ファイルがあっても全部書き出してくれたから、

多分大丈夫。

メデタシ、メデタシ。(かな?

あ、HTMLのINPUTタグでwebkitdirectory を指定すると

フォルダも選択できるらしい。

こっちの方が良さそう。

でも、非標準らしい。



[JavaScript clipboard]表の行の背景色を

表をTABLEタグ風のテキストをEXCELに貼ると、それっぽい表になる。

でも安易に

TD付の行リスト.push("<td>" + data1 + "</td><td>" + ... + "</td");
・・・
TD付の行リスト.push("<td>" + data1 + "</td><td>" + ... + "</td");
var str="<table><tr>" +TD付の行リスト.join("</tr><tr>") + "</tr></table>");
window.clipboardData.setData("Text" , str);

な感じで作ってしまったら、行の背景色を交互に変えるのがめんどくさくなってしまった。

TD付の行リスト.push("<td>" + data1 + "</td><td>" + ... + "</td");
・・・
TD付の行リスト.push("<td>" + data1 + "</td><td>" + ... + "</td");
var p = "<table><tr ${bgColorRow}>" +TD付の行リスト.join("</tr><tr>") + "</tr></table>";

// 行の背景色(${bgColorRow})を交互に変える
var i=0;
var str= p.replaceAll(/\$\{bgColorRow\}/g, function(pat){
   i++;
   return  (i%2)? '': 'bgcolor="lightgrey"';
}));
window.clipboardData.setData("Text" , str);

でもいいけど、10,000行をJoinした後にReplaceAllは重すぎる様な気がした。



【javascript】Spread in destructuring

沢山の引数をまとめて渡す方法

すぐ思いつく方法としては、
自前の関数を

aaa({ a: 111,b:222, c:333, d:444});

そして、渡して関数の方も

function aaa(json) {
  console.log(json.a);
  ...
  console.log(json.d);
}

とすればいい。
しかし、関数の方は<script src=”http:…”>な感じの場合は書き換え不能だ。

調べてみると

function myFunction(x, y, z) { }
  var args = [0, 1, 2];
  myFunction.apply(null, args);

と書くのが普通らしい。IEでも動くそうだ。※動作未確認。
applyの最初のパラメータはthisで、 普通の関数ではnullになる。
2番目に引数リストを渡す。

今は、スプレッド構文「関数名(変数)な形式」で関数を呼び出す書き方もあった。

aaa(a,b,c,d) な感じの関数のパラメータをまとめて渡すには

var args =[a,b,c,d];

または

var  args = [];
args . push(a);
...
args.push(d);

で、配列に詰め込んで

aaa(...args);

難点を云えば、Edgeは動いたが、IEではダメらしい。



javascript

jQueryを使わなくてもCSSのセレクタの指定方法でDOMオブジェクトを取得できる。

dom-Object = document. querySelector (セレクタ)

セレクタの書式は、
#xxx で id属性
.xxxで class属性
xxxで 要素 (タグ )名
を指定できる
xxx yyy と空白で間を開けると、 xxxの子孫のyyyの意味
xxx>yyy と>で間を開けると、 xxxの直下のyyyの意味
xxx+yyy と+で間を開けると、 xxxの直後のyyyの意味
xxx, yyy とカンマで区切ると、xxxまたはyyyの意味

あと、xxx:yyyの書き方で、xxxの疑似要素または 擬似クラスyyyの意味

疑似要素には、
・要素名:first-line 要素の最初の一行
・要素名:first-letter 要素の最初の一文字
・要素名:before   要素の直前
・要素名:after    要素の直後

疑似クラスには、
・要素名:link 未訪問のリンク
・要素名:visited 訪問済のリンク
・要素名:hover カーソルが乗っている要素
・要素名:active クリック中の要素
・要素名:focus フォーカスされている要素
・要素名:lang 特定の言語を指定した要素
・要素名:first-child 要素内の最初の子要素

となるらしい。

要素.addEventListener( type, listener, option) でイベントを追加できる

type には
・ load Webページの読み込みが完了した時
・ DOMContentLoaded Webページが読み込みが完了した時
・ click マウスボタンをクリックした時
・ mousedown マウスボタンを押している時
・ mouseup マウスボタンを離した 時
・ mousemove マウスカーソルが移動した時
・ keydown キーボードのキーを押した 時
・ keyup キーボードのキーを離した 時
・ keypress キーボードのキーを押している時
・ change フォーム部品の状態が変更された時
・ submmit フォームのsubmitボタンを押した 時
・ scroll 画面がスクロールした時

listenerには function(..) { }で書く

optionには通常はfalseを指定するが、追加した listener を優先して処理する意味。

optionに、capture、once、passiveを指定するには連想リストで指定する。
{ passive: true }



prototype と static

javascriptでクラスっぽいものにしたい場合
メンバーをprototypeにメンバー関数を追加する。

sjis.prototype.xxx = function() {
alert("OK");
};

ところがこの宣言したメンバーをメンバーから参照すると

sjis.prototype.yyy = function() {
xxx();
alert("OK");
};

とundefinedとなってしまうことがある。
通常のfunction 定義は、jsファイルを読み切ってから実行するので参照先は後に書いても問題ないけど、
無名関数の場合は、その場で組み込んでしまうようで、参照するメンバーは先に記述しないとダメらしい。
 
クラス宣言っぽいことはできるけど
private、protected、staticなメンバーは作れないのも痛い。
それっぽいやり方はいくつか見つけたけど
基本は、prototype に入れる方法なのでチョット扱いが大変。
それくらいなら、
var   クラス名_STATIC_メンバー名=null;
と書いた方が楽。



デスクトップに貼ったJSからSJISのCSVをダウンロードさせてみる 改訂2018/10/2

サーバーサイドで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 クラシック版エディタの記事だったので差替え



JavaScriptで壁を感じる時

Hyper Script が JavaScriptになって既に四半世紀が過ぎようとしていた。
大量の document.www.xxx.yyy.zzz の  getter と setter にマミれてたJavaScriptも
良好な document.getElementById(id-name) の発見により暫しの安寿を得た。
が、これを好機と捉えたMSがブラウザーのIE独自仕様変更紛争を仕掛け、これにより世界中のサイトが不意のWindowsUpdateにより、画面が崩壊する事態が発生する。
幾度のW3C勧告も虚しく仕様変更紛争は激化し無料のIE6がマーケットで伸びるなか、ActiveXやOLEによるクリティカルなセキュリティホールが多数出現する事態が続きMSの戦況は大きく傾き、ついには最終防衛ラインVB-Scriptの陥落により、MS独自路線は崩壊することとなる。
それにより、ブラウザごとに実装がバラバラで誰にも全く見向きもされなかったためロクなハッキングがなされなかったJavaScriptが脚光を浴びることになるが、仕様の齟齬ゆえに入力チェック程度の実装ですら行き詰まりとなり、多くのコードはサーバーサイドへと流れていった。
そして時が過ぎ、無名の多数の不屈の精神によって生み出されたprototype.jsやjquery.jsの様な汎仕様的なコードの出現により、JavaScriptのクライアントサイドへのコードの復帰を促す流れが生み出されたものの、それはJavaScriptをWEBのダークサイドに手招く悪魔の仕業であり、再びWEBは泥沼のセキュリティホールへと押し戻されるのであった。
戦況は膠着状態となり・・・今に至る。



javascript で DBM?

FireFoxに入っているSQLiteはデータベースをファイルロックで競合管理しているので、何かと使いにくいので、
ブラウザのLocalStorageをDataBase代わりに使えないかなぁ?と
phpMyAdminでINFORMATION_SCHEMAを見ながら
チョコチョコとJavaScriptに焼直してます。
Select A.fld1, B.fld2 From XXX A Left Outer Join YYY B On A.key = B.key Where A.fldX = xxxxx
とか使えたらいいな。
一通り動いたら、LocalStorage用サーバー・パッケージとJScriptやJDKのツールでNode.jsっぽく動くサーバー・パッケージに分離して、どこでもデータベース化できたらいいな。できれば1つのスキーマを別々のパソコンに分散できたらいいな。
 




top