生物物理計算化学者の雛

主に科学に関する諸々を書き留めています。

C言語でいろいろなフォーマットのテキストファイルを読むプログラム例

C言語でテキストファイルに書き込まれているデータを読み込むプログラムの例をいくつか記述しておきます。

fscanf()関数を使うと楽に記述できるケースもありますが、思い通りに fscanf() が動作してくれないことがありますので、ファイルから1行ずつ文字列配列 char line[] に読み込んでから、データに変換するという手順で実装しています。

atoi() とatof() 関数

"123", "1.234" のように文字列として読み込んだデータはatoi(), atof()関数によりint型、double型に変換することができます。

なお、文字列の先頭に " 123" のようにスペースが入っていても、あるいは "1.234kcal/mol" のように後ろに余分な文字が入っていてもatoi(), atof()は動作してくれます。

int    dat_i = atoi("123"); 
double dat_d = atof("1.234");

int    dat_i2 = atoi("  123");          // 文字列先頭にスペースがあってもOK
double dat_d2 = atof(" 1.234kcal/mol"); // 数字データの後ろに別の文字があってもOK

int    dat_i3 = atoi("a 123");          // 先頭が数字でない文字なので 0   が代入される
double dat_d3 = atof("kcal/mol 1.234");  // 先頭が数字でない文字なので 0.0 が代入される

1行に1つのデータが記述されているテキストファイルを読み込む

一番シンプルに1行に1つのデータが記述されている以下のようなファイル

 52.321
 1.523
 -5.891

を読むプログラムを示します。

#include <stdio.h>
#include <stdlib.h>

#define LINE_LENGTH_MAX 256 // 1行の長さの上限
#define DAT_NUM_MAX 128     // データ数の上限

int main(int argc, _TCHAR* argv[])
{
	FILE *fp;
	char line[LINE_LENGTH_MAX]; 
	double values[DAT_NUM_MAX];
	int n;

	// テキストファイルを読み込み用に開く
	if(!(fp = fopen("C:/work/input.txt", "r"))){
		// ファイルオープンに失敗
		return 1;
	}

	n = 0;
	// ファイルから1行ずつ line に読み込む
	//    データを読み終えたら while 文から抜ける
	while(fgets(line, LINE_LENGTH_MAX, fp))
	{
		// atofにより文字列 "12.345" から double型に変換する
		values[n] = atof(line);
		n++;
	}
	// 配列 values[0 - (n-1)] に読み込まれたデータが保存されている
	// 読み込んだデータ数 n 

	fclose(fp);
	return 0;
}

1行に複数のデータがコンマ区切りで記述されているテキストファイル(.csvファイル)を読み込む

1行に複数のデータ(1行当たりのデータ数は任意)がコンマ区切りで記述されている以下のようなファイル

 52.321,1.234,5.325,-124.234
 1.523,199.239
 -5.891,806.125,61.235,-15.238
 -538.352

を読むプログラムを示します。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define LINE_LENGTH_MAX 256 // 1行の長さの上限
#define DAT_NUM_MAX 128     // データ数の上限

int main(int argc, _TCHAR* argv[])
{
	FILE *fp;
	char line[LINE_LENGTH_MAX];
	double values[DAT_NUM_MAX];
	int n;

	// テキストファイルを読み込み用に開く
	if(!(fp = fopen("C:/work/input.txt", "r"))){
		// ファイルオープンに失敗
		return 1;
	}

	n = 0;
	// ファイルから1行ずつ line に読み込む
	//    データを読み終えたら while 文から抜ける
	while(fgets(line, LINE_LENGTH_MAX, fp))
	{
		char *ch = line; // char line[] 用の文字列ポインタ、最初は line[] の先頭を示す
		                 // "->52.321,1.234,5.325,-124.234" (->がchの示す位置)

		while(-1){ // 1行から複数データを取得するための無限ループ
			if(*ch == '\n')  // 行末だったら while(-1)から抜ける
				break;  

			// atofにより文字列 "12.345" から double型に変換する
			values[n] = atof(ch);
			n++;

			if(!(ch = strchr(ch, ','))) // "->52.321,1.234,5.325,-124.234" から "52.321->,1.234,5.325,-124.234"
				break;   // ','が見つからない場合は行末なので while(-1) の無限ループを抜ける

			ch++;        // ポインタ ch を',' の次に移動
			             // "52.321->,1.234,5.325,-124.234" から "52.321,->1.234,5.325,-124.234"
		}
	}
	// 配列 values[0 - (n-1)] に読み込まれたデータが保存されている
	// 読み込んだデータ数 n 

	fclose(fp);
	return 0;
}

複雑なフォーマットのデータファイルから特定の項目を読み込む

以下のような複雑なファイル(Gaussian03出力ファイル)からある項目の数値「 E(UB+HF-LYP) = xxxxx.xxxxxxx 」(複数回出現する)を読み込むプログラムを示します。

・・・
 Gap=     0.081 Goal=   None    Shift=    0.000
 RMSDP=8.53D-09 MaxDP=8.49D-07 DE=-3.55D-11 OVMax= 1.10D-06

 SCF Done:  E(UB+HF-LYP) =  -1841.49580923     A.U. after   25 cycles
             Convg  =    0.8535D-08             -V/T =  2.0826
             S**2   =   2.0044
 KE= 1.700992279018D+03 PE=-1.295249248986D+04 EE= 5.110287762944D+03
 Annihilation of the first spin contaminant:
 S**2 before annihilation     2.0044,   after 
・・・
 Gap=     0.082 Goal=   None    Shift=    0.000
 RMSDP=7.16D-09 MaxDP=7.08D-07 DE=-1.36D-11 OVMax= 1.03D-06

 SCF Done:  E(UB+HF-LYP) =  -1841.60229698     A.U. after   16 cycles
             Convg  =    0.7165D-08             -V/T =  2.0830
             S**2   =   2.0044
 KE= 1.700433072720D+03 PE=-1.293752712536D+04 EE= 5.103154386887D+03
 Annihilation of the first spin contaminant:
・・・
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define LINE_LENGTH_MAX 256 // 1行の長さの上限
#define DAT_NUM_MAX 128     // データ数の上限

int main(int argc, _TCHAR* argv[])
{
	FILE *fp;
	char line[LINE_LENGTH_MAX];
	double values[DAT_NUM_MAX];
	int n;

	// テキストファイルを読み込み用に開く
	if(!(fp = fopen("C:/work/input.txt", "r"))){
		// ファイルオープンに失敗
		return 1;
	}

	n = 0;
	// ファイルから1行ずつ line に読み込む
	//    データを読み終えたら while 文から抜ける
	while(fgets(line, LINE_LENGTH_MAX, fp))
	{
		if(!strstr(line, "E(UB+HF-LYP)")) // 文字列 "E(UB+HF-LYP)" が行内に含まれなければ
		    continue;                     // 次の行にスキップ  

		char *ch = line; // char line[] 用の文字列ポインタ、最初は line[] の先頭を示す
		                 // "-> SCF Done:  E(UB+HF-LYP) =  -1841.49580923     A.U. after   25 cycles" (->がchの示す位置)

		ch = strchr(line, '=') + 1; // ch を '=' の次の位置に移動
		                            //      "-> SCF Done:  E(UB+HF-LYP) =  -1841.49580923     A.U. after   25 cycles"
		                            // から " SCF Done:  E(UB+HF-LYP) =->  -1841.49580923     A.U. after   25 cycles"
		
		// atofにより文字列 "  -1841.49580923" から double型に変換する
		values[n] = atof(ch);
		n++;
	}
	// 配列 values[0 - (n-1)] に読み込まれたデータが保存されている
	// 読み込んだデータ数 n 

	fclose(fp);
	return 0;
}

新・C言語入門 シニア編 (C言語実用マスターシリーズ)

新・C言語入門 シニア編 (C言語実用マスターシリーズ)