C言語/ファイル入出力

まず、ファイルを作成したり読書きしたりするための関数を使うには、 ヘッダー<stdio.h>をインクルードする。
Visual Cでは、標準設定ではfopen関数などの標準入出力関数の使用が禁止されており、そのまま上記のコードをコンパイルすると、
のような警告を表示し、コンパイルが完了しない。
このような禁止されている関数の使用を許可するために、#pragma warning(disable:4996)を加え、コンパイラの警告を無効化する(メッセージ通り fopen を fopen_s で置換えても良いが、fopen_s を用意している処理系は稀で、GCCもclangもサポートしておらず、環境を選ばずコンパイルするためには #ifdef _MSC_VER などでプリプロセッサーで条件コンパイルする方法があるが、コードの見通しが悪くなる上バッファサイズの計算のケアなどを見落としがちなので、警告を無効化することを選択した)。
#pragma warning(disable:4996) が、fopenなどの関数の使用を許可するプラグマです。これは、stdafx.h など一連のWindows用ヘッダのインクルードの後に指令する必要がある。
fopen は、モードを "w" で開いた場合、もし対象のファイルが存在しないときは、その名のファイルを新規作成する。
なお、fopenのモードは、
がある。
なお、一般にファイル操作のプログラミングでは、ファイルのオープンに失敗した場合を想定して、そのような処理を書く必要がある。
fclose は、ファイルを閉じる関数であり、書き込みモードであればバッファキャッシュをクリアしてから、読み出しモードであればその場でシステムにファイルをディスクリプターを返却(closeシステムコール)する。
読者は、ソースファイルのあるフォルダを確認してみて、"test1.txt"というファイルが作られている事を確認しよう。
ファイルを作成したりオープンしただけでは、まだ何も読み込まれないし、何も書き込まれない。
ファイルに何かデータを書き込むには、 fprintf という関数を使う。
fprintf の冒頭のfは、ファイル関係であることをあらわしている。最後のfは、もともとC言語には「printf」という関数があり、(最後のfは)Format の f。
さて、windowsの設定では、ある設定の場合では fopen と同様に fprintf が初期状態では使えない場合があるので、
で指令しておく必要の生じる場合もある。
インクルードすべきヘッダについては、標準出力のヘッダにあるので、
でインクルードすれば充分です。
さて fprintf で書き込むデータの形式は、変数でもいいし、書き込みたいテキストを直接指定することもできる。
また、もし 変数を書き込む場合なら、変数の型は、文字列型でもいいし、数値型でもいい。
ただし、書き込まれた先のテキストファイルでは、書かれたテキストの由来が何かは、もはや不明になる。つまり、書きこまれた先のテキストファイルでは、書かれた文字列は、すべて単なるテキスト文字の集まりとして扱われる。
さて、たとえばファイルに「test」とだけ書き込みたいなら、 fprintf(fp, "test \n"); というふうに入力すればいい。
何か変数をファイルに書き込みたい場合、たとえば整数型の変数だとして、変数名が「var」なら、それを書き込むには、あらかじめソースコードで
のように、どこかで宣言しておく。
そのあと、プログラム中にて変数「var」に目的の値を入力させるようにしておく。
そのあと、プログラムがコード内でのファイル書き込み実行場所にて
のようなコードを実行するように、ソースコードを記述すればいい。

ファイル書き込むのできる変数の型は、数値型も文字列型でも可能です。
次のコード例として、文字列型の変数をファイルに書き込むコード例を示す。
このようにして、ファイルへの出力が出来る。

次に、ファイルからの読み取りをする方法を学ぼう。
ファイルからの読み取りには、まずfopenの際にモードを"r"にする。(英語で、本や文章などを読むことを read (リード)という。その read リードの頭文字 r のこと。)
読み取りの際の関数には fgets や fscanf を使えばいい。
fscanf や fgetsは、ファイルから1行ずつ、読み取ることができる。
なお、 fscanf や fgets の違いは、
です。fscanfのほうが、空白文字(半角スペース)があると読み込みを中断します。 くわしくは、あとの章で説明します。
とりあえず、ファイル読み取りの具体例を調べていきましょう。
あらかじめ、テキストファイル test1.txt に、なんらかの文字列を書いておこう。
一例として、
と書いたとする。

コード例を示す。

このように、最初の1行(aaaaaaaaaaa)だけが読み取りされている。
2行目以降も読み取りたい場合には、その行数だけ、下記のように繰り返しfscanfを使う必要がある。

fscanf や fgets の違いは、大まかには
です。fscanfのほうが、空白文字(半角スペース)があると読み込みを中断します。fgetsのほうが、改行を読み込みます。
論より証拠で、実際にコードを実行して比べてみましょう。
まず、読み込み対象のテキストファイル test2.txtとして、
のように、空白文字(半角スペース)を入れてみましょう。rt と aaaaaaaaaaa のあいだに半角スペースが一文字あります。

そしてコードは、(Fedora30 の gccコンパイルで確認)
fgetsのほうは
このコードでは、printf の%sの後ろには改行文字(\n)をつけていません。なぜなら、fgets ではファイル読み込み時に改行文字も一緒に読み込むからです。

です。



いっぽう、fscanf のほうは
です。
このように、fcanf のほうでは、空白文字があるので、最初の1回目のfscanfでは、「rt」までしか読み込みません。
しかも改行を読み込まないので、 「rt」と「2行目に書いてある文字列」が続いて
と表示されてしまっています。
そして、2回目の fscanf で「aaaaaaaaaaa」を読み込んでいます。
CSVファイルという、カンマで区切った形式のファイルがある。
たとえば、 次のようなデータです。
ファイル名「sample.csv」
なお、表計算ソフト( libreOffice の Calc など)で、表計算ファイルをCSVファイルに変換することができる。 表計算ソフトのそれぞれのセルにデータを入力したあと、表計算ファイルを「名前をつけて保存」するときに、ファイル形式を選択する欄があるので、その欄でCSV形式を選べばいい。
さて、CSVファイルをカンマで区切って一つずつ読み取りたい場合、
fscanf関数では %[^ ]という関数を使う。%[^ ]は、そのカッコの中にある文字をみつけるまで文字を読み込む。また、そのカッコ内の文字は保持しない。
たとえば %[^,]なら、カンマ記号(,)の手前まで文字を読み込み、カンマ記号じたいは除いて読み込む。
なので、%[^,]なら、結果的にカンマ文字を区切り文字として扱うことになる。
CSV読み取りファイルのプログラム例は、たとえば次のようなファイルになる。
(Fedora30 で確認)
実行結果 (Fedora30 で確認)

さきほどの章では、説明の都合上、fscanfを使ったが、じつは、fscanfでCSVファイルを作るのは、あまり実用的ではない。
なぜなら、まず空白文字(半角スペース)で読み込みを停止してしまうので、半角スペースを含む文字列の読み込みが出来無いからです。
なので、CSVの読み取りファイルをC言語で作る場合、fgets関数を作るほうが良い。
しかし、fgets には、%[^,] のようなカンマで読み込み中止関数をする都合いい機能が無い。
なので、いったんfgetsで、その行の全体を読み込んだ後に、 strtok という別の関数を使うのが良い。
strtok 関数とは、指定した文字列の直前までを読み込み機能のある関数なので、この関数でカンマ文字まで読み込むことを関数すればいい。 strtok では、指定した文字列じたいは読み込まない。
なお、 strtok 関数を使うためには #include <string.h> が必要です。
書式は
のようになる。
CSVファイルを読み込ませたいなら、区切り文字にしたいのはカンマ記号なので、
となる。
なお、 strtok の第一引数を「NULL」にすると、前の文字の続きから読み込む。つまり
にすると、前の文字の続きから次ぎの区切り文字まで(その行に残りの区切り文字がない場合には行末まで)を読み込む。
コードは下記のようになる。
(Fedora30 で確認)

上記のように、たしかに、読み取った文字列を分解している。

さきほどの例では、説明の単純化のため、たったの各行あたり、要素数はたった2個だった。
しかし、実際の場合は、もっと要素数が多い。
たとえば、
のように、各行あたり4個のデータを読み込む場合を考えよう。


前の節では、単純化のために配列や構造体やfor文は用いなかったが、実務では用いたほうがイイだろう。
各行の最初の項目だけ strncpy(row[0].str, strtok(buffer1, ","), 150); のようにstrtokの第一引数を指定する必要がある。
いっぽう、strncpy(row[temp].str, strtok(NULL, ","), 150); のようにstrtokの第一引数がnullなら、strtokは以前の区切り文字から次の区切り文字までを抜き取るだけなので、2番目の項目からはfor文で使いまわしができる。
fgets関数やstrtok関数などで読み取った文字列をもとに、if文などの条件分岐関数と組み合わせる際、標準C言語のif文では、文字列にはイコール記号== など算術的な記号での比較ができません。
これはつまり、Cでは、文字列の比較にイコール記号が使えないという事です。
標準C言語では、文字列どうしの比較をする際、 strcmp という関数を使います。
strcmpについて詳しくは『C言語/制御文』をお読みください。

一例として、Fedora28でファイル入出力を実行する場合のコードと実行結果の例を下記にしめします。
テキストファイルの文字コードはUTF-8です。Fedoraの標準の文字コードのままです。テキストエディタにはGedit(ジーエディット)を用いている。GeditはFedora28で標準に付属してくるテキストエディタです。
Fedora28の場合、読み取り対象のテキストファイルの文字コードを何に変えても、コマンドプロンプトで正しく表示される。
なお、Geditの変換可能な文字コードの一覧に「日本語 (CP932)」 というのがあるが、これがマイクロソフト Shift-JIS のことである(つまり マイクロソフト自称「ANSI」)。もし、マイクロソフト自称ANSIとLINUX用テキストファイルを共通化したい場合、 CP932 を選べばいい。
とはいえ、最近のWindows用テキストエディタでは(Linuxでは標準的に採用されている)UTF-8も表示できるのが一般的なので、特に文字化けなどの起きないかぎり、むりに文字コードを変える必要は無い。

C言語のプログラムで、バイナリーファイルの読み書きをしたい場合、
fopen の引数で「r」(読み込み)とか「w」(書き込み)とか編集モードの指定がありますが、それに「b」をつけます。つまり「rb」や「wb」などの引数になります。
また、書き込む関数には fwrite を使う必要があります。 いっぽう、 fprintf では、テキストファイルに自動的に変換してしまいます。
たとえば下記のようになります。
書き込みできたか否かを確認するには、バイナリエディタ(あるいは「16進エディタ」などと言われる)で確認してください。
0x42 などの冒頭の 0x は16進数であることを表す。バイナリーファイルの読み書きに限らず、一般にC言語で16進数をあつかう場合は、16進数である数に接頭辞 0x をつけて区別する。数値 0~9 までは0x をつけなくても十進数と同じなので省略できる。
バイナリエディタで読み込み
と書き込まれていることが確認できれば成功です。

バイナリファイルの読み取りには fread を使うことがある。
いきなりバイナリファイルを読み取るのは初心者には難しいので、まずテキストファイルを読み取る実験をしてみよう。
以前に作った test1.txt ファイルを読み取るとしよう。
test1.txt
コード例


では、バイナリーデーターを読み取ろう。
コード例