付録:csv2tsvソースコードほか
tasks.json
{
"version": "2.0.0",
"tasks": [
{
"label": "Clang",
"type": "process",
"command": "make",
"args": [
"build"
],
"problemMatcher": [],
"group": {
"kind": "build",
"isDefault": true
}
},
{
"label": "Test",
"type": "process",
"command": "make",
"args": [
"test"
],
"problemMatcher": [],
"group": {
"kind": "build",
"isDefault": true
}
}
]
}
Makefile
CMD= csv2tsv.exe
SRCS= $(wildcard *.c)
OBJS= $(SRCS:.c=.o)
CC= clang
CFLAGS+=-g
EXIST= cmd.exe //C if exist
build: $(CMD)
$(CMD): $(OBJS)
$(CC) $(CFLAGS) -o $(CMD) $(OBJS)
.c.o:
$(CC) -c $< -o $@
test: test-kyua
test-kyua: $(CMD)
cd tests; kyua test
test-original: $(CMD)
pwsh .\tests\test.ps1
report: $(CMD) clean-report
cd tests; kyua report-html
clean: clean-report
$(EXIST) $(CMD) del $(CMD)
$(EXIST) main.o del $(OBJS)
$(EXIST) $(CMD:.exe=.ilk) del $(CMD:.exe=.ilk)
$(EXIST) $(CMD:.exe=.pdb) del $(CMD:.exe=.pdb)
$(EXIST) nul del *.tmp
$(EXIST) csv2tsv.exe.stackdump del csv2tsv.exe.stackdump
clean-report:
$(EXIST) .\tests\html rmdir .\tests\html //S //Q
main.h
int csv2tsv(const char *, int, char *, int);
char *file2str(const char *)
main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "main.h"
int main(int argc, char *argv[]) {
char *csvdata, *tsvdata;
int csvdata_bytes, tsvdata_bytes;
csvdata = file2str(argv[1]);
csvdata_bytes = strlen(csvdata);
tsvdata_bytes = csvdata_bytes;
tsvdata = calloc(tsvdata_bytes + 1, sizeof(char));
csv2tsv(csvdata, csvdata_bytes, tsvdata, tsvdata_bytes);
printf("%s", tsvdata);
return 0;
}
util_csv.c
#include <stdbool.h>
static bool record_outputed;
static char gettsvchar(const char);
int csv2tsv(const char *ibuf, int ibufsize, char *obuf, int obufsize) {
// When the target is empty, no processing is done.
if (0 == ibufsize)
return 0;
const char *p_i, *end_i;
char *p_o;
int tsv_len = 0;
p_i = ibuf;
end_i = &ibuf[ibufsize - 1];
p_o = obuf;
// Indicates the state during parsing.
typedef enum FIELD_STATUS {
FIELD_END,
IN_FIELD,
IN_QUOTED_FIELD
} record_status;
record_status rs = FIELD_END;
record_outputed = false;
while (1) {
if ('\n' == *p_i) {
if (!record_outputed) {
// nothing
}
rs = FIELD_END;
*p_o = gettsvchar('\n');
++p_o;
++tsv_len;
} else {
switch (rs) {
case FIELD_END:
if (',' == *p_i) {
// nothing
} else if ('"' == *p_i) {
rs = IN_QUOTED_FIELD;
} else {
rs = IN_FIELD;
*p_o = gettsvchar(*p_i);
++p_o;
++tsv_len;
}
break;
case IN_FIELD:
if (',' == *p_i) {
rs = FIELD_END;
} else {
*p_o = gettsvchar(*p_i);
++p_o;
++tsv_len;
}
break;
case IN_QUOTED_FIELD:
if ('"' == *p_i) {
if (p_i == end_i) {
rs = FIELD_END;
} else if (',' == *(p_i + 1)) {
rs = FIELD_END;
++p_i;
} else if ('"' == *(p_i + 1)) {
*p_o = gettsvchar(*p_i);
++p_o;
++tsv_len;
++p_i;
}
} else {
*p_o = gettsvchar(*p_i);
++p_o;
++tsv_len;
}
break;
}
switch (rs) {
case FIELD_END:
*p_o = '\t';
++p_o;
++tsv_len;
record_outputed = false;
break;
case IN_FIELD:
case IN_QUOTED_FIELD:
break;
}
}
if (p_i == end_i || tsv_len == obufsize)
break;
else
++p_i;
}
return tsv_len;
}
static char gettsvchar(const char c) {
record_outputed = true;
if ('\t' == c) {
return ' ';
} else {
return c;
}
}
util_file.c - Windows APIを使うように書き換えたバージョン
#include <stdio.h>
#include <windows.h>
char *file2str(const char *filepath) {
/*
* 指定されたファイルを開く
*/
HANDLE hFile;
// ファイルを開く
hFile = CreateFile(filepath, // ファイル名
GENERIC_READ, // 読み込みのために開く
0, // 共有しない
NULL, // デフォルトセキュリティ
OPEN_EXISTING, // 既存のファイルを開く
FILE_ATTRIBUTE_NORMAL, // 通常のファイル
NULL); // 属性およびフラグなし
// ファイルが開けなかった場合の処理
if (INVALID_HANDLE_VALUE == hFile) {
fprintf(stderr, "no such file: %s\n", filepath);
exit(EXIT_FAILURE);
}
/*
* ファイルサイズを取得し、ファイルサイズ+1のメモリを確保
*/
DWORD dwFileSize;
dwFileSize = GetFileSize(hFile, NULL);
// ヒープを生成
HANDLE hHeap = HeapCreate(HEAP_GENERATE_EXCEPTIONS, 0, 0);
// ヒープからメモリを確保
char *buf, *p;
int buflen = dwFileSize + 1;
buf = (char*)HeapAlloc(hHeap,
HEAP_ZERO_MEMORY,
sizeof(char) * buflen);
p = buf;
/*
* ファイルの中身をメモリへ読み込み
*/
DWORD dwBytesRead;
while (ReadFile(hFile, p, buflen, &dwBytesRead, NULL)
&& dwBytesRead > 0) {
p += dwBytesRead;
}
/*
* ファイルを閉じる
*/
CloseHandle(hFile);
/*
* ヒープを閉じる
*/
CloseHandle(hHeap);
return buf;
}
参考