[C言語] ファイルを読み込んで配列に値を格納する方法
C言語でファイルからデータを読み込み、配列に格納するには、まずファイルを開く必要があります。これにはfopen
関数を使用します。
次に、fscanf
やfgets
などの関数を用いてファイルからデータを読み取ります。
読み取ったデータは、適切な型の配列に格納します。配列のサイズを超えないように注意が必要です。
最後に、ファイルを閉じるためにfclose
関数を使用します。
この方法を用いることで、テキストファイルやバイナリファイルから効率的にデータを配列に取り込むことができます。
ファイルからのデータ読み込み
ファイルからデータを読み込むことは、C言語プログラミングにおいて非常に重要なスキルです。
ここでは、ファイルからデータを読み込むための基本的な関数とその使い方について解説します。
fscanf関数の基本
fscanf関数
は、ファイルからフォーマットに従ってデータを読み込むための関数です。
以下に基本的な使い方を示します。
#include <stdio.h>
int main() {
FILE *file;
int number;
char name[50];
// ファイルを読み込みモードで開く
file = fopen("data.txt", "r");
if (file == NULL) {
printf("ファイルを開けませんでした。\n");
return 1;
}
// ファイルからデータを読み込む
fscanf(file, "%d %s", &number, name);
// 読み込んだデータを表示
printf("番号: %d, 名前: %s\n", number, name);
// ファイルを閉じる
fclose(file);
return 0;
}
番号: 123, 名前: Tanaka
この例では、data.txt
というファイルから整数と文字列を読み込んでいます。
fscanf
は、フォーマット指定子を使ってデータを読み込むため、データの形式が決まっている場合に便利です。
fgets関数とfgetc関数の違い
fgets
とfgetc
は、どちらもファイルからデータを読み込むための関数ですが、用途が異なります。
関数名 | 説明 |
---|---|
fgets | 1行ずつ文字列として読み込む。改行文字も含まれる。 |
fgetc | 1文字ずつ読み込む。 |
fgets
は、行単位でデータを読み込むため、テキストファイルの内容をそのまま取得したい場合に適しています。
一方、fgetc
は1文字ずつ読み込むため、細かい制御が必要な場合に使用します。
バッファの重要性
バッファは、データを一時的に保存するためのメモリ領域です。
ファイルからデータを読み込む際には、バッファを適切に管理することが重要です。
バッファが小さすぎると、データが途中で切れてしまう可能性があります。
#include <stdio.h>
int main() {
FILE *file;
char buffer[100];
file = fopen("data.txt", "r");
if (file == NULL) {
printf("ファイルを開けませんでした。\n");
return 1;
}
// バッファを使って1行読み込む
while (fgets(buffer, sizeof(buffer), file) != NULL) {
printf("%s", buffer);
}
fclose(file);
return 0;
}
この例では、buffer
という配列を使って1行ずつデータを読み込んでいます。
バッファサイズを適切に設定することで、効率的にデータを処理できます。
エラー処理の方法
ファイル操作では、エラー処理が重要です。
ファイルが存在しない、読み込みに失敗したなどのエラーを適切に処理することで、プログラムの信頼性を高めることができます。
- ファイルが開けない場合:
fopen
の戻り値をチェックし、NULL
であればエラーメッセージを表示します。 - 読み込みエラー:
fscanf
やfgets
の戻り値を確認し、期待したデータが読み込めなかった場合にエラーメッセージを表示します。
例:if (file == NULL) { printf("ファイルを開けませんでした。\n"); }
エラー処理をしっかりと行うことで、予期しない動作を防ぎ、ユーザーに適切なフィードバックを提供できます。
配列へのデータ格納
ファイルから読み込んだデータを配列に格納することは、データを効率的に管理するために重要です。
ここでは、配列の基本的な使い方から、動的配列の利用方法までを解説します。
配列の初期化と宣言
配列は、同じ型のデータを連続して格納するためのデータ構造です。
以下に配列の宣言と初期化の例を示します。
#include <stdio.h>
int main() {
// 整数型の配列を宣言し、初期化
int numbers[5] = {1, 2, 3, 4, 5};
// 配列の要素を表示
for (int i = 0; i < 5; i++) {
printf("%d ", numbers[i]);
}
printf("\n");
return 0;
}
1 2 3 4 5
この例では、5つの整数を格納する配列numbers
を宣言し、初期化しています。
配列の要素はインデックスを使ってアクセスできます。
fscanfを用いたデータの格納
fscanf
を使ってファイルから読み込んだデータを配列に格納する方法を紹介します。
#include <stdio.h>
int main() {
FILE *file;
int numbers[5];
int i = 0;
file = fopen("numbers.txt", "r");
if (file == NULL) {
printf("ファイルを開けませんでした。\n");
return 1;
}
// ファイルからデータを読み込み、配列に格納
while (fscanf(file, "%d", &numbers[i]) != EOF && i < 5) {
i++;
}
fclose(file);
// 配列の要素を表示
for (int j = 0; j < i; j++) {
printf("%d ", numbers[j]);
}
printf("\n");
return 0;
}
10 20 30 40 50
この例では、numbers.txt
から整数を読み込み、配列numbers
に格納しています。
fscanf
を使うことで、フォーマットに従ったデータの読み込みが可能です。
fgetsを用いたデータの格納
fgets
を使ってファイルから読み込んだデータを配列に格納する方法を紹介します。
fgets
は文字列を扱うため、文字列データの読み込みに適しています。
#include <stdio.h>
#include <string.h>
int main() {
FILE *file;
char lines[5][100];
int i = 0;
file = fopen("lines.txt", "r");
if (file == NULL) {
printf("ファイルを開けませんでした。\n");
return 1;
}
// ファイルから1行ずつ読み込み、配列に格納
while (fgets(lines[i], sizeof(lines[i]), file) != NULL && i < 5) {
// 改行文字を削除
lines[i][strcspn(lines[i], "\n")] = '\0';
i++;
}
fclose(file);
// 配列の要素を表示
for (int j = 0; j < i; j++) {
printf("%s\n", lines[j]);
}
return 0;
}
Hello
World
C
Programming
Language
この例では、lines.txt
から1行ずつ文字列を読み込み、配列lines
に格納しています。
fgets
は改行文字も読み込むため、必要に応じて削除します。
動的配列の利用
動的配列は、プログラムの実行時にサイズを決定できる配列です。
malloc関数
を使ってメモリを動的に確保します。
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE *file;
int *numbers;
int size = 5;
int i = 0;
// 動的にメモリを確保
numbers = (int *)malloc(size * sizeof(int));
if (numbers == NULL) {
printf("メモリの確保に失敗しました。\n");
return 1;
}
file = fopen("numbers.txt", "r");
if (file == NULL) {
printf("ファイルを開けませんでした。\n");
free(numbers);
return 1;
}
// ファイルからデータを読み込み、動的配列に格納
while (fscanf(file, "%d", &numbers[i]) != EOF) {
i++;
if (i >= size) {
size *= 2;
numbers = (int *)realloc(numbers, size * sizeof(int));
if (numbers == NULL) {
printf("メモリの再確保に失敗しました。\n");
fclose(file);
return 1;
}
}
}
fclose(file);
// 配列の要素を表示
for (int j = 0; j < i; j++) {
printf("%d ", numbers[j]);
}
printf("\n");
// メモリを解放
free(numbers);
return 0;
}
10 20 30 40 50 60 70 80 90 100
この例では、動的にメモリを確保し、必要に応じてrealloc
でサイズを拡張しています。
動的配列を使うことで、事前に配列のサイズを決める必要がなくなり、柔軟なデータ管理が可能です。
実践例:CSVファイルの読み込み
CSV(Comma-Separated Values)ファイルは、データをカンマで区切って保存する形式で、データの交換や保存に広く利用されています。
ここでは、C言語を用いてCSVファイルを読み込み、データを処理する方法を解説します。
CSVファイルの構造
CSVファイルは、以下のような構造を持っています。
各行がレコードを表し、カンマで区切られた各要素がフィールドを表します。
ID,Name,Age
1,John Doe,30
2,Jane Smith,25
3,Bob Johnson,40
この例では、3つのフィールド(ID、Name、Age)があり、3つのレコードが含まれています。
デリミタの処理方法
CSVファイルの読み込みでは、カンマ,
をデリミタとしてデータを分割する必要があります。
以下の例では、fgets
とstrtok
を使ってデリミタを処理します。
#include <stdio.h>
#include <string.h>
int main() {
FILE *file;
char line[100];
char *token;
file = fopen("data.csv", "r");
if (file == NULL) {
printf("ファイルを開けませんでした。\n");
return 1;
}
// 1行ずつ読み込む
while (fgets(line, sizeof(line), file) != NULL) {
// 改行文字を削除
line[strcspn(line, "\n")] = '\0';
// カンマで分割
token = strtok(line, ",");
while (token != NULL) {
printf("%s ", token);
token = strtok(NULL, ",");
}
printf("\n");
}
fclose(file);
return 0;
}
ID Name Age
1 John Doe 30
2 Jane Smith 25
3 Bob Johnson 40
この例では、strtok関数
を使ってカンマで区切られたデータを分割し、各フィールドを表示しています。
文字列データの扱い
CSVファイルには文字列データが含まれることが多く、これを適切に扱う必要があります。
文字列データはそのまま配列に格納することができます。
#include <stdio.h>
#include <string.h>
int main() {
FILE *file;
char line[100];
char name[50];
file = fopen("data.csv", "r");
if (file == NULL) {
printf("ファイルを開けませんでした。\n");
return 1;
}
// ヘッダー行を読み飛ばす
fgets(line, sizeof(line), file);
// 1行ずつ読み込む
while (fgets(line, sizeof(line), file) != NULL) {
// 改行文字を削除
line[strcspn(line, "\n")] = '\0';
// カンマで分割し、名前を取得
strtok(line, ","); // IDをスキップ
strcpy(name, strtok(NULL, ",")); // 名前をコピー
printf("名前: %s\n", name);
}
fclose(file);
return 0;
}
名前: John Doe
名前: Jane Smith
名前: Bob Johnson
この例では、strtok
を使って名前のフィールドを抽出し、strcpy
で文字列をコピーしています。
数値データの変換
CSVファイルから読み込んだ数値データは、文字列として扱われるため、数値型に変換する必要があります。
atoi
やatof
を使って変換します。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
FILE *file;
char line[100];
int age;
file = fopen("data.csv", "r");
if (file == NULL) {
printf("ファイルを開けませんでした。\n");
return 1;
}
// ヘッダー行を読み飛ばす
fgets(line, sizeof(line), file);
// 1行ずつ読み込む
while (fgets(line, sizeof(line), file) != NULL) {
// 改行文字を削除
line[strcspn(line, "\n")] = '\0';
// カンマで分割し、年齢を取得
strtok(line, ","); // IDをスキップ
strtok(NULL, ","); // 名前をスキップ
age = atoi(strtok(NULL, ",")); // 年齢を整数に変換
printf("年齢: %d\n", age);
}
fclose(file);
return 0;
}
年齢: 30
年齢: 25
年齢: 40
この例では、atoi関数
を使って文字列を整数に変換し、年齢を表示しています。
数値データを適切に変換することで、計算や比較が可能になります。
応用例
ここでは、ファイル操作の応用例として、バイナリファイルの読み込み、構造体を用いたデータ格納、複数ファイルの同時処理について解説します。
これらの技術を活用することで、より高度なデータ処理が可能になります。
バイナリファイルの読み込み
バイナリファイルは、テキストファイルとは異なり、データがバイナリ形式で保存されています。
fread関数
を使ってバイナリデータを読み込みます。
#include <stdio.h>
int main() {
FILE *file;
int numbers[5];
file = fopen("data.bin", "rb");
if (file == NULL) {
printf("ファイルを開けませんでした。\n");
return 1;
}
// バイナリデータを読み込む
fread(numbers, sizeof(int), 5, file);
fclose(file);
// 読み込んだデータを表示
for (int i = 0; i < 5; i++) {
printf("%d ", numbers[i]);
}
printf("\n");
return 0;
}
10 20 30 40 50
この例では、data.bin
というバイナリファイルから整数データを読み込んでいます。
fread
を使うことで、バイナリデータを効率的に読み込むことができます。
構造体を用いたデータ格納
構造体を使うことで、異なる型のデータをまとめて管理することができます。
ファイルから読み込んだデータを構造体に格納する方法を紹介します。
#include <stdio.h>
#include <string.h>
typedef struct {
int id;
char name[50];
int age;
} Person;
int main() {
FILE *file;
Person people[3];
char line[100];
int i = 0;
file = fopen("people.csv", "r");
if (file == NULL) {
printf("ファイルを開けませんでした。\n");
return 1;
}
// ヘッダー行を読み飛ばす
fgets(line, sizeof(line), file);
// 1行ずつ読み込む
while (fgets(line, sizeof(line), file) != NULL && i < 3) {
// 改行文字を削除
line[strcspn(line, "\n")] = '\0';
// カンマで分割し、構造体に格納
people[i].id = atoi(strtok(line, ","));
strcpy(people[i].name, strtok(NULL, ","));
people[i].age = atoi(strtok(NULL, ","));
i++;
}
fclose(file);
// 構造体のデータを表示
for (int j = 0; j < i; j++) {
printf("ID: %d, 名前: %s, 年齢: %d\n", people[j].id, people[j].name, people[j].age);
}
return 0;
}
ID: 1, 名前: John Doe, 年齢: 30
ID: 2, 名前: Jane Smith, 年齢: 25
ID: 3, 名前: Bob Johnson, 年齢: 40
この例では、CSVファイルから読み込んだデータをPerson
という構造体に格納しています。
構造体を使うことで、関連するデータを一つの単位として扱うことができます。
複数ファイルの同時処理
複数のファイルを同時に処理することで、データの統合や比較が可能になります。
以下の例では、2つのファイルを同時に開き、データを読み込んで表示します。
#include <stdio.h>
int main() {
FILE *file1, *file2;
char line1[100], line2[100];
file1 = fopen("file1.txt", "r");
file2 = fopen("file2.txt", "r");
if (file1 == NULL || file2 == NULL) {
printf("ファイルを開けませんでした。\n");
if (file1 != NULL) fclose(file1);
if (file2 != NULL) fclose(file2);
return 1;
}
// 両方のファイルから1行ずつ読み込む
while (fgets(line1, sizeof(line1), file1) != NULL && fgets(line2, sizeof(line2), file2) != NULL) {
// 改行文字を削除
line1[strcspn(line1, "\n")] = '\0';
line2[strcspn(line2, "\n")] = '\0';
printf("File1: %s, File2: %s\n", line1, line2);
}
fclose(file1);
fclose(file2);
return 0;
}
File1: Hello, File2: World
File1: C, File2: Programming
File1: Language, File2: Example
この例では、file1.txt
とfile2.txt
の両方から1行ずつデータを読み込み、同時に表示しています。
複数ファイルを同時に処理することで、データの比較や統合が容易になります。
まとめ
ファイルからデータを読み込み、配列に格納する方法は、C言語プログラミングにおいて基本的かつ重要なスキルです。
この記事では、ファイルの読み込み方法、配列へのデータ格納、CSVファイルの処理、応用例としてのバイナリファイルや構造体の利用について解説しました。
これらの知識を活用することで、より複雑なデータ処理が可能になります。
ぜひ、実際のプログラムでこれらの技術を試してみてください。