
あけましておめでとうございます。今年も1年元気で過ごすぞ。ときりっとした顔で言っているようです。
概要
C言語でソートに挑戦することにしました。
成し遂げられるのか分かりませんがとにかくやってみます。
CSVファイルの読み込みを確かめる。
読み込んだデータを配列(構造体)にセットする。
ソートは次回とします。
C言語 マニュアル
私が今回参考にしたC言語マニュアルです。
いずれも非常に分かりやすい文書と例文で書かれています。
ありがとうございました。
https://manpages.ubuntu.com/manpages/kinetic/ja/man3/
https://programming-place.net/ppp/index.html
https://www.ibm.com/docs/ja/i
ファイルを読み込み後、ファイルの作成
とっかかりのコードです。
一文字毎に取り出しファイルに書き込むということをしています。
本来は、1字毎に読み取ってバイト数の判定して処理するのでしょうが、文字の扱いが難しいそうです。
ちょっと大変そうなので別の方法を模索します。
#include <stdio.h>
#include <wchar.h>
int main()
{
FILE *fin;
FILE *fout;
wint_t wc;
fin = fopen("./input.csv", "r");
fout = fopen("./output.csv", "w");
while ((wc = fgetwc(fin)) != WEOF){
wprintf(L"%ls\n", wc);
fputwc(wc, fout);
}
fclose(fin);
fclose(fout);
printf("File has been created...\n");
return 0;
}
上のコードの実行結果です。
バイト単位で表示しているようです。

次に取り組んだのが、一行毎にバッファに取り込みセパレート文字で分離するという方法です。
試行錯誤して落ち着いたのが次のコードです。
こちらの方が何とか進めれそうなのでこれで行くことにします。
#include <stdio.h>
#include <stdlib.h>
#include <wchar.h>
#include <locale.h>
int main()
{
FILE *fin;
FILE *fout;
wchar_t *wc;
setlocale(LC_CTYPE, "");
fin = fopen("./input.csv", "r");
fout = fopen("./output.csv", "w");
const wchar_t *const separator = L",";
for (;;)
{
wchar_t str[90];
if (fgetws(str, sizeof(str) / sizeof(str[0]), fin) == NULL)
{
if (feof(fin))
{
/* ファイルの終わり */
break;
}
else
{
fputs("エラーが発生しました。\n", stderr);
exit(EXIT_FAILURE);
}
}
wchar_t *token = wcstok(str, separator);
while (token != NULL)
{
wprintf(L"%ls\n", token);
token = wcstok(NULL, separator);
}
fwprintf(fout, L"%ls", str);
}
fclose(fin);
fclose(fout);
printf("\nFile has been created...\n");
return 0;
}
上のコード実行結果です。
セパレータで綺麗に分離されており、文字もきちんと表示しています。

wcstok カンマ区切りで分離
IBM i 資料の wcstok の解説の中にあるコードを手直ししてみました。
参考にしたC言語マニュアルにも wcstok の第三引数 &work があるのですが、私の使っているコンパイラーでは引数が多すぎますとエラーになります。
ですので、第三引数 &work をカットして使ってみましたが、今回の使い方で特に問題はないようです。
#include <stdio.h>
#include <wchar.h>
int main(void)
{
static wchar_t str1[] = L"?a??b,,,#c";
static wchar_t str2[] = L"\t \t";
wchar_t *t;
/* IBM等 とは違うようです。
/* wchar_t *ptr1, *ptr2; */
/* &work(&ptr1 &ptr2) をカットして順番を入れ替えました。 */
t = wcstok(str1, L"?"); /* t points to the token L"a" */
wprintf(L"t = %ls\n", t);
t = wcstok(NULL, L","); /* t points to the token L"?b" */
wprintf(L"t = %ls\n", t);
t = wcstok(NULL, L"#,"); /* t points to the token L"c" */
wprintf(L"t = %ls\n", t);
t = wcstok(str2, L" \t,"); /* t is a null pointer */
wprintf(L"t = %ls\n", t);
t = wcstok(NULL, L"?"); /* t is a null pointer */
wprintf(L"t = %ls\n", t);
return 0;
}
上のコードの実行結果です。
想定通りになっています。

2次元配列(構造体)に格納
変更前のトークン部分です。
wchar_t *token = wcstok(str, separator);
while (token != NULL)
{
wprintf(L"%ls\n", token);
token = wcstok(NULL, separator);
}
このようにしたいので、全体的に見直したコードが下のようになりました。
wchar_t *token = wcstok(str, separator);
while (token != NULL)
{
/* wprintf(L"%ls\n", token); */
if (f == 0)
{
res = wcsncpy(record[count].f1, token, 20);
}
else
{
res = wcsncpy(record[count].f2, token, 30);
}
f += 1;
token = wcstok(NULL, separator);
}
#include <stdlib.h>
#include <stdio.h>
#include <wchar.h>
#include <locale.h>
int main()
{
FILE *fin;
FILE *fout;
wchar_t *res;
int f;
setlocale(LC_CTYPE, "");
fin = fopen("./input.csv", "r");
fout = fopen("./output.csv", "w");
const wchar_t *const separator = L",";
struct records
{
char f1[40];
char f2[50];
};
struct records record[1000];
int count = 0;
for (;;)
{
wchar_t str[90];
f = 0;
if (fgetws(str, sizeof(str) / sizeof(str[0]), fin) == NULL)
{
if (feof(fin))
{
/* ファイルの終わり */
break;
}
else
{
fputs("エラーが発生しました。\n", stderr);
exit(EXIT_FAILURE);
}
}
wchar_t *token = wcstok(str, separator);
while (token != NULL)
{
/* wprintf(L"%ls\n", token); */
if (f == 0)
{
res = wcsncpy(record[count].f1, token, 20);
}
else
{
res = wcsncpy(record[count].f2, token, 30);
}
f += 1;
token = wcstok(NULL, separator);
}
wprintf(L"%ls\n", record[count].f1);
wprintf(L"%ls\n", record[count].f2);
fwprintf(fout, L"%ls", str);
count += 1;
}
fclose(fin);
fclose(fout);
printf("\nFile has been created...\n");
return 0;
}
上のコードを実行してみました。
input.csv の文字コードが Shift_JIS ならば文字化けもなくコンソールに表示されます。

しかし、文字コードが UTF-8 ですと、次に示すように文字化けします。
3バイト文字をうまく処理する方法を考えなければなりませんが、今回はShift_JIS のファイル作成を目的としますので、UTF-8からShift_JIS のデコード、逆のエンコードを考えません。

100万件の読み込みができない
不覚でした。関数内で大きな配列を作ることは不適切のようです。
2000件でチェックしていたのですが、大きなデータにしたらメモリ不足で止まりました。
一時しのぎですが、次のように構造体の定義、作成をグローバルにしました。
#include <stdlib.h>
#include <stdio.h>
#include <wchar.h>
#include <locale.h>
/* ここから外に出した部分 */
#define N 1200000
struct records
{
char f1[40];
char f2[50];
};
struct records record[N];
/* ここまで */
int main()
{
FILE *fin;
FILE *fout;
wchar_t *res;
int f;
setlocale(LC_CTYPE, "");
fin = fopen("./input.csv", "r");
fout = fopen("./output.csv", "w");
const wchar_t *const separator = L",";
int count = 0;
for (;;)
{
wchar_t str[90];
f = 0;
if (fgetws(str, sizeof(str) / sizeof(str[0]), fin) == NULL)
{
if (feof(fin))
{
// ファイルの終わり
break;
}
else
{
fputs("エラーが発生しました。\n", stderr);
exit(EXIT_FAILURE);
}
}
wchar_t *token = wcstok(str, separator);
while (token != NULL)
{
if (f == 0)
{
res = wcsncpy(record[count].f1, token, 20);
}
else
{
res = wcsncpy(record[count].f2, token, 30);
}
f += 1;
token = wcstok(NULL, separator);
}
/* wprintf(L"%ls\n", record[count].f1); */
/* wprintf(L"%ls\n", record[count].f2); */
fwprintf(fout, L"%ls\n", str);
count += 1;
}
fclose(fin);
fclose(fout);
wprintf(L"%ls\n", record[count - 1].f1);
wprintf(L"%ls\n", record[count - 1].f2);
printf("\nFile has been created...%d\n", count);
return 0;
}
上のコードを実行してみました。
時間にして1秒足らずに読み込みました。期待できそうです。

今日の投稿はここまでとします。