C:\Windows\Microsoft.NET\Framework64\v4.0.30319\ にあるのだから、やっぱりどこでも入ってるのかも。今では実行時にコードをあれこれ操作したり作り出したりすることもできるみたいだから、コンパイラのコアもランタイムの一部で、コンパイラはただの UI(ガワ)だったりするだろうか。■データファイルはヘッダ+固定長レコードだった。Shift_JISで読み込むと文字数とバイト数が比例しないし文字コードとバイナリも素直に対応しないので、2バイト単位になるけど Unicodeテキストとして読み込むことでバイナリデータにアクセスできる。こういうときサロゲートペアが2文字扱いになる仕様が重要。ECMAScriptがそうかは知らないけど、よく聞く仕様ではある。■データの型は(桁数を無視すると)3種類だった。Shift_JISの文字列と、Shift_JISの半角数字(※ASCII数字ともいう)と、唯一混ざり込んだバイナリっぽいものが10進1桁を4ビットで表現して最後を 0xC(正符号)でターミネイトした packed decimalだった。これがあるせいで、このバイナリっぽいものがたまたま Shift_JISの先行バイトだったときに、文字数とバイト数の対応や文字コードとバイナリの対応が崩れるせいで、全体を Shift_JISテキストとして読み込んで切り分けることができなかったのだった。■固定長だということや Shift_JISや ASCII文字だということや、16進1桁をそのまま 10進1桁として使用することが、どれもエディタで見てわかることに繋がっていて非常に助かる。可変長だったらヘッダの特定のバイトを読んで長さを確定しないといけないし、テキストデータだからって圧縮されていたら展開しないといけないし、10進3桁(1000通り)を表現するには2進10桁(1024通り)もあれば十分だぜなんて効率を考え出したらバイナリエディタで開いただけで数字が読めたりはしなくなるので。■■■意外に難しい。「4nビットを用いて整数を表現するとき,符号なし固定小数点表示法で表現できる最大値をaとし,BCD(2進化10進符号)で表現できる最大値をbとする。nが大きくなるとa/bはどれに近づくか。」さっきも書いたけど、BCDって言われて10ビットで10進3桁を表現することを考え出すと答えから遠ざかってしまう。■これをダブルクリックしたら入力済みのフォームが表示されるからメニューから印刷したらいいよと一人に説明したら、パソコンに手入力したものを印刷してたのだと思ってたとのこと。そうね、パソコンって使いにくいワープロかもしれないね。最終更新: 2018-06-09T23:02+0900
用途はタイトル欄(↑)を参照のこと。JavaScriptのビット演算は事前に整数化が必要なため遅いらしいですよ。
あと、これは JavaScriptではなく JScriptです。関数名でインチキしてるので、って書くまでもなく new ActiveXObject してるのだから明らかだった。
function String.prototype.trim()
{
return this.replace(/^[\s ]+|[\s ]+$/g, "");
}
function String.prototype.atoi(offset, width) // 引数の単位はバイト。Unicodeにマッピングされた ASCIIではなく、UTF-16の符号単位2バイトを2つの ASCII数字として解釈する。符号は非対応。省略したら文字列の端から端まですべてが対象。
{ // 0x30(0),..., 0x39(9)
var Offset = offset != null && 0 <= offset && offset < 2*this.length ? (offset>>>0) : 0;
var Width = width != null && 0 <= width && Offset + width < 2*this.length ? (width>>>0) : 2*this.length - Offset;
if (Width == 0) {
return 0; // NaN もありか?
}
var x = 0, i = (Offset + 1)>>>1, I = (Offset + Width)>>>1;
if (Offset & 1) {
x += (this.charCodeAt(i-1)>>>8) & 0xF;
}
if (x == 0) {
// ありがちな場合のショートカット
while (i < I && this.charCodeAt(i) == 0x3030) {
i += 1;
}
}
for (; i < I; ++i) {
var Ch = this.charCodeAt(i);
x = x * 10 + (Ch & 0xF);
x = x * 10 + ((Ch>>>8) & 0xF);
}
if ((Offset + Width) & 1) {
x = x * 10 + (this.charCodeAt(I) & 0xF);
}
return x;
}
function String.prototype.asPackedSigned(offset, width) // 引数の単位はいずれもパックされた10進1桁(=4bit)。省略したら文字列の端から端まですべてが対象。
{
// assert(0 != this.length);
offset = 0 <= offset ? offset>>>0 : 0;
width = 1 <= width && width <= 4*this.length-offset ? width>>>0 : 4*this.length-offset;
var decstr = "";
for (var i = offset>>>2; i < (offset+width)/4; ++i) {
var LLHH = this.charCodeAt(i);
decstr += String.fromCharCode((LLHH>>>4)&0xF|0x30, LLHH&0xF|0x30, (LLHH>>>12)&0xF|0x30, (LLHH>>>8)&0xF|0x30);
}
offset &= 3;
var Sign = {
"<"/*0x3C*/:+1, "3"/*0x33*/:+1,
"="/*0x3D*/:-1, "7"/*0x37*/:-1
}[decstr.charAt(offset+width-1)] || 1;
return Sign * decstr.substr(offset, width-1); // decstrが 0-9以外の文字(:;<=>?)を含んでると NaN。
}
function String.prototype.endWithSJISLead()
{
if (! this) {
return false;
}
var Ch = this.charCodeAt(this.length-1) >>> 8;
return 0x81 <= Ch && Ch <= 0x9F || 0xE0 <= Ch && Ch <= 0xEF;
}
function String.prototype.asSJIS(Mask) // 先頭1バイトを捨てる(Mask=0xFF00), 末尾1バイトを捨てる(Mask=0x00FF), 両方捨てる(Mask=0x0000), そのまま(Mask=0xFFFF,null,undefined)
{
var Share = {
old: arguments.callee,
path: FSO().BuildPath(
FSO().GetSpecialFolder(2/*TemporaryFolder*/),
FSO().GetTempName()
),
temp: null
};
Share.temp = {
fou: FSO().OpenTextFile(Share.path, 2/*ForWriting*/, true/*creat*/, -1/*Unicode*/),
fin: FSO().OpenTextFile(Share.path, 1/*ForReading*/, true/*creat*/, 0/*Shift_JIS*/)
};
Share.temp.fou.Write(""); // write BOM
Share.temp.fin.ReadAll(); // drop BOM
var asSJIS = function(Mask) {
if (! this) {
return "";
}
if (Mask == null) { // 速度面で意味のあるショートカット。
Share.temp.fou.Write(this);
return Share.temp.fin.ReadAll();
} else {
var s = this;
s = String.fromCharCode(s.charCodeAt(0)&(Mask|0xFF00)) + s.slice(1); // s.length == 1 のときを忘れないように。
s = s.slice(0,-1) + String.fromCharCode(s.charCodeAt(s.length-1)&(Mask|0x00FF)); // s.length == 1 のときを忘れないように。
Share.temp.fou.Write(s);
s = Share.temp.fin.ReadAll();
return s.slice(!(Mask&0x00FF), s.length-!(Mask&0xFF00)); // 後ろから2バイト目が Shift_JISの先行バイトだった場合、捨てるべき1バイトと結びついて1文字になってるだろうから捨てすぎてしまうだろうね。
}
};
asSJIS.clean = function() {
Share.temp.fou.Close();
Share.temp.fin.Close();
FSO().DeleteFile(Share.path);
String.prototype.asSJIS = Share.old;
};
return (String.prototype.asSJIS = asSJIS).call(this, Mask);
// 循環参照とかありそうでやばくない? 長く実行するわけじゃないけども。
}
function String.prototype.asSJIS.clean()
{
/*
asSJISを呼び出したのと同じ回数だけ
ファイルを作成して、削除するのなら、cleanメソッドはいらない。
一時ファイルが蓄積するのを気にしないのなら、cleanメソッドはいらない。
どちらも気になるのでテキトーなタイミングで cleanメソッドを呼んで
一時ファイルを削除する。
その次の asSJIS呼び出しでまた一時ファイルが作成される。
この空のメソッドは上記のコメントのために存在しているのではなく、
一度も asSJISを呼び出さないうちに asSJIS.clean()を呼び出しても
エラーにならないように置かれている。
*/
}
function Date.prototype.format(Fmt)
{
var Map = {
"%": "%"
,Y : ""+this.getFullYear()
,m : ("0"+(this.getMonth()+1)).slice(-2)
,d : ("0"+this.getDate()).slice(-2)
,H : ("0"+this.getHours()).slice(-2)
,M : ("0"+this.getMinutes()).slice(-2)
,S : ("0"+this.getSeconds()).slice(-2)
};
return Fmt.replace(/%(.)/g, function($0, $1) {
return Map[$1] || $0;
});
}
function Date.prototype.isOlderThan(Other)
{
return this.valueOf() < Other.valueOf();
}
function FSO()
{
var FSO_ = new ActiveXObject("Scripting.FileSystemObject");
return (FSO = function() { return FSO_; })();
}
JScriptの function Declarationはたぶん事前実行される点が違うだけで function Expressionのシンタックスシュガーなんだよね。だからドットでつないだ関数名でプロパティ代入ができる。便利。関数の順番を気にしないで済む。
50行目、String.prototype.asPackedSigned
- }[decstr.charAt(width-1)] || 1; + }[decstr.charAt(offset+width-1)] || 1;
offset が4の倍数でない時に間違った場所を読んでいた。ときどき個数としてありえない負の数が現れて発覚した。2万数千件に対して10数件の割合だったので2か月以上気がつかなかった。無念。
最終更新: 2017-03-17T03:48+0900
今まで意識してなかったけど、ビット演算を使って数値を整数化するとき、その結果が符号付き整数か符号無し整数かは重要。ビット演算の前準備としての整数化では32ビット幅への切り詰めが行われるから、すごく大きな正数が切り詰められた結果の32ビット目がたまたま 1 だったときに負数が返ってきたりする。それに驚きたくないなら符号無しのビット演算を使う。
999999999999|0 //=> -727379969 999999999999>>>0 //=> 3567587327 999999999999>>>0|0 //=> -727379969
new Date(File.DateLastModified) みたいに Date型のコンストラクタに渡す以外の使い道が見つけられないのに、その唯一の使用法が載っていない。■今日知ったこと。WScript.Arguments.Namedが返す WshNamedオブジェクトは名前付きのコマンドラインオプションに名前でアクセスするために用意されてるんだけど、名前のリストを列挙するメンバーがドキュメントには存在していない。数字を引数にしたり添え字にしたりしても何も引き出せないし、JavaScriptらしく for...in しても列挙できない。この、JavaScriptになじまない感じ、DateLastModifiedを Dateでラップする感じ、フォルダやファイルのコレクションを Enumeratorでラップするのと同じ感じ……。new Enumerator(WScript.Arguments.Named) でキーを列挙できるんだね。初めて知ったよ。WSH5.6の HTMLヘルプの更新日時が 2001年だっていうから、16年目の発見!■まだわかっていないこと。Dateのコンストラクタのひとつが year, month, date,...というパラメータのリストを受けとるんだけど、手元に "2017-03-07".split("-") したような配列がある場合、Functionオブジェクトの applyメソッドを使って引数リストを配列で与えたくなる。だってそうすると変数に入った文字列が時分秒まで含んでいた場合もひっくるめて "2017-03-07 23:34:00".split(/[- :]/) で処理できるから。でも new演算子ってちょっとなじみが悪くて、この場合に組み合わせて使えない感じ。■あ、だめだ。monthは 0-11の範囲の数字なんだった。splitしたそのままでは使えない。\x{0}A がマッチしない。後ろにあるのが \x{0}\x{0} だとしっかりマッチするんだけど。■比較したのは ver.2.00となんだけど、こちらも、HIGH SURROGATEの直後にパターン \x{0}A で表される文字列がくる場合に限りマッチしない。直後でなければマッチするし、直後でもそれが \x{0}\x{0} で表される文字列とパターンであればマッチするし、HIGH SURROGATEが LOW SURROGATEであってもやはりマッチする。■ということを VC++の L"\xD8D8\xDCDC\0A" みたいなリテラル文字列をちょっとずつ修正しながら確かめたんだけど、コンパイルされるから実際の文字コード・バイト列はよくわからんよね。Windowsでワイド文字っていうと Unicodeであり、Windowsで Unicode(符号化文字集合だが符号化方式を指したりもする)っていうと UTF-16LEなんだろうけど。←ここでは BOMの有無を規定していない。こんな勝手なお約束をグローバルルールにしようなんて考えてるとしても無理筋ですよ>「UTF-16LE符号化スキーム/16ビット整数をリトルエンディアンで直列化する。バイト順マーク (BOM) は使用不可。」 使用不可! 無理に決まってんじゃん。そんな期待予測予断を誰かに抱かせようという試みが既に有害だわ。LEは little endianの略で、さっきのは目に見えない情報を UTF-16というエンコーディング名の後にメモ書きしただけですからね。■2種類のパターンの選び方に特に意味はなくて、「U+0000 U+0000 U+0041(大文字A) U+0000」の4文字を多数含むバイナリファイルを置換・整形してるときに気づいたってだけ。エディタは UTF-8で解釈してるからファイルにあるのは 00 00 41 00 の4バイトで(※Stirlingで確かめた)、エディタの内部表現とコンパイラのワイド文字列リテラルが wchar_t列で 0000 0000 4100 0000 だと思う(※たぶん UTF-16LE。LEのバイトオーダーってたぶんこうだよね。sizeof (wchar_t) == 2 なのは処理系依存で VC++だからだってね)。■ところで、サクラエディタがファイルを UTF-8のバイトストリームとして読み込んだとき、文字を構成しない不正なバイトは CCodeBase::BinToText によって「バイナリ1バイトを U+DC00 から U+DCFF までに対応付け」られるらしいことが、ちょろっとソースを読んだ限りでは窺われるんだけど、マッチを見失わせるペアを構成しないサロゲートってこうして生まれるんだね……。
midnight-railgun 去年の分のcsv持ってたから見てみたけど普通に2016年から始まってたからどんどん列が増えてく形式みたいね。」右に伸びていくぐらいはちょっと転置してやれば下に伸びていくのと同じなわけで。その