標準入出力 (Standard I/O)
標準入出力は、C言語で最も基本的なファイル操作です。`stdin`(標準入力)、`stdout`(標準出力)、`stderr`(標準エラー出力)の3つのストリームが事前に定義されており、これらはプログラムの起動時に自動的に開かれます。これらのストリームは、`FILE`型のポインタを通じて操作されます。
ファイルのオープンとクローズ
ファイルを操作するには、まず`fopen`関数を使用してファイルを開き、操作が終わったら`fclose`関数で閉じる必要があります。`fopen`は、ファイル名とモード(読み取り、書き込みなど)を指定してファイルポインタを返します。
#include <stdio.h>
int main() {
// ファイルポインタの宣言
FILE *file_handle = NULL;
// 書き込みモードでファイルを開く
file_handle = fopen("sample.txt", "w");
if (file_handle == NULL) {
perror("ファイルを開けませんでした");
return -1;
}
printf("ファイルを開くことに成功しました
");
// ファイルを閉じる
fclose(file_handle);
return 0;
}
文字単位の読み書き
`fputc`関数は、指定した文字をファイルに書き込み、`fgetc`関数はファイルから1文字を読み込みます。これらの関数は、ファイルの終わり(EOF)に達したかどうかを確認するために頻繁に使用されます。
#include <stdio.h>
int main() {
FILE *file_handle = NULL;
// 書き込みモードでファイルを開く
file_handle = fopen("data.txt", "w");
if (file_handle == NULL) {
perror("ファイルを開けませんでした");
return -1;
}
// ファイルにデータを書き込む
fputc('H', file_handle);
fputc('i', file_handle);
// ファイルを閉じる
fclose(file_handle);
// 読み取りモードでファイルを再オープン
file_handle = fopen("data.txt", "r");
if (file_handle == NULL) {
perror("ファイルを開けませんでした");
return -1;
}
char current_char;
while (1) {
current_char = fgetc(file_handle); // ファイルから1文字読み込む
if (current_char == EOF) { // ファイルの終わりに達した場合
break;
}
printf("%c ", current_char);
}
printf("
");
// ファイルを閉じる
fclose(file_handle);
return 0;
}
文字列の読み書き
複数の文字を一度に扱うには、`fputs`と`fgets`を使用します。`fputs`は文字列をファイルに書き込み、`fgets`はファイルから文字列をバッファに読み込みます。
#include <stdio.h>
int main() {
FILE *file_handle = NULL;
// 書き込みモードでファイルを開く
file_handle = fopen("message.txt", "w");
if (file_handle == NULL) {
perror("ファイルを開けませんでした");
return -1;
}
// ファイルに文字列を書き込む
fputs("こんにちは
", file_handle);
fputs("世界
", file_handle);
// ファイルを閉じる
fclose(file_handle);
// 読み取りモードでファイルを再オープン
file_handle = fopen("message.txt", "r");
if (file_handle == NULL) {
perror("ファイルを開けませんでした");
return -1;
}
char buffer[10]; // 文字列を一時的に保持するバッファ
while (1) {
char *result = fgets(buffer, sizeof(buffer), file_handle); // ファイルから文字列を読み込む
if (result == NULL) {
break;
}
printf("バッファの内容: %s
", buffer);
}
// ファイルを閉じる
fclose(file_handle);
return 0;
}
バイナリデータの読み書き
テキストデータだけでなく、整数や構造体などのバイナリデータを効率的に読み書きするには、`fwrite`と`fread`を使用します。これらの関数は、データのサイズと個数を指定して操作を行います。
#include <stdio.h>
// 構造体の定義
struct Student {
int id;
char name[20];
float grade;
};
int main() {
FILE *file_handle = NULL;
// 書き込みモードでファイルを開く
file_handle = fopen("student.dat", "wb"); // バイナリ書き込みモード
if (file_handle == NULL) {
perror("ファイルを開けませんでした");
return -1;
}
// 構造体の配列を定義
struct Student students[2] = {{101, "田中", 85.5}, {102, "佐藤", 92.0}};
// 構造体の配列をファイルに書き込む
fwrite(students, sizeof(struct Student), 2, file_handle);
// ファイルを閉じる
fclose(file_handle);
// 読み取りモードでファイルを再オープン
file_handle = fopen("student.dat", "rb"); // バイナリ読み取りモード
if (file_handle == NULL) {
perror("ファイルを開けませんでした");
return -1;
}
struct Student read_student;
// ファイルから1つの構造体を読み込む
fread(&read_student, sizeof(struct Student), 1, file_handle);
printf("ID: %d, 名前: %s, 成績: %.2f
", read_student.id, read_student.name, read_student.grade);
// ファイルを閉じる
fclose(file_handle);
return 0;
}
ファイルポインタの操作
`fseek`関数を使用すると、ファイル内の任意の位置にポインタを移動させることができます。これにより、ランダムアクセスが可能になります。
#include <stdio.h>
int main() {
FILE *file_handle = fopen("random_access.txt", "w+");
if (file_handle == NULL) {
perror("ファイルを開けませんでした");
return -1;
}
// データを書き込む
fputs("ABCDEFGHIJKLMNOPQRSTUVWXYZ", file_handle);
// ファイルポインタを先頭に戻す
fseek(file_handle, 0, SEEK_SET);
// 10番目の文字から読み込む
fseek(file_handle, 9, SEEK_SET);
char ch;
while ((ch = fgetc(file_handle)) != EOF) {
putchar(ch);
}
printf("
");
fclose(file_handle);
return 0;
}
ファイル入出力 (File I/O)
ファイル入出力は、より低レベルの操作を提供します。システムコールを直接呼び出すことで、標準入出力よりも細かい制御が可能になります。主にファイル記述子(File Descriptor, fd)を介して操作が行われます。
ファイル記述子の基本操作
`open`関数はファイルを開き、ファイル記述子を返します。`read`と`write`は、ファイル記述子を指定してデータの読み書きを行います。`close`はファイル記述子を閉じます。
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main() {
int file_descriptor;
// 書き込みモードでファイルを開く
file_descriptor = open("low_level.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (file_descriptor == -1) {
perror("ファイルを開けませんでした");
return -1;
}
printf("ファイルを開くことに成功しました。fd=%d
", file_descriptor);
// データを書き込む
const char *data = "これは低レベルI/Oのテストです。";
write(file_descriptor, data, sizeof(data) - 1); // NULL終端文字を除く
// ファイルを閉じる
close(file_descriptor);
return 0;
}
ファイル記述子の複製
`dup`と`dup2`関数は、既存のファイル記述子を複製し、新しいファイル記述子を生成します。これにより、同じファイルに対して複数のストリームを扱うことができます。
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main() {
int original_fd, duplicated_fd;
// ファイルを開く
original_fd = open("duplicate_test.txt", O_RDWR | O_CREAT | O_TRUNC, 0644);
if (original_fd == -1) {
perror("ファイルを開けませんでした");
return -1;
}
// データを書き込む
const char *msg = "オリジナルのメッセージ";
write(original_fd, msg, sizeof(msg) - 1);
// ファイルポインタを先頭に戻す
lseek(original_fd, 0, SEEK_SET);
// ファイル記述子を複製する
duplicated_fd = dup(original_fd);
printf("元のfd: %d, 複製されたfd: %d
", original_fd, duplicated_fd);
// 複製されたfdのポインタを移動させる
lseek(duplicated_fd, 5, SEEK_SET);
// 元のfdから読み込む
char buffer[50];
read(original_fd, buffer, sizeof(buffer) - 1);
buffer[sizeof(buffer) - 1] = '\0'; // NULL終端
printf("読み込んだデータ: %s
", buffer);
close(original_fd);
return 0;
}
ファイルの状態を取得する
`stat`システムコールは、ファイルの属性(サイズ、種類、権限など)を取得するために使用されます。
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "使用法: %s ファイル名
", argv[0]);
return -1;
}
struct stat file_stat;
if (stat(argv[1], &file_stat) == -1) {
perror("statエラー");
return -1;
}
// ファイルの種類を判別する
switch (file_stat.st_mode & S_IFMT) {
case S_IFREG: printf("通常ファイル
"); break;
case S_IFDIR: printf("ディレクトリ
"); break;
case S_IFLNK: printf("シンボリックリンク
"); break;
// ... 他の種類
}
printf("モード: %#o\t サイズ: %ld bytes\t inode: %ld
",
file_stat.st_mode & 0777, file_stat.st_size, file_stat.st_ino);
return 0;
}