PHPを使ってディレクトリ配下の全ファイルを再帰的に処理する方法です。 イテレータを使います。
例えば全ファイルの絶対パスを出力するなら次のようにします。
|
|
$iterator = new RecursiveIteratorIterator( new RecursiveDirectoryIterator(realpath('dir_path'))); foreach ($iterator as $fileInfo) { if ($fileInfo->isFile()) { echo $fileInfo->getPathname(), "n"; } } |
イテレーションする対象をファイルだけに絞ったものを作るのもありですね。 次のように yield を使って関数を作ることもできます。
|
|
function allSubdirectoryAndFilesIterator($dirPath) { return new new RecursiveIteratorIterator( new RecursiveDirectoryIterator(realpath($dirPath))); } function allFiles($dirPath) { foreach (allSubDirectoryAndFiles('directory_path') as $fileInfo) { if ($fileInfo->isFile()) { yield $fileInfo; } } } |
でもこれは yield を使っているため PHP 5.5 からしか使えません。 FilterIterator を使えば PHP 5.1 から使える関数を作成できます。
|
|
class FileFilterIterator extends FilterIterator { function accept() { $fileInfo = $this->getInnerIterator()->current(); if ($fileInfo !== null || $fileInfo->isFile()) { return true; } return false; } } |
このように FileFilterIterator を作り、 $iterator = new FileFilterIterator($anIterator); のようにすれば、ファイルのみの Iterator が作れます。
「Iterator なんて 言語の機能に依存しすぎだから readdir つかって自分でイテレータ作るべきだ」というご意見もあります。
ctype.h で定義されている、よく使うであろう関数をまとめてみました。
文字チェック
- int isalnum(int character)
引数が文字 ('A' から 'Z', 'a' から 'z' または 0 から 9) なら 非ゼロ、そうでなければゼロを返す。
- int isalpha(int character)
引数がアルファベットなら非ゼロ、そうでなければゼロを返す。
- int isdigit(int character)
引数が数字 (0 から 9) なら非ゼロ、そうでなければゼロを返す。
- int isxdigit(int character)
引数が16進数文字 (0 から 9、'A' から 'F' または 'a' から 'f') なら非ゼロ、そうでなければゼロを返す。
- int iscntrl(int character)
引数が制御文字 (0x00 から 0x1F または 0x7F) なら非ゼロ、そうでなければゼロを返す。
- int isprint(int character)
引数が印字可能文字 (0x20 から 0x7E) なら非ゼロ、そうでなければゼロを返す。
- int isgraph(int character)
引数がスペース以外の印字可能文字 (0x21 から 0x7E) なら非ゼロ、そうでなければゼロを返す。
- int islower(int character)
引数が小文字なら非ゼロ、そうでなければゼロを返す。
- int isupper(int character)
引数が大文字なら非ゼロ、そうでなければゼロを返す。
- int isspace(int character)
引数が空白文字 (スペース、タブ文字、復帰文字、改行文字、垂直タブ文字、改ページ文字) なら非ゼロ、そうでなければゼロを返す。
- int ispunct(int character)
引数が句読点文字 (空白文字とisalnumが非ゼロとなる文字を除くすべての文字) なら非ゼロ、そうでなければゼロを返す。
文字変換
- int tolower(int character)
character が大文字なら、それを小文字にして返す。
- int toupper(int character)
character が小文字なら、それを大文字にして返す。
C言語でよく使うであろうヘッダファイル string.h で定義されている関数をまとめてみました。
検出系
- void *memchr(const void *buf, int c, size_t count)
buf の最初の count バイトを検索して、 unsigned char に変換された c が最初に現れる場所を探します。 buf 内の c の場所へのポインタを返します。 buf の最初の count バイトの中に c が存在しない場合は NULL が返されます。
- char *strchr(const char *string, int c)
文字列 string の最初の文字 c を検出します。 string はnul文字で終わっている必要があります。 戻り値は、内部的に文字に変換された c が string の中で最初に現れる場所へのポインタです。 存在しなければ NULL が戻り値となります。
- char *strstr(const char *string1, const char *string2)
string1 の中で string2 が最初に現れる場所を検出します。 戻り値は、 string1 の中で最初に現れた string2 の先頭へのポインタです。 string2 が string1 の中に存在しなかった場合、 戻り値は NULL となります。 string2 が指す文字列の長さが 0 の場合は string1 へのポインタが戻り値になります。
- size_t strspn(const char *string1, const char *string2)
文字列 string1 の先頭から始まる部分文字列で、 string2 に含まれる文字からなるものの最大の長さを返します。 別の言い方をすれば、 文字列 string2 に含まれない文字が string1 の中で最初に現れる場所のインデックスを返します。 string1 のすべての文字が string2 に含まれる場合には string1 の長さが返されます。
- size_t strcspn(const char *string1, const char *string2)
文字列 string1 の先頭から始まる文字列で、 string2 に含まれない文字からなるものの最大の長さを返します。 別の言い方をすれば、文字列 string2 に含まれる文字が string1 にの中で最初に現れる場所のインデックスを返します。 string1 のすべての文字が string2 に含まれない場合には string1 の長さが返されます。
- char *strpbrk(const char *string1, const char *string2)
string2 が指し示す文字列に含まれる文字のうちで、 string1 が指し示す文字列の中に最初に現れる文字を探します。 戻り値は検出された文字へのポインタです。 string1 と string2 に共通の文字がなければ NULL を返します。
- char *strtok(const char *string, const char *separator)
separator を string のトークンの区切り文字として扱います。 戻り値は先頭にある区切り文字をスキップして、 string の中の最初のトークンを探し、そのトークンへのポインタを返します。 1度呼び出した後は、 string に NULL を指定して strtok を呼び出すことで、 最初に指定された string 内の次のトークンへのポインタを返します。 返されるトークンは、区切り文字が nul文字に置き換えられたものになります。 また、区切り文字 separator は呼び出しごとに変更できます。 トークンがなくなると、 NULL が返されます。
比較系
- int memcmp(const void *buf1, const void *buf2, size_t count)
buf1 と buf2 の最初の count バイトを比較して、次の値を返します。
| 値 |
条件 |
| 正 |
buf1 は buf2 より小さい |
| 0 |
buf1 は buf2 と等しい |
| 負 |
buf1 は buf2 より大きい |
- int strncmp(const char *string1, const char *string2, size_t count)
string1 と string2 の最初の count 文字分を比較して、次の値を返します。
| 値 |
条件 |
| 正 |
substring1 は substring2 より小さい |
| 0 |
substring1 は substring2 と等しい |
| 負 |
substring1 は substring2 より大きい |
- int strcmp(const char *string1, const char *string2)
string1 と string2 を比較して、次の値を返します。
| 値 |
条件 |
| 正 |
string1 は string2 より小さい |
| 0 |
string1 は string2 と等しい |
| 負 |
string1 は string2 より大きい |
コピー系
- void *memcpy(void *dest, const void *src, size_t count)
src の count バイトを dest にコピーします。 アドレスが重複するオブジェクト間でのコピーが行われた場合の動作は不定です。 戻り値は dest へのポインタです。
- char *strcpy(char *string1, const char *string2)
string2 を最後のnul文字も含めて、ポインタ string1 で指定される位置にコピーします。 string1 に文字列定数を指定することはできません。 戻り値は string1 へのポインタです。
- char *strncpy(char *string1, const char *string2, size_t count)
string2 の count 文字分を string1 にコピーします。 count が string1 の長さより小さいか等しい場合、コピーされた文字列にnul文字は付加されません。 count が string2 の長さより大きい場合、結果の string1 には長さが count になるまで nul文字が埋め込まれます。 戻り値は string1 へのポインタです。
- void *memmove(void *dest, const void *src, size_t count)
memcpy と同様に、 src の count バイトを dest にコピーしますが、 memcpy とは違ってアドレスが重複するオブジェクト間でのコピーを安全に行うことができます。 これは src を内部の1次配列にコピーしたうえでバイトを行うからです。 戻り値は dest へのポインタです。
- void *memset(void *dest, int c, size_t count)
dest の最初の count バイトを値 c に変換します。 戻り値は dest へのポインタです。
文字列結合
- char *strcat(char *string1, const char string2)
string2 を string1 に連結し、結果の文字列の最後に '\0' を付けます。 string1 に文字列定数を指定することはできませんが、 string2 には文字列定数を指定することができます。 戻り値は string1 へのポインタです。
- char *strncat(char *string1, const char string2, size_t count)
string2 の最初の count 文字を string1 に連結し、結果の文字列の最後に '\0' を付けます。 string2 が count 文字に満たない場合は string2 全体を string1 に連結します。 string1 に文字列定数を指定することはできませんが、 string2 には文字列定数を指定することができます。 戻り値は string1 へのポインタです。
その他
- char *strerror(int errno)
errno にあるエラー番号をエラーメッセージ文字列にマップして、文字列へのポインタを返します。
定期的にメールを送るとき、みなさんどうしていますか? Google を使用すると、 gmail から自動でメールを送信することができます。
続きを読む 定期的にメールを自動で送る方法 →
コードを書く際によく話題になるインデント。タブを使いますか?スペースを使いますか?それともほかのなにかを使いますか?
私はスペースを使います。 それはインデントが見易さのためのものだからです。
タブは制御文字
タブは制御文字で見易さの調整に使われるものではありません。確かに昔はタイプライターの位置調整のために使われていたそうですが、今はそういった使われ方はしません。もしタブ文字がタイプライターにおける水平タブと同じ意味を持つのなら、タブ幅を個人で変えられるようにはしなかったでしょう。
TSV では、 タブは異なる属性だとか、異なる種類だとかいう意味を表すための区切り文字になります。 タブでインデントを行ったソースコードをコピーしてExcelに貼り付けると、タブでインデントされた文字は別の列に貼り付けられます。 そういった情報の制御文字として タブは使われています。
ありがちな誤解
このようにタブを制御文字として扱うと、「インデントは情報の深さを表すものだから、タブでインデントしてもいいじゃないか」と考えてしまうこともあるかもしれません。 たしかにタブを使っていれば、先ほど例に挙げた Excel にコードを張り付けるケースでは、インデントされた行はそれだけ右のカラムに貼り付けられます。
しかし、情報の深さはインデントだけで決まるものではありません。 そのため、仮にすべてのコードがきれいにインデントされているとしてもタブを使うべきではないです。
下に情報の深さをインデントを使って表した例を示します。 (都合上 下の例ではインデントをスペース2つで現しています。)
|
|
sample_function(a, b, c, d, e); |
|
|
sample_function( a, b, c, d, e ); |
インデントを情報の深さとしてとらえると、1つ目では特に深い情報がなく、2つ目では1行に書ける関数でも、仮に引数が1つでも改行とインデントが必要になります。 また2つ目ではカンマの存在意義がありません。
最後に
最初に書いた「私はスペースを使います。 それはインデントが見易さのためのものだからです。」というのはわかっていただけたでしょうか。 インデントは見易さの問題を解決する目的があるので、見える文字であるスペースを使っています。
A Life Summary of an Gypsy