昔の思い出、今の現実
昔、とてもきれいな女の子がいた。
初めて会った瞬間、何か吸い込まれる感じがしたのを今でもはっきりとおぼえている。最初は何も話しかけられなかった。でも、そのうち会うたびに、何か話すことができて、そのたび毎にいろんなことを話した。何を話題として話したのかはもう忘れてしまったが、会話するたびに話し方の上品さが伝わってくる。
手紙も何通か送ったのをおぼえている。返事も何通かもらった。達筆だった。本当に魅力的なだった。ぼくなぞは手の届かないような高嶺の花のように思われた。
でも、先日一年ぶりぐらいに、久しぶりに会ってみたら、風貌こそ変わりは無かったが、表情がまるで無い。昔はあんなに表情豊かだったと思ったが、今では魅力のかけらもない、ただの女になっていた。
あまりの変わりように、少し戸惑った。それほど、彼女は変わってしまった。何がそんなに変えてしまったのだろう。思い出の中の女の子と同姓同名の別人が目の前にいるような感覚だった。「昔はあんなに・・・」というのは、単なる思い出になってしまった。
それが本当に悲しい。
CSVを処理するパーザーを作ってみた
職場では何かとCSVとしてデータを書き込んだり、読み込んだりすることが多い。書き込むのは簡単でも、読み込んで使うとなると文字列処理が入ってくるので難しくなる。なので、ちょっと試しに作ってみた。デリミタは引数として変えられるようにして、コンマじゃなくても融通が利くようにしてみた。
ウェブは便利だが、初歩でつまづく人も多くてどうしてもちょっと踏み込んだアルゴリズムはなかなか無いのが現実だ。このぐらいの解析作業もちょっとコピー&ペーストってなわけにもいかず、地道に自分で考えるしかないこともしょっちゅうである。不平不満を言ってもしょうがないのでためしに公開してみる。DLLにして自分用のライブラリ化するのもいいかもしれない。
strchr.c
#include <stdio.h> #include <stdlib.h> #include <string.h> /* get_token: 文字列をデリミタに従い、トークンに切り出す */ int get_token(char* crnt, int delimiter, char** output, int* num) { int i = 0; // カウンター int count = 0; // 切り出したトークンの数 char* next = NULL; // 次のトークンの先頭位置 void** token = NULL; // 文字列の先頭、または各改行の次の文字 // 初期化 *num = 0; // (エラー処理)文字列がNULLの場合は、何もせず終了する if (crnt == NULL) { fprintf(stderr, "NULLポインターが渡されました\n"); return 0; } // (エラー処理)何もせず終了する if (strlen(crnt) <= 0) { fprintf(stderr, "文字列長が0です\n"); return 0; } // トークンに分ける while (1) { // 文字列先頭の位置をトークンに代入する output[count] = crnt; // 次のトークンを切り出してくる next = strchr(crnt, delimiter); // デリミタが見つからないときは抜ける if (next == NULL) { // デリミタは無い場合でも、 // まだ文字列がある場合は最後の要素として格納する if (strlen(crnt) >= 0) { count++; output[count] = crnt; } break; } else { *next = '\0'; } // 次の文字列に行く crnt = next + 1; // トークン数をカウントアップする count++; } // トークン数を出力する *num = count; return 0; } int main(int argc, char* argv[]) { int i = 0; int num = 0; char* str = NULL; char** data = NULL; // 入力する文字列 str = malloc(0xFFFF); strcpy(str, "1,22,,333,444,555,"); printf("INPUT: %s:(%p)\n", str, str); // 与えられた文字列をトークンに従い、切り分ける // (元々のstrは破壊される) data = malloc(0xFFFF); get_token(str, ',', data, &num); // 切り出してきたトークンの表示を行う for (i = 0; i < num; i++) { printf("OUTPUT: %d: [%s:\t(%p)]\n", i, data[i], data[i]); } free(data); free(str); return 0; }
ヒープを使ってみる
今回はヒープを用いたメモリ確保を実験してみた。Windows APIによれば、mallocを用いるより少し細かく制御できるようだ。
heap.c
#include <windows.h> #include <stdio.h> #include <stdlib.h> #include <string.h> int main(int argc, char* argv[]) { LPVOID p; // ヒープから取得したポインタ HANDLE hHeap; // ヒープのハンドル SIZE_T dwInitialSize = 0xFFFF; // 初期のヒープサイズ SIZE_T dwBytes = dwInitialSize; // 割り当てたいバイト数 SIZE_T dwMaximumSize = 0; // 最大ヒープサイズ DWORD dwFlags = HEAP_ZERO_MEMORY; // ヒープの割り当て方法の制御 DWORD flOptions = 0; // ヒープ割り当て方法の属性 // プライベートヒープのハンドルを取得する hHeap = HeapCreate(flOptions, dwInitialSize, dwMaximumSize); // ヒープを取得する p = HeapAlloc(hHeap, dwFlags, dwBytes); // ヒープを使ってみる memcpy(p, "abcde", sizeof("abcde")); printf("%s\n", p); // ヒープの解放 HeapFree(hHeap, 0, p); return 0; }
ファイルの途中からメモリマップドファイルのマッピングをする(続き)
ファイルの途中からマッピングをすることで、何ができるかというと、巨大なファイルを少しずつビューをずらしながら全体を見渡せることができるということが挙げられる。
p619.cpp
#include <windows.h> #include <stdio.h> __int64 Count0s(void) { // ビューの先頭アドレスは、割り当て単位の倍数でなければならない SYSTEM_INFO sinf; GetSystemInfo(&sinf); // データファイルをオープンする HANDLE hFile = CreateFile( "C:\\HugeFile.Big", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); // ファイルマッピングオブジェクトを作成する HANDLE hFileMapping = CreateFileMapping( hFile, NULL, PAGE_READONLY, 0, 0, NULL); DWORD dwFileSizeHigh; __int64 qwFileSize = GetFileSize(hFile, &dwFileSizeHigh); qwFileSize += (((__int64)dwFileSizeHigh) << 32); // ファイルオブジェクトハンドルにはもうアクセスする必要はない CloseHandle(hFile); __int64 qwFileOffset = 0, qwNumOf0s = 0; while (qwFileSize > 0) { // このビューにマッピングするバイト数を決める DWORD dwBytesInBlock = sinf.dwAllocationGranularity; if (qwFileSize < sinf.dwAllocationGranularity) { dwBytesInBlock = (DWORD)qwFileSize; } PBYTE pbFile = (PBYTE)MapViewOfFile( hFileMapping, FILE_MAP_READ, (DWORD)(qwFileOffset >> 32), (DWORD)(qwFileOffset & 0xFFFFFFFF), dwBytesInBlock); // このブロックに含まれる0を数える for (DWORD dwByte = 0; dwByte < dwBytesInBlock; dwByte++) { if (pbFile[dwByte] == 0) { qwNumOf0s++; } } // ビューのマッピングを解除する。 // アドレス空間に複数のビューを配置するのを避ける UnmapViewOfFile(pbFile); // ファイル内の次のバイトセットにスキップする qwFileOffset += dwBytesInBlock; qwFileSize -= dwBytesInBlock; } CloseHandle(hFileMapping); return qwNumOf0s; } int main() { return printf("%ld\n", Count0s()); }
ファイルの途中からメモリマップドファイルのマッピングをする
ファイルの途中からマッピングを開始しようと思ったのだが、なかなかうまくいかず、単なるNULLポインタしか取得できなかった。ようやく理由が分かった。オフセットがシステム指定の数の倍数で、自由ではないらしい。とりあえず、ファイルの途中からマッピングをさせることができた。このソースコードを実行して、ファイルを生成させるとちゃんと途中からマッピングした結果を見ることができる。
john.c
#include <windows.h> #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { int i = 0; int tmp = 0; int memblock = 0; int size = 65536; char* mydata = NULL; HANDLE h = 0; HANDLE h2 = 0; DWORD errorcheck = 0; SYSTEM_INFO SystemInfo; // [01]システム情報を得る GetSystemInfo(&SystemInfo); // [02]メモリマップのオフセット最小単位をとってくる memblock = SystemInfo.dwAllocationGranularity; // [03] メモリをマッピングをするファイルを作成する h = CreateFile( "john_3_16.dat" , GENERIC_READ | GENERIC_WRITE , FILE_SHARE_READ | FILE_SHARE_WRITE , NULL , CREATE_ALWAYS , FILE_ATTRIBUTE_NORMAL , NULL ); errorcheck = GetLastError(); if (h == INVALID_HANDLE_VALUE) { printf("エラー(%d)\n", errorcheck); } // [04] ファイルのマッピングを作成する h2 = CreateFileMapping(h, NULL, PAGE_READWRITE, 0, memblock*3, "mmap"); errorcheck = GetLastError(); if (h2 == INVALID_HANDLE_VALUE) { printf("エラー(%d)\n", errorcheck); } // [05] ファイルビューをマッピングする mydata = MapViewOfFile(h2, FILE_MAP_ALL_ACCESS, 0, memblock, memblock*2); errorcheck = GetLastError(); if (mydata == NULL) { printf("エラー(%d)\n", errorcheck); } // [06]確保したヒープを自由に使ってみる printf("address: 0x%p\n", mydata); for (i = 0; i < size; i++) { mydata[i] = ' '; } strcpy(mydata, "For God so loved the world that He gave His only begotten Son, "); strcat(mydata, "that whoever believes in Him should not perish"); strcat(mydata, " but have everlasting life."); // [07] ファイルのビューをフラッシュする tmp = FlushViewOfFile(mydata, 0); errorcheck = GetLastError(); if (tmp == 0) { printf("エラー(%d)\n", errorcheck); } // [08] ファイルビューをマッピング解除する tmp = UnmapViewOfFile(mydata); errorcheck = GetLastError(); if (tmp == 0) { printf("エラー(%d)\n", errorcheck); } CloseHandle(h2); CloseHandle(h); return 0; }
メモリマップドファイルを活用し、メモリの中身をファイルに書き出す
malloc()のように使え、そのままファイルに書き出すことができる。また、プロセス間でメモリを共有するのに便利である。
sharememory.c (共有メモリに情報を書き出す)
#include <windows.h> #include <stdio.h> #include <stdlib.h> typedef struct __mydata { long data[0xFFFFFF]; } mydata_t; int main(int argc, char *argv[]) { int i =0; int *p = NULL; HANDLE h; HANDLE h2; int tmp = 0; mydata_t* mydata; h = CreateFile( "memorymapping.dat" , GENERIC_READ | GENERIC_WRITE , FILE_SHARE_READ | FILE_SHARE_WRITE , NULL , OPEN_ALWAYS , FILE_ATTRIBUTE_NORMAL , NULL ); printf("HANDLE = %ld, INVALID_HANDLE_VALUE = %ld\n", h, INVALID_HANDLE_VALUE ); h2 = CreateFileMapping(h, NULL, PAGE_READWRITE, 0, sizeof(mydata_t), "mmap"); mydata = MapViewOfFile(h2, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(mydata_t)); printf("address: 0x%p\n", p); for (i = 0; i < 128; i++) { mydata->data[i] = i + 'A'; } tmp = FlushViewOfFile(p, 0); printf("FlushViewOfFile() = %d\n", tmp); tmp = UnmapViewOfFile(p); printf("UnmapViewOfFile() = %d\n", tmp); CloseHandle(h2); CloseHandle(h); return 0; }