「C言語/ファイル入出力」の版間の差分

削除された内容 追加された内容
Ef3 (トーク | 投稿記録)
→‎ファイルのオープン: {{コラム|"stdafx.h"って何?}}
タグ: 2017年版ソースエディター
Ef3 (トーク | 投稿記録)
同じファイルを重複して fopen() しているバグ多数。
タグ: 2017年版ソースエディター
29 行
FILE型へのポインタの宣言は、
 
<code> FILE *fp; </code>
 
の書式で行う。
 
さて、例えば、
<code> FILE *fp1; </code>
なら、fp1という名のFILE型へのポインタが作成される。
 
137 行
 
ファイルに何かデータを書き込むには、 <code>fprintf</code> という関数を使う。
:(なお、「fprintf」とはC言語の専門用語であり、日常英語には無い用語である。)
 
fprintf の冒頭のfは、ファイル関係であることをあらわしている。最後のfは、もともとC言語には「printf」(プリントエフ)という関数があり、(最後のfは)Format の f。
 
 
147 ⟶ 146行目:
</syntaxhighlight>
 
宣言指令しておく必要の生じる場合もある。
 
 
172 ⟶ 171行目:
 
 
何か変数をファイルに書き込みたい場合、たとえば整数型の変数だとして、変数名が「hensuuvar」なら、それを書き込むには、あらかじめソースコードで
 
<syntaxhighlight lang="C">
int hensuuvar;
</syntaxhighlight>
 
のように、どこかで宣言しておく。
 
そのあと、プログラム中にて変数「hensuuvar」に目的の値を入力させるようにしておく。
 
そのあと、プログラムがコード内でのファイル書き込み実行場所にて
<syntaxhighlight lang="C">
fprintf(fp1, "%d \n", hensuuvar);
</syntaxhighlight>
のようなコードを実行するように、ソースコードを記述すればいい。
207 ⟶ 206行目:
int main()
{
FILE *fp1 = fopen("test1.txt", "w");
if (fp1 == NULL) {
 
perror("ファイルを開けませんでした。\n");
fp1 = fopen("test1.txt", "w");
if ( (fp1 = fopen("test1.txt", "w") ) == NULL) {
printf("ファイルを開けませんでした。\n");
system("pause");// 「続行するには何かキーを押してください . . .」の待機命令
return 1;
}
else {
printf("ファイルをオープンしました。\n");
}
 
char mojiretustring[50];
printf("キーボードから文字列を入力してください。\n");
scanf("%s", &mojiretustring);
printf("入力された文字列は %s です。 \n", mojiretustring);
 
printf("これをファイルに書き込みます。 \n");
 
fprintf(fp1, "%s \n", mojiretustring);
 
fclose(fp1);
printf("ファイルをクローズしました。\n");
system("pause");// 「続行するには何かキーを押してください . . .」の待機命令
282 ⟶ 279行目:
int main()
{
FILE *fp1 = fopen("test1.txt", "r");
if (fp1 == NULL) { // ここを読み取りモード"r"にするのを忘れないように
 
perror("ファイルを開けませんでした。\n");
fp1 = fopen("test1.txt", "r");
if ( (fp1 = fopen("test1.txt", "r") ) == NULL) { // ここを読み取りモード"r"にするのを忘れないように
printf("ファイルを開けませんでした。\n");
system("pause");// 「続行するには何かキーを押してください . . .」の待機命令
return 1;
}
else {
printf("ファイルをオープンしました。\n");
}
304 ⟶ 299行目:
 
fclose(fp1);
printf("ファイルをクローズしました。\n");
341 ⟶ 336行目:
int main()
{
FILE *fp1 = fopen("test1.txt", "r"); // ここを読み取りモード"r"にするのを忘れないように
FILE *fp1;
if (fp1 == NULL) {
 
perror("ファイルを開けませんでした。\n");
fp1 = fopen("test1.txt", "r");
if ((fp1 = fopen("test1.txt", "r")) == NULL) { // ここを読み取りモード"r"にするのを忘れないように
printf("ファイルを開けませんでした。\n");
system("pause");// 「続行するには何かキーを押してください . . .」の待機命令
return 1;
419 ⟶ 412行目:
int main()
{
FILE *fp1 = fopen("test1.txt", "r"); // ここを読み取りモード"r"にするのを忘れないように
FILE *fp1;
if (fp1 == NULL) {
 
perror("ファイルを開けませんでした。\n");
fp1 = fopen("test1.txt", "r");
if ( (fp1 = fopen("test2.txt", "r") ) == NULL) { // ここを読み取りモード"r"にするのを忘れないように
printf("ファイルを開けませんでした。\n");
system("pause");// 「続行するには何かキーを押してください . . .」の待機命令
return 1;
}
else {
printf("ファイルをオープンしました。\n");
}
 
char buffer1[150];
445 ⟶ 435行目:
 
fclose(fp1);
printf("ファイルをクローズしました。\n");
486 ⟶ 476行目:
int main()
{
FILE *fp1 = fopen("test1.txt", "r"); // ここを読み取りモード"r"にするのを忘れないように
FILE *fp1;
if (fp1 == NULL) {
 
perror("ファイルを開けませんでした。\n");
fp1 = fopen("test1.txt", "r");
if ( (fp1 = fopen("test2.txt", "r") ) == NULL) { // ここを読み取りモード"r"にするのを忘れないように
printf("ファイルを開けませんでした。\n");
system("pause");// 「続行するには何かキーを押してください . . .」の待機命令
return 1;
}
else {
printf("ファイルをオープンしました。\n");
}
 
 
char buffer1[150];
513 ⟶ 499行目:
 
fclose(fp1);
printf("ファイルをクローズしました。\n");
553 ⟶ 539行目:
次のようなデータである。
 
ファイル名「sanpurusample.csv」
<pre>
名前,番号
567 ⟶ 553行目:
さて、CSVファイルをカンマで区切って一つずつ読み取りたい場合、
 
fscanf命令関数では <code>%[^ ]</code>という命令関数を使う。<code>%[^ ]</code>は、そのカッコの中にある文字をみつけるまで文字を読み込む。また、そのカッコ内の文字は保持しない。
 
たとえば <code>%[^,]</code>なら、カンマ記号(,)の手前まで文字を読み込み、カンマ記号じたいは除いて読み込む。
585 ⟶ 571行目:
int main()
{
FILE *fp1 = fopen("sample.csv", "r");
if (fp1 == NULL) { // ここを読み取りモード"r"にするのを忘れないように
 
perror("ファイルを開けませんでした。\n");
fp1 = fopen("sanpuru.csv", "r");
if ( (fp1 = fopen("sanpuru.csv", "r") ) == NULL) { // ここを読み取りモード"r"にするのを忘れないように
printf("ファイルを開けませんでした。\n");
//system("pause");// 「続行するには何かキーを押してください . . .」の待機命令
return 1;
}
else {
printf("ファイルをオープンしました。\n");
}
612 ⟶ 596行目:
 
fclose(fp1);
printf("ファイルをクローズしました。\n");
return 0;
639 ⟶ 622行目:
 
 
なので、CSVの読み取りファイルをC言語で作る場合、fgets命令関数を作るほうが良い。
 
 
しかし、fgets には、<code>%[^,]</code> のようなカンマで読み込み中止命令関数をする都合いい機能が無い。
 
 
なので、いったんfgetsで、その行の全体を読み込んだ後に、<code> strtok </code> という別の命令関数を使うのが良い。
 
 
<code> strtok </code> 命令関数とは、指定した文字列の直前までを読み込み機能のある命令関数なので、この命令関数でカンマ文字まで読み込むことを命令関数すればいい。<code> strtok </code> では、指定した文字列じたいは読み込まない。
 
なお、 <code> strtok </code> 命令関数を使うためには <code> #include <string.h> </code> が必要である。
 
書式は
<syntaxhighlight lang="C">
strtok(分解する文字列のある変数,"区切り文字にしたい文字")
</syntaxhighlight>
のようになる。
660 ⟶ 643行目:
CSVファイルを読み込ませたいなら、区切り文字にしたいのはカンマ記号なので、
<syntaxhighlight lang="C">
strtok(分解する文字列のある変数,",")
</syntaxhighlight>
となる。
666 ⟶ 649行目:
なお、<code> strtok </code> の第一引数を「NULL」にすると、前の文字の続きから読み込む。つまり
<syntaxhighlight lang="C">
strtok(NULL,"区切り文字にしたい文字")
</syntaxhighlight>
にすると、前の文字の続きから次ぎの区切り文字まで(その行に残りの区切り文字がない場合には行末まで)を読み込む。
682 ⟶ 665行目:
int main()
{
FILE *fp1 = fopen("sample.csv", "r"); // ここを読み取りモード"r"にするのを忘れないように
FILE *fp1;
if (fp1 == NULL) {
 
perror("ファイルを開けませんでした。\n");
fp1 = fopen("sanpuru.csv", "r");
if ( (fp1 = fopen("sanpuru.csv", "r") ) == NULL) { // ここを読み取りモード"r"にするのを忘れないように
printf("ファイルを開けませんでした。\n");
system("pause");// 「続行するには何かキーを押してください . . .」の待機命令
return 1;
}
else {
printf("ファイルをオープンしました。\n");
}
733 ⟶ 714行目:
 
fclose(fp1);
printf("ファイルをクローズしました。\n");
789 ⟶ 770行目:
int main()
{
FILE* fp1 = fopen("sample.csv", "r");
if (fp1 == NULL) { // ここを読み取りモード"r"にするのを忘れないように
perror("ファイルを開けませんでした。\n");
fp1 = fopen("sanpuru.csv", "r");
 
if ((fp1 = fopen("sanpuru.csv", "r")) == NULL) { // ここを読み取りモード"r"にするのを忘れないように
printf("ファイルを開けませんでした。\n");
system("pause");// 「続行するには何かキーを押してください . . .」の待機命令
return 1;
810 ⟶ 788行目:
char str[150];
};
 
struct seisekihyou row[20]; // 構造体配列の宣言
 
917 ⟶ 895行目:
int main()
{
FILE *fp1 = fopen("test1.txt", "r"); // ここを読み取りモード"r"にするのを忘れないように
FILE *fp1;
if (fp1 == NULL) {
 
perror("ファイルを開けませんでした。\n");
fp1 = fopen("test1.txt", "r");
if ( (fp1 = fopen("test1.txt", "r") ) == NULL) { // ここを読み取りモード"r"にするのを忘れないように
printf("ファイルを開けませんでした。\n");
system("pause");// 「続行するには何かキーを押してください . . .」の待機命令
return 1;
}
else {
printf("ファイルをオープンしました。\n");
}
939 ⟶ 915行目:
 
fclose(fp1);
printf("ファイルをクローズしました。\n");
963 ⟶ 939行目:
int main()
{
FILE* fp1 = fopen("test1.txt", "r"); // ここを読み取りモード"r"にするのを忘れないように
FILE* fp1;
if (fp1 == NULL) {
 
perror("ファイルを開けませんでした。\n");
fp1 = fopen("test1.txt", "r");
if ((fp1 = fopen("test1.txt", "r")) == NULL) { // ここを読み取りモード"r"にするのを忘れないように
printf("ファイルを開けませんでした。\n");
//fclose(fp1); // ここでクローズするとエラー
1,002 ⟶ 976行目:
<syntaxhighlight lang="C">
fp1 = fopen("test1.txt", "r");
if ((fp1 = fopen("test1.txt", "r")) == NULL) {
printfperror("ファイルを開けませんでした。\n");
fclose(fp1); // コメントアウトしないとエラーになる
1,033 ⟶ 1,007行目:
#include <stdlib.h> // Windowsでは「続行するには何かキーを押してください . . .」を表示するのに必要だった。
 
// #pragma warning(disable:4996) // Linux なので不要だし、あ。未知の pragma は無視されとエラー原因にでコメント化は必要
 
int main()
{
FILE* fp1 = fopen("SettingFile.txt", "r"); // まず、読み込みモードで開く。
FILE* fp1;
{
fp1 = fopen("SettingFile.txt", "r"); // まず、読み込みモードで開く。
 
if ( (fp1 = fopen("SettingFile.txt", "r") ) == NULL) { // 読み込みが失敗(NULL)の場合のブロック
printfperror("ファイルを開けませんでした。\n");
fclose(fp1);
}
1,056 ⟶ 1,028行目:
//system("pause");// 「続行するには何かキーを押してください . . .」の待機命令
return 0;
}
}
</syntaxhighlight>
 
 
いっぽう、次のように改善して、読み込み失敗後のクローズ命令関数を除去すれば、セグメンテーションホルトにならず、実行できます。
 
(Fedora 31 で確認ずみ)
1,073 ⟶ 1,044行目:
int main()
{
FILE* = fopen("SettingFile.txt", "r"); // まず、読み込みモードで開く。
FILE* fp1;
{
fp1 = fopen("SettingFile.txt", "r"); // まず、読み込みモードで開く。
 
if ( (fp1 = fopen("SettingFile.txt", "r") ) == NULL) { // 読み込みが失敗(NULL)の場合のブロック
printf("ファイルを開けませんでした。\n");
 
if (fp1 == NULL) { // 読み込みが失敗(NULL)の場合のブロック
perror("ファイルを開けませんでした。\n");
}
else {
1,092 ⟶ 1,060行目:
//system("pause");// 「続行するには何かキーを押してください . . .」の待機命令
return 0;
}
}
</syntaxhighlight>
1,148 ⟶ 1,115行目:
この理由は、おそらく、読み込みに失敗した場合は、そもそも何もオープンしてないと判断なされるので、なので、失敗したぶんの <code> fopen </code> の1回ぶんは、カウントされないからです。
 
ただし、書き込みは通常、対象ファイルの有無に関係なく成功するので(対象ファイルの無い場合には自動作成するので)、なので、書き込みの命令関数のさいにファイルがオープンされるので、その1回ぶんのクローズは必要になります。
 
 
 
なので、上記のコードの直後に、もし <code> fgets </code> などの読み込み命令関数をしても、エラーになって、読み込みできないです。
 
上記のコードのあとに読み込みをしたい場合、再度 <code> fopen </code> の読み込みモードで開いてからでないと、 <code> fgets </code> などの読み込み命令関数は実行できないのです。
 
 
1,172 ⟶ 1,139行目:
int main()
{
FILE* fp1 = fopen("SettingFile.txt", "r"); // まず、読み込みモードで開く。
FILE* fp1;
{
fp1 = fopen("SettingFile.txt", "r"); // まず、読み込みモードで開く。
 
if ( (fp1 = fopen("SettingFile.txt", "r") ) == NULL) { // 読み込みが失敗(NULL)の場合のブロック
fp1 = fopen("SettingFile.txt", "w"); // 同名のファイルをそのまま fopen で開いてもいい
 
printf("ファイルが存在しないので作成しています。ファイル名: SettingFile.txt \n");
<!--ここにも fp1 NULLを fprintf(fp1, "書き込みでないかのテストが必要 \n");-->
 
printf("ファイルが存在しないので作成しています。ファイル名: SettingFile.txt \n");
fprintf(fp1, "書き込みテスト \n");
}
else {
printf("ファイルはすでに存在しています。 \n");
}
fclose(fp1);
1,191 ⟶ 1,159行目:
//system("pause");// 「続行するには何かキーを押してください . . .」の待機命令
return 0;
}
}
</syntaxhighlight>
1,198 ⟶ 1,165行目:
;実行例 (ファイルのない場合)
<pre>
ファイルが存在しないので作成しています。ファイル名: SettingFile.txt
ファイルを閉じました。
終了しています。
</pre>
 
1,206 ⟶ 1,173行目:
;実行例 (ファイルのある場合)
<pre>
ファイルはすでに存在しています。
ファイルを閉じました。
終了しています。
</pre>
 
;SettingFile.txtの中身
<pre>
書き込みテスト
</pre>
 
1,222 ⟶ 1,189行目:
いっぽう、もし <code>fclose(fp1);</code> を2回使っても、コンパイルは失敗してエラーになり、下記のように途中でエラーメッセージが表示されてしまい中断します。
<pre>
ファイルはすでに存在しています。
free(): double free detected in tcache 2
Aborted (コアダンプ)
1,258 ⟶ 1,225行目:
int main()
{
FILE *fp1 = fopen("totyuu.txt", "r"); // ここを読み取りモード"r"にするのを忘れないように
FILE *fp1;
 
if (fp1 == fopen("totyuu.txt", "r"NULL); {
perror("ファイルを開けませんでした。\n");
if ( (fp1 = fopen("totyuu.txt", "r") ) == NULL) { // ここを読み取りモード"r"にするのを忘れないように
printf("ファイルを開けませんでした。\n");
//system("pause");// 「続行するには何かキーを押してください . . .」の待機命令
return 1;
}
else {
printf("ファイルをオープンしました。\n");
}
1,304 ⟶ 1,270行目:
 
fclose(fp1);
printf("ファイルをクローズしました。\n");
1,328 ⟶ 1,294行目:
 
== if文との組み合わせ ==
fgets命令関数やstrtok命令関数などで読み取った文字列をもとに、if文などの条件分岐命令関数と組み合わせる際、標準C言語のif文では、文字列にはイコール記号<code>==</code> など算術的な記号での比較ができないです。
 
これはつまり、Linux標準のGCCでは、文字列の比較にイコール記号が使えないという事です。
1,403 ⟶ 1,369行目:
int main()
{
FILE *fp1 = fopen("test1.txt", "r"); // ここを読み取りモード"r"にするのを忘れないように
FILE *fp1;
if (fp1 == NULL) {
 
perror("ファイルを開けませんでした。\n");
fp1 = fopen("test1.txt", "r");
if ((fp1 = fopen("test1.txt", "r")) == NULL) { // ここを読み取りモード"r"にするのを忘れないように
printf("ファイルを開けませんでした。\n");
system("pause");// 「続行するには何かキーを押してください . . .」の待機命令
return 1;
1,481 ⟶ 1,445行目:
setlocale(LC_ALL, "ja_JP.UTF-8"); //ロケール(地域)を設定する。
 
FILE* fp1 = fopen( "test1.txt", "r, ccs = UTF-8"); // ここを読み取りモード"r"にするのを忘れないように
FILE* fp1;
if (fp1 == NULL) {
 
fp1 = fopen( "test1.txt", "r, ccs = UTF-8");
if ((fp1 = fopen("test1.txt", "r")) == NULL) { // ここを読み取りモード"r"にするのを忘れないように
wprintf(L"ファイルを開けませんでした。\n");
return 1;
1,558 ⟶ 1,520行目:
_wsetlocale(LC_ALL, L""); //ロケール(地域)を設定する。
 
FILE* fp1 = _wfopen( L"test1.txt", L"r, ccs = UNICODE");
FILE* fp1;
if (fp1 == NULL) { // ここを読み取りモード"r"にするのを忘れないように
 
fp1 = _wfopen( L"test1.txt", L"r, ccs = UNICODE");
if ((fp1 = _wfopen(L"test1.txt", L"r")) == NULL) { // ここを読み取りモード"r"にするのを忘れないように
wprintf(L"ファイルを開けませんでした。\n");
perror(0);
system("pause"); // 「続行するには何かキーを押してください . . .」の待機命令
return 1;