* kclib1のページ#9 -(by [[K]], 2019.04.18) ** (13) KSizPtr_addFile -int KSizPtr_addFile(KSizPtr *w, const char *path, const char *mod, int flg) --これはKSizPtr_addBytes()の仲間みたいなもので、ファイルを読み込んで、KSizPtrの末尾に追加します。 --戻り値は読み込んだファイルのバイト数です。 --flgについては次の通りです。 ---1: foepnに失敗したらエラー終了します。これがなければエラーの場合は-1を返して何もしません。 ---2: ファイルを読み込んだ後に16バイトの0x00を追加します。テキストファイルなどで末尾にゼロを付与したい場合は有益でしょう。このオプションを指定した場合ですが、戻り値にはこの16は加算されません。 ---4: 渡されたKSizPtrをテンポラリとして使ってもよいという特別なオプションです。この場合、wは真っ先に初期化されるので、wは未初期化かもしくはfree済みである必要があります。またs1はかなり大きく取られているケースがあります(特にファイルが小さかった場合)。しかしこのオプションが有効なら、memcpyの呼び出し回数が1回減るので、若干の高速化が期待できます。・・・まあ詳細は実装を見て使うかどうか決めればいいでしょう。よくわからなければ使わなければいいだけです。 ---- -[内部実装] #include "kclib1.h" #include <stdio.h> #include <string.h> int KSizPtr_addFile(KSizPtr *w, const char *path, const char *mod, int flg) { KSizPtr sp, *v = &sp; int i; FILE *fp = fopen(path, mod); if (fp == 0) { if (path == 0) path = "(NULL)"; if ((flg & 1) != 0) kerrorExit("KSizPtr_readFile: fopen error: '%s'", path); return -1; } if ((flg & 4) != 0) v = w; KSizPtr_init(v, 256 * 1024); for (;;) { if (v->s >= v->s1) KSizPtr_expand2(v); i = fread(v->p + v->s, 1, v->s1 - v->s, fp); if (i == 0) break; v->s += i; } fclose(fp); i = v->s; if ((flg & 2) != 0) { static char z16[16]; KSizPtr_addBytes(v, 16, z16); } if (w != v) { KSizPtr_addBytes(w, v->s, v->p); KSizPtr_free(v); } return i; } ** (14) kreadFileA -void *kreadFileA(const char *path, const char *mod, int *psiz, int flg) --これはファイルを読み込んで文字列もしくはバイト列として返す関数です。 --psizがNULLでなければ、読み込んだファイルのバイト数を*psizに返します。 --flgに2を指定すれば、末尾に0x00を16バイト追加します(まあたいていは1バイトで十分でしょうけど)。 --関数名の末尾にAがついているのは、ここで返されるポインタは自動解放プール(KAutoreleasePool)に登録されていることを表しています。 ---- -[効果]ここまでの関数を使うと、[[text0001]]に書いてある「''TL-1''」は65行から50行にまで減ります。 --しかも単に短くなっただけではなく、ファイルが長くなっても問題なく追従して動くようになっています(オリジナルのTL-1は手抜きにより1万バイト以上のファイルは扱えませんでした)。 --なんと素晴らしいことでしょう。・・・そう、私はこういう成果のためにkclib1を開発してきたのです! #include <stdio.h> #include "kclib1.h" int var[256]; // 変数. unsigned char *txt; // ソースコード. int pc = 0; // プログラムカウンタ. int getNumber(unsigned char c) { if ('0' <= c && c <= '9') return c - '0'; // 1桁の定数. return var[c]; // 1文字の変数名. } int main(int argc, const char **argv) { int i, pc0; KAutoreleasePool_open(); if (argc < 2) // 引数が少ないのでエラー表示して終了. kerrorExit("usage>%s program-file\n", argv[0]); txt = kreadFileA(argv[1], "rt", NULL, 1 + 2); for (;;) { pc0 = pc; if (txt[pc] == 0) // ファイル終端. return 0; if (txt[pc] == '\n' || txt[pc] == ' ' || txt[pc] == '\t' || txt[pc] == ';') { // 空行など. pc++; continue; } if (txt[pc + 1] == '=') { // 2文字目が"=". i = txt[pc]; if (txt[pc + 3] == ';') { // 単純代入. var[i] = getNumber(txt[pc + 2]); } else if (txt[pc + 3] == '+' && txt[pc + 5] == ';') { // 加算. var[i] = getNumber(txt[pc + 2]) + getNumber(txt[pc + 4]); } else if (txt[pc + 3] == '-' && txt[pc + 5] == ';') { // 減算. var[i] = getNumber(txt[pc + 2]) - getNumber(txt[pc + 4]); } else goto err; } else if (txt[pc] == 'p' && txt[pc + 1] == 'r' && txt[pc + 5] == ' ' && txt[pc + 7] == ';') { // 最初の2文字しか調べてない(手抜き). printf("%d\n", var[txt[pc + 6]]); } else goto err; while (txt[pc] != ';') pc++; } err: kerrorExit("syntax error : %.10s\n", &txt[pc0]); return 1; // dummy. } ---- -[内部実装] #include "kclib1.h" #include <string.h> void *kreadFileA(const char *path, const char *mod, int *psiz, int flg) { KSizPtr sp; int i = KSizPtr_addFile(&sp, path, mod, flg | 4), j; void *p; if (i < 0) return 0; j = i; if ((flg & 2) != 0) j += 16; p = KAutoreleasePool_alloc(0, j); if (j > 0) memcpy(p, sp.p, j); KSizPtr_free(&sp); if (psiz != 0) *psiz = i; return p; } ** (15) ksgetsA -char *ksgetsA(const char **ps, int *psiz) --何か適当なファイルを s = kreadFileA(...); で読み込んだとして、 t = ksgetsA(&s, NULL); などとすれば、1行ずつ切り出せます。 --sは切り出した分だけ進みます。すべて切り出し終わって *s == '\0' になった場合は、 strlen(t) == 0 な文字列tが返され続けます。 --関数名の末尾にAがついているのは、ここで返されるポインタは自動解放プール(KAutoreleasePool)に登録されていることを表しています。 ---- -[効果]切り出す行がどのくらいの長さになるのか気にする必要はありません。こんなに適当に書いてもバッファのオーバーランもメモリリークもありません。 --まあメモリの解放を最後までサボっているので、非常に大きなテキストファイルを渡せば途中で out of memory になるかもしれませんが、それにしたってメモリ不足で暴走したりとかヌルポインタを踏んで落ちるとか、そういう心配はありません。きちんとエラーメッセージを出してからエラー終了してくれます。 --以下のプログラムは引数で渡されたテキストファイルを行番号付きで表示します。 #include "kclib1.h" #include <stdio.h> int main(int argc, const char **argv) { KAutoreleasePool *ap = KAutoreleasePool_open(); const char *s = kreadFileA(argv[1], "rb", NULL, 1 + 2); int i; for (i = 1; ; i++) { const char *t = ksgetsA(&s, 0); if (*t == '\0') break; printf("%08d: %s", i, t); } KAutoreleasePool_close(ap); return 0; } ---- -[内部実装] #include "kclib1.h" #include <string.h> void *ksgetsA(void *ps, int *psiz) { const char *s = *((const char **) ps); char *t; int i; for (i = 0; s[i] != '\0'; i++) { if (s[i] == '\n') { i++; break; } } t = KAutoreleasePool_alloc(0, i + 1); if (i > 0) memcpy(t, s, i); t[i] = '\0'; *((const char **) ps) = s + i; if (psiz != 0) *psiz = i; return t; } -関数呼び出し時に、constがついているとかいないとか、unsignedがついているかいないとかで警告が出るのがうっとうしいので、内部実装では上記の型で実装してあります。 ** (16) kcutCrLfM -char *kcutCrLfM(int s, char *p) --文末にCRLFコードもしあれば、それを除去します。 --関数名末尾のMはmutatorやmodifierを意味しており、つまり引数を直接書き換える形で結果を生成します。 ---- -[内部実装] #include <string.h> char *kcutCrLfM(int s, char *p) { if (s < 0) s = strlen(p); if (s > 0 && p[s - 1] == '\n') p[--s] = '\0'; if (s > 0 && p[s - 1] == '\r') p[--s] = '\0'; return p; } ** (17) kstrncpyA -char *kstrncpyA(int s, char *p) --文字列の一部を切り出した自動解放済みの文字列を作ります。バイト列にも使用可能です。 --文字列の一部を切り出した自動解放済みの文字列を作ります。 --sに負の数を指定した場合は、kstrcpyAとしてふるまいます。 ---- -[内部実装] #include "kclib1.h" #include <string.h> void *kstrncpyA(int s, const void *p) { void *q; if (s < 0) s = strlen(p); q = KAutoreleasePool_alloc(0, s + 1); if (s > 0) memcpy(q, p, s); ((char *) q)[s] = '\0'; return q; } * こめんと欄 #comment