C言語の始め方【第8回 ファイル操作】※現役エンジニアによる解説

C言語ファイル操作例 C言語の始め方
本ページは、プロモーションを含んでいる場合があります。
対象C言語初心者
概要C言語のファイルについて記載しています。
筆者Zuu [ ]

C言語歴20年以上。
大手電機メーカーでの製品開発およびマネジメント経験がある現役エンジニアです。
プロジェクトマネージャ、システムアーキテクトなどの資格を保有しています。
システムアーキテクト試験合格証書

はじめに

これまで使用してきた「変数」は、プログラムが動作している間だけ値を保存してくれる機能でした。

実用的なアプリケーションを開発する場合、例えばゲームのセーブデータのように、プログラムが終了しても値を覚えておきたいといったことを考えるようになると思います。

そんな要望を叶えてくれる機能の1つが「ファイル」です。

本記事では、C言語でファイルを扱う方法について記載しています。ファイルの基本から説明していますので、初心者の方でもこのページの内容だけで十分に理解できる構成となっております。

経験者の方で、C言語でのファイル操作だけを手っ取り早く知りたい方は3章まで飛ばしてください。

ファイルとは?

ファイルとは、コンピュータにおけるデータの管理単位の一つで、ストレージ装置(外部記憶装置)などにデータを記録する際に利用者やOSから見て最小の記録単位となるデータのまとまり。

今まで、プログラム内で値を覚えておくためには「変数」を使用してきました。

変数には数値や文字などの値を記憶しておくことができましたが、プログラムが終了すると消えてしまうという特性がありました。これは、変数が「メモリ」と呼ばれる“一時的に”データを保管する装置に保存されるためです。

これに対して、ファイルは「ストレージ」と呼ばれる“永続的に”データを保管する装置に保存します。そのため、プログラムが終了しても値を覚えておくことができるのです。

変数とファイル

身近なファイルの例としては、写真(JPEGファイル)が挙げられます。スマホやデジカメで撮影した画像をファイルとしてストレージに保存しているため、機器の電源を切ってもなくなることはありません

メモリとストレージの違いについては、以下の記事が参考になります。興味がある方は是非ご覧になってください。

メモリとストレージの違い | Crucial Japan
パソコンを稼働するにはメモリとストレージの両方が必要です。メモリとストレージの違いはなんでしょうか?Crucialはメモリとストレージの違いについてご紹介します。

テキストファイルとバイナリファイル

ファイルには様々なものがありますが、大きく分けるとテキストファイルバイナリファイルに分類されます。例えば、上述した写真(JPEGファイル)はバイナリファイルの一種となります。

プログラムからファイルを扱う場合、テキストファイルとバイナリファイルで扱い方が異なることが多いです。使用したいファイルがどちらなのか把握することが先決です。

テキストファイルとバイナリファイルの特徴を以下に示します。

テキストファイル文字だけで構成されているファイルのことです。
Windowsであれば、メモ帳で開いた際に文字が表示されるものという認識で良いと思います。

テキストファイルのイメージ

例:txtファイル、iniファイル、など
バイナリファイル文字も含め、様々なデータから構成されているファイルのことです。
読み取るためには、専用のアプリケーションが必要だと思ってもらって構いません。自作のアプリケーション用のファイルを作成する場合は、バイナリファイルで作ることが多いと思います。

バイナリファイルのイメージ
※データが並んでいますが、テキストファイルと違って、ぱっと見では内容がわかりません。

例:JPEGファイル、docxファイル、xlsxファイル、など

絶対パスと相対パス

次に、「ファイルパス」について理解する必要があります。というのも、プログラムからファイルを操作する場合は、操作するファイルのパスを知っている必要があるからです。

ファイルパスとは、コンピュータ内で特定のファイルの所在を表す文字列のこと。 上位にあるディレクトリを区切り文字を挟んで順に並べて表記する。

つまり、ファイルがどこにあるかを示すものがファイルパスとなります。ファイルの住所のようなものですね。

Windowsであれば、エクスプローラーを使用するとわかりやすいと思います。

ファイルパス

ファイルパスには、絶対パス相対パスがあります。どちらもよく使われるため、ITエンジニアを目指すなら必ず覚えておく必要があります。絶対パスと相対パスの特徴を以下に示します。

絶対パスストレージの最上位の階層から、ファイルがある階層までをすべて記述する表現方法です。上の例で言えば、「C:\work\directory\file.txt」が絶対パスとなります。

階層構造がすべて記述されているのでわかりやすいというメリットがありますが、ディレクトリ(フォルダ)を移動してしまうとパスが変わってしまうため、それを考慮したプログラムを作る必要があるといったデメリットがあります。
相対パス現在の階層(だいたいはプログラムのある階層)から目的のファイルまでを記述する表現方法です。

ぱっと見わかりにくく間違いやすいというデメリットがありますが、うまく使いこなせばディレクトリ(フォルダ)の移動に強いプログラムが作れるといったメリットがあります。

相対パスでは、「.」「..」といった特殊な指定方法を使用します。
「.」:現在の階層を示します。
「..」:1つ上の階層を示します。

例えば、上述した「C:\work\directory」が現在の階層であるとき、file.txtの相対パスは、単に「file.txt」とするか「.\file.txt」とします。
また、「..」は「C:\work」を指すことになります。「..\..」と記述することもでき、この場合は「C:」を指すことになります。

C言語でのファイルの取り扱い方(ファイル操作)

プラグラムからファイルを操作する場合、だいたいどの言語でも以下のような順番になります。

  • ファイルのオープン
  • ファイルの読み書き
  • ファイルのクローズ

では、C言語での例を以下に示します。

ファイルのオープンとクローズ

最初は、ファイルのオープンとクローズです。ファイルを操作しようと思ったら、まず最初にオープンしないといけません。ファイルのクローズし忘れはよくやってしまうミスなので、最初にセットで書いてしまうのが良いでしょう。

FILE* fopen(const char *filename, const char *mode)
第1引数(filename)ファイル名(ファイルパス)
※ファイルパスについては、上述の「絶対パスと相対パス」の項目をご参照ください。
第2引数(mode)モード

  “r”:テキストファイルの読み込み
  “w”:テキストファイルの書き込み
  “rb”:バイナリファイルの読み込み
  “wb”:バイナリファイルの書き込み
  “a”:テキストファイルの追加書き込み
  “ab”:バイナリファイルの追加書き込み

「追加書き込み」とは、ファイルの中身を消さずに、既に書き込んであるデータに続けて書き込むことです。ログファイルなどに使用されます。
戻り値ファイルポインタ
※以降のファイル操作で必要、失敗時はNULL

fopen()の戻り値は必ずNULLチェックしましょう。ファイルパスが誤っていた時など、ファイルがうまく開けなかった場合にはNULLが返ります。この状態で、以降の処理を行ってしまうと、NULLポインタアクセスとなり、プログラムが落ちます。

int fclose(FILE *stream)
第1引数(stream)ファイルポインタ
※fopen()で取得したものを指定します
戻り値処理結果
  正常時:0
  異常時:EOF(負の値)

ファイルの読み書き

続いて、ファイル読み書きです。ファイルのオープンの戻り値で取得したファイルポインタを使用して読み書きします。

テキストファイルとバイナリファイルで使用する関数が異なるので注意が必要です。

ファイルの読み込み

①テキストファイルの場合:
fgets()を使用することで、テキストファイルから1行読み込むことができます。

char* fgets(char *s, int n, FILE *stream)
第1引数(s)文字列を受け取る配列
第2引数(n)第1引数で指定した配列の長さ
第3引数(stream)ファイルポインタ
※fopen()で取得したものを指定します
戻り値成功時:読み込んだ文字列(第1引数で指定した配列)
失敗時:NULL

②バイナリファイルの場合:
fread()を使用することで、バイナリファイルから指定したサイズのデータを読み込むことができます。

size_t fread(void *buf, size_t size, size_t n, FILE *stream)
第1引数(buf)データを受け取る配列
第2引数(size)第1引数で指定した配列の1要素の大きさ(型)
第3引数(n)第1引数で指定した配列の長さ
第4引数(stream)ファイルポインタ
※fopen()で取得したものを指定します
戻り値成功時:読み込んだデータ数
※バイト数ではなく、配列に格納した数です

ファイルの書き込み

①テキストファイルの場合:
fprintf()を使用することで、テキストファイルに文字列を書き込むことができます。

int fprintf(FILE *stream, const char *format, …)
第1引数(stream)書き込む文字列
フォーマット(format)書式
printf()と同じ理解で問題ありません
第3引数以降書式に入力するデータ
printf()と同じ理解で問題ありません
戻り値成功時:文字数
失敗時:負の値

②バイナリファイルの場合:
fwrite()を使用することで、バイナリファイルに指定したサイズのデータを書き込むことができます。

size_t fwrite(const void *buf, size_t size, size_t n, FILE *stream)
第1引数(buf)書き込むデータ配列
第2引数(size)第1引数で指定した配列の1要素の大きさ(型)
第3引数(n)第1引数で指定した配列の長さ
第4引数(stream)ファイルポインタ
※fopen()で取得したものを指定します
戻り値成功時:書き込んだデータ数
※バイト数ではなく、配列の要素数です

その他の操作

今まで紹介した操作に加えて、覚えておきたいファイル操作を紹介します。

ファイルの存在確認

ファイルが存在しているかどうかで処理を分岐させたい場合があります。

例えば、アプリケーションの設定をファイルに保存していて、設定ファイルが存在しない場合はデフォルト設定で起動するといった場合などです。

ファイルのオープンで使用したfopen()関数を使用することで確認可能です。以下のように、正しく開けた場合はファイルが存在、開けなかった場合はファイルが存在しないとすれば良いです。

ファイルの削除

不要になった場合などに、ファイルを削除したい場合があるかと思います。そんな時は、C言語の標準関数としてremove()が用意されているので、これを使用するとお手軽です。

int remove(const char *pathname)
第1引数(*pathname)ファイルパス
戻り値処理結果
  正常時:0
  異常時:-1

C言語でのファイル操作まとめ

「ファイル」の概要およびC言語でのファイル操作について解説しました。

  • ファイルを使用すると、プログラムが終了しても値を覚えておくことができる
  • ファイル操作には、オープン/クローズ読み込み/書き込みなどがある
  • テキストファイルバイナリファイルで使用する関数が変わる
C言語の始め方【第9回 ファイル分割のやり方】※現役エンジニアによる解説
C言語でのファイル分割のやり方について解説。ヘッダファイルを作成し、定数や構造体の定義、関数のプロトタイプ宣言などの「前方宣言」を記述する。「前方宣言」を使いたいファイルで、ヘッダファイルをincludeする。二重include防止も忘れずに。
C言語の始め方(初心者向けC言語講座)
C言語歴20年以上の現役エンジニアがC言語について説明しています。 重要なポイントを学習しやすい構成にしているつもりです。 変数、分岐処理、反復処理、・配列、構造体、関数、ポインタ、ファイルの取り扱い、など。ポインタの理解に主眼を置いています。

コメント

タイトルとURLをコピーしました