ファイルの存在をチェックしたり、ファイルサイズ、 更新日時などを知りたいことがしばしばある。 そんなとき有効なのが stat関数である。
次のようにして、ファイルの存在の有無をチェックできる。
#include <sys/stat.h>
struct stat st;
if (stat(fname, &st) == 0) {
return; // 既に存在する
}
stat構造体の中身は次の通りである。
struct stat {
dev_t st_dev; /* ファイルがあるデバイスの ID */
ino_t st_ino; /* inode 番号 */
mode_t st_mode; /* アクセス保護 */
nlink_t st_nlink; /* ハードリンクの数 */
uid_t st_uid; /* 所有者のユーザ ID */
gid_t st_gid; /* 所有者のグループ ID */
dev_t st_rdev; /* デバイス ID (特殊ファイルの場合) */
off_t st_size; /* 全体のサイズ (バイト単位) */
blksize_t st_blksize; /* ファイルシステム I/O でのブロックサイズ */
blkcnt_t st_blocks; /* 割り当てられたブロック数 */
time_t st_atime; /* 最終アクセス時刻 */
time_t st_mtime; /* 最終修正時刻 */
time_t st_ctime; /* 最終状態変更時刻 */
};
ここで、st_ctimeは作成時間ではない。cはchageの頭文字である。 すなわち、stat関数ではファイル作成時刻を知ることはできない。
stat構造体には三種類の時刻があるが、下のプログラムでは、 三つとも同じ時刻であった。
// filetime.c 2013-03-08 Hatada
#include <stdio.h>
#include <time.h>
#include <sys/stat.h>
int main(int argc, char* argv[]) {
struct stat st;
stat(argv[1], &st);
printf("%s\n%s\n%s\n",
asctime(localtime(&st.st_atime)),
asctime(localtime(&st.st_mtime)),
asctime(localtime(&st.st_ctime)));
}
下に実行時間を示す。 エクスプローラでは、作成時間とアクセス時間は共に 2013年3月8日 16:15:21で、更新時間は2013年3月8日 17:33:57 であった。
c:\mh\mcc>filetime ../www/c01/file/filetime.c Fri Mar 08 16:15:21 2013 Fri Mar 08 16:15:21 2013 Fri Mar 08 16:15:21 2013
指定ディレクトリを再帰的に探索し、 拡張子別にファイル数、行数、サイズを求める。
count-lines.c
char *loadText(char *file) {
char *buf;
FILE *fp;
struct _stat st;
if (_stat(file, &st) != 0) return NULL;
buf = calloc(st.st_size + 1, sizeof(char));
fp = fopen(file, "rb");
fread(buf, sizeof(char), st.st_size, fp);
fclose(fp);
return buf;
}
234,121,45, のような整数列を読み込む。末尾にも「 , 」があるものとする。
long number;
FILE *fp = fopen(fname, "r");
if (fp == NULL) {
printf("ファイル %s が開けません\n", fname);
return -1;
}
while (fscanf(fp, "%ld,", &number) > 0) {
printf("%d,", number);
}
fclose(fp);
数値が実数の場合には次のようにすればよい。 数値は 2104.61 のような表記だけでなく、2.10461e+03 のような指数表記でもよい。
double number;
FILE *fp = fopen(fname, "r");
if (fp == NULL) {
printf("ファイル %s が開けません\n", fname);
return -1;
}
while (fscanf(fp, "%lf,", &number) > 0) {
printf("%f,", number);
}
fclose(fp);
CSVファイルにレコードを追加する場合にはアペンドモードでオープンしてレコードを書き込むだけでよいから簡単である。
これに対して HTMLファイルでの表は次のような形をしている。
これは下に示すように表示される。
| 表現 | 意味 |
|---|---|
| \d | 任意の数値([0-9]と同じ) |
| \D | \d以外の文字([^0-9]と同じ) |
| \w | 英数文字([A-Za-z0-9_]と同じ) |
| \W | \w以外の文字 |
この表の末尾に1行追加して
| 表現 | 意味 |
|---|---|
| \d | 任意の数値([0-9]と同じ) |
| \D | \d以外の文字([^0-9]と同じ) |
| \w | 英数文字([A-Za-z0-9_]と同じ) |
| \W | \w以外の文字 |
| \s | スペース(\f\r\n\t\v) |
<html> <body> <table border="1"> <tr><th>表現</th><th>意味</th></tr> <tr><th>\d</th><td>任意の数値([0-9]と同じ)</td></tr> <tr><th>\D</th><td>\d以外の文字([^0-9]と同じ)</td></tr> <tr><th>\w</th><td>英数文字([A-Za-z0-9_]と同じ)</td></tr> <tr><th>\W</th><td>\w以外の文字</td></tr> <tr><th>\s</th><td>スペース(\f\r\n\t\v)</td></tr> </table> </body> </html>
これを実現するには、このファイルを読み書き可能なモードでオープンして、 1行ずつ読み込み、</table を探し、そこに新しいレコードを書き込み、その後に </table>、 </body>、 </html>を書き込むことになる。
プログラム例を下に示す。 一行ずつ読み込み、</table>が現れるまで繰り返す。 そこから書き込むと </table> の後ろに書き込むことになるため、それではいけない。 ftell関数を使って、</table>の先頭を覚えている。
long filepos;
char *result = "<tr><th>\s</th><td>スペース(\f\r\n\t\v)</td></tr>";
FILE *fp = fopen("test.html", "rb+");
while (fgets(buffer, sizeof(buffer), fp) != NULL) {
if (strncmp(buffer,"</table>",7) == 0) break;
filepos = ftell(fp);
}
fseek(fp, filepos, SEEK_SET); // filepos は の位置を指している
fprintf(fp, "%s\n", result); // 表の一行分を出力する
fprintf(fp, "</table>\n</body>\n</html>\n");
fclose(fp);
ftellを使わず、</table>行のサイズだけ、前に戻す方法もある。これを下に示す。
char *result = "<tr><th>\s</th><td>スペース(\f\r\n\t\v)</td></tr>";
FILE *fp = fopen("test.html", "rb+");
while (fgets(buffer, sizeof(buffer), fp) != NULL) {
if (strncmp(buffer,"</table>",7) == 0) break;
}
fseek(fp, -strlen(buffer), SEEK_CUR);
fprintf(fp, "%s\n", result); // 表の一行分を出力する
fprintf(fp, "</table>\n</body>\n</html>\n");
fclose(fp);