ファイルの存在をチェックしたり、ファイルサイズ、 更新日時などを知りたいことがしばしばある。 そんなとき有効なのが 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.cchar *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);