【C言語】read()システムコールを完全マスターする総合ガイド――構文・使い方・エラー処理を徹底解説
C言語にはファイルを読み込むための関数が複数用意されており、最もよく使われるのがread()とfread()です。また、getchar()などの関数を使えば、ファイルを1文字ずつ読み込むことも可能です。
ファイル操作関数の呼び出し方法や入出力引数などを正しく理解しておくことは非常に重要です。これらは、プログラマがシステム上に保存された情報を自由に扱うための基本となるリソースだからです。
この記事では、read()関数を使ってファイルを読み込む方法を詳しく解説します。
read()関数の構文、呼び出し方法、入出力引数、それぞれが受け付けるデータ型、そして正しい宣言方法まで、あらゆる側面を説明します。
その後、実際のサンプルコードを通して、学んだ内容を実践に応用していきます。
さらに、read()関数の使用時に発生しうるあらゆるエラーと、その検出・識別方法をまとめた特別なセクションも用意しました。万が一エラーが発生しても、迅速に対処するためのテクニックを身につけられます。
C言語におけるread()関数の構文
size_t read(int fd, void *buf, size_t count);
read()関数の詳細
read()関数は、第1引数「fd」で指定されたファイルディスクリプタに対応する、開かれたファイルの内容を読み込みます。この関数は、ファイルの内容を「buf」が指すバッファへ、「count」で指定したバイト数だけ読み込んでコピーします。「fd」識別子は整数値であり、open()関数でファイルを開いた際の戻り値として取得できます。
read()関数が正常に完了した場合は、読み込んだバイト数が返されます。戻り値が0の場合はファイルの終端まで読み込んだことを意味し、-1の場合はエラーが発生したことを示します。
具体的なエラーの内容は、グローバル変数「errno」からエラーコードを取得することで特定できます。記事の後半では、read()関数の使用時に発生しうるエラーと、それらを検出・識別するための手法について詳しく解説します。
read()関数は「unistd.h」ヘッダーで宣言されています。使用する際は、次のようにコードにこのヘッダーをインクルードしてください。
#include <unistd.h>
read()関数でファイルを読み込む方法
ここでは、open()関数とread()関数を組み合わせてファイルを開き、読み込む方法を実際の例で説明します。
まず準備として、Linuxのファイルマネージャーから「example.txt」という名前のテキストファイルを作成し、「Documents」ディレクトリに保存しておきました。ファイルの内容には、この記事の最初の段落を書き込んでいます。
ファイルを読み込むコードを作成する第一歩は、必要なヘッダーをインクルードし、空の戻り値を返すmain()関数を定義することです。main()関数内では、ファイルディスクリプタとして機能する整数型の「fd」、read()で読み込んだ情報を格納する1024文字のバッファ「buff」、そして読み込みたいファイルのパスと名前を格納する配列「path」を定義します。
必要な変数を定義したら、open()関数を呼び出してファイルを開きます。第1引数にはパスとファイル名を格納したpath配列を渡し、第2引数には読み取り専用を意味するO_RDONLYフラグを指定します。戻り値は、open()が返すディスクリプタを受け取るための整数型変数「fd」に代入し、以降のファイル読み込みに使用します。
ファイルを開けたら、read()関数を呼び出して内容を読み込みます。第1引数にはopen()関数が返した「fd」ディスクリプタを渡し、第2引数には読み込んだ内容を格納する「buff」バッファへのポインタを、最後にバッファのサイズ(ここでは1024バイト)を指定します。
その後、printf()関数を使って、buffに格納された内容をコマンドコンソールに表示します。以下がこの例の完全なコードです。
サンプルコード全文
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
int fd;
char buff[1024];
char path[] = "Documents/example.txt";
fd = open(path, O_RDONLY);
read(fd, buff, 1024);
printf("\n\n%s\n\n", buff);
}
下の図は、このコードのコンパイルと実行結果です。ご覧のとおり、read()が「example.txt」ファイルの全内容を「buff」に格納し、printf()関数がその内容をコマンドコンソールに出力しています。

read()関数使用時のエラーを検出・識別する方法
read()の使用中には、さまざまなエラーが発生する可能性があります。エラーが起きた場合、この関数は-1を返します。
エラーの有無を確認する最も簡単な方法は、戻り値が-1であるかどうかを条件とする「if」文を使うことです。それでは、この方法でエラーを判定する方法を見てみましょう。
int n;
n = read(fd, buff, 1024);
if (n == -1) {
printf("An error occurred while trying to read the file.");
}
read()関数がエラーで戻ってきた場合は、「if」文の中の処理に移行し、"An error occurred while trying to read the file"(ファイルの読み込み中にエラーが発生しました)というメッセージが出力されます。
エラーが発生すると、「errno.h」ヘッダーで定義されているグローバル変数「errno」に、エラーを表す数値コードが自動的に格納されます。このコードを参照することで、発生したエラーの種類を特定できます。
以下は、read()関数が生成しうる主なエラーのうち「errno.h」ヘッダーで定義されているものを、各エラーの簡単な説明と対応する整数値とともにまとめた一覧表です。
| 定義 | errnoの値 | エラーの意味 |
|---|---|---|
| EAGAIN | 11 | 再試行(リソースが一時的に利用不可) |
| EBADF | 9 | 不正なファイルディスクリプタ |
| EDESTADDRREQ | 89 | 宛先アドレスが必要 |
| EFAULT | 14 | 不正なアドレス(バッファが不正な領域を指している) |
| EFBIG | 27 | ファイルが大きすぎる |
| EINTR | 4 | システムコールがシグナルによって中断された |
| EINVAL | 22 | 無効な引数 |
| EIO | 5 | 入出力(I/O)エラー |
| EPERM | 1 | 操作が許可されていない |
エラーを特定する最も簡単な方法は、「errno」変数をswitch文の分岐条件にし、caseごとに各エラーの定義を割り当てて処理を振り分けることです。
次に、無効なディスクリプタ(99999999)を渡して意図的にエラーを発生させる例を見てみましょう。エラーの有無の判定には、前のコード断片で紹介した「if」条件を使用し、エラーの種類の特定には、この関数が生成しうる3つの代表的なエラーをcaseに持つswitch文を使用します。
errnoによるエラー特定のサンプルコード
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
int main()
{
int fd, n;
char buff[1024];
char path[] = "Documents/example.txt";
fd = open(path, O_RDONLY);
n = read(99999999, buff, 1024);
if (n == -1) {
switch (errno) {
case EBADF:
printf("Bad file number. Error: %i\n", errno);
break;
case EINVAL:
printf("Invalid argument. Error: %i\n", errno);
break;
case EIO:
printf("I/O error. Error: %i\n", errno);
break;
}
}
}
下の図のとおり、無効なディスクリプタを入力引数として渡すとread()関数はエラーを返し、分岐条件に使用した「errno」変数から取得した値によってEBADFのcaseに入り、エラーの種類を正確に特定できています。

まとめ
この記事では、C言語でファイルを読み込むために最も広く使われる関数のひとつであるread()関数の使い方を解説しました。
まず構文を確認し、動作の仕組み、入出力引数、それぞれのデータ型について理論的な説明を行いました。その後、open()関数とread()関数を組み合わせてファイルを開き、読み込む方法を、コードと実行結果の画像を使った実践的な例で紹介しました。
さらに、この関数の使用時に起こりうるエラーへ的確にトラブルシューティングできるよう、C言語が提供するエラーの検出・識別方法を解説する特別なセクションも設けました。本記事の内容を活かして、堅牢なファイル読み込み処理を実装してみてください。
-
C/C++で実装するBarabási-Albertグラフ(スケールフリーモデル向け)とは?
バラバーシ・アルバート(BA)モデルとは バラバーシ・アルバート(BA)モデルは、スケールフリーネットワークを生成するために提案された複数のモデルの一つとして扱われています。このモデルは、「成長(growth)」と「選択的接着(preferential attachment)」という2つの重要な概念を組み合わせたものです。どちらの概念も、現実のネットワークに広く見られる性質です。 成長(Growth) 「成長」とは、ネットワーク内のノード数が時間の経過とともに増加していくことを意味します。 選択的接着(Preferential Attachment) 「選択的接着」とは、すでに多くの接続を持つ
-
C言語で連結リストの末尾からn番目のノードを取得するプログラム
n個のノードからなる連結リストが与えられたとき、その末尾からn番目のノードを出力するのが本記事の目的です。プログラムはリスト内のノードの並び順を変更してはならず、あくまで末尾から数えてn番目に位置するノードの値を表示するだけでなければなりません。具体例入力 -: 10 20 30 40 50 60 N = 3 出力 -: 40上記の例では、先頭ノードから順に「count − n」個目までのノード(10, 20, 30, 40, 50, 60)を走査し、末尾から3番目のノードとして 40 が得られます。効率的なアプローチリスト全体を最後まで走査しなくても、以下の手順で目的のノードを見つけられ