習得中 Rust で 日付順 のファイル一覧取得と php でのコマンドラインツール
Page No.1
- Rust で全サブフォルダ下にある全ファイルの日付逆順リストの取得
- Vector 版 main.rs
- BTreeMap 版 main.rs
- Rust でのファイルの日付を取得する方法での失敗
- php でも全サブフォルダ下にある全ファイルの日付逆順リストを取得するコマンドラインツール
- おまけ:Rust-analyzer の 変数の型や関数の引数の型のヒントを消す設定
あるフォルダ下にある、サブフォルダにあるファイルを含めた総ファイルにおいて、日付逆順(新ー>旧)での一覧を取得したいということがあって。これ、dir コマンドで出来そうなのではあるのだけれど、いかに?
例えば、dir /s /d /Od というコマンドで、 サブフォルダのファイルも表示されるし、日付の古いものから順に並べてもくれる。しかしそれは各フォルダ内にあるファイルだけの並び替えとなる。今、自分が必要としているのは、元親にある全サブフォルダ下(孫、ひ孫フォルダなども含む)の全ファイルによる日付の並び替えなのです。これが標準装備のコマンドで出来るのかどうか、よくわからない。
それなら作ってしまったほうが早い、ってことになるね。
で、現在、習得中の Rust で経験を積むべく、挑むことにしたと。
と、また、まったく WordPress には関係のないネタだったりする。
Rust で全サブフォルダ下にある全ファイルの日付逆順リストの取得
初めてコンピュータ言語を勉強したのは、MS-DOS 時代においての Microsoft が出してた Quick-C という C言語だった。このソフトウェアを買ったら、しっかりした C言語の教本まで入っていて、別途、教本を買う必要がなかった。とても親しみを持ってたソフトウェアだったのだけれど、引っ越しの時に処分してしまって、今思うと、やっぱり持っていればよかったと少々後悔してる。まぁ、その前に Basic なんてのも少しさわっていた事はあるけれど。それから Excel や Access の VBA のあと、php、Javascript、Python という感じ。C 以外は変数の型などゆるゆるなものばかり。
それに対して Rust はこれでもかっ!ってぐらいに変数の型に対してガチガチに石頭な言語。ずっとゆるゆるに慣れ親しんできたものには、それはとてもとても厳しいコンパイラを相手にしないといけないということになる。
まぁ、まだ知らないことばかりなので、おかしな部分も多いのだろうけれど、とりあえず目的を達成してちゃんと機能してはいるということで。
必要な機能としては、先にも書いた通り、あるフォルダ下の全てのサブフォルダに含まれる全てのファイルを対象に、日付の新しい順での一覧を得る、ということ。で、ファイルが千も2千もあると表示がうっとうしいので、表示するファイルの数を指定できるということ。それぐらい。
Rust の場合、とりあえず必要なことは、外部クレートを使用するときには、Cargo.toml にそのクレートを登録しなくてはいけないこと。この場合、日付をタイムスタンプに変換したりするので、必要だったのが chrono というクレート。まずそれをやる。
Vector 版 main.rs
と、いうことでいきなり、その本丸である main.rs を。
ファイルの日付により新しい順に並べたかったので、ファイル名と日付のコレクションを作ってから並べ替えさせるということを普通に考えていたけれど、調べていると Rust には BTreeMap という便利な HashMap が備えられていた。この BTreeMap はデフォルトでキーにより昇順に並べられているということで、キーにタイムスタンプを使用すれば後でわざわざ並べ替える必要もないとのこと。
こりゃいいや!ってな具合で、実際に最初に作ったのはこの BTreeMap を使った方。が、後でふと考えてみると、デフォルトでキーによって並び替えられているということは、要素を追加するごとに並び替えをしているのではないか?という疑問。これって遅くなるんじゃない?と。Vector で値をとにかく蓄積しておいて最後に一度だけ sort させるのとどちらが負荷が少ないのだろうか?と。
まぁ、あとに書いてあるけれど、BTreeMap の場合はキーで使うタイムスタンプの重複という問題もあるし、言語の習得はとにかく思いついたら書く、ということだと思っているので、BTreeMap で書いたものを Vector に直してみた。いらないものがかなり減るので明らかに Vector の物の方がすっきりしていい。と、いうことでその Vector 版。どちらが速いか?って。同じように速いのでよくわからない。
しかし、その速さには但し書きがつく。詳しくは最後に書いているけれど、このプログラムにおいて Rust が速いのは Windows を起動してこのプログラムを動かすのが二度目以降のことに限られる。
BTreeMap 版 main.rs
BTreeMap を使う場合、1つ問題としてあるのが、キーにファイルの日付を使うということで、全く同日同時刻の物の存在を否定することができないということ。ファイルの日付もいくつかあるので、どれを使用するのかも考えなくてはならない。例えば Windows の場合なら、ファイルの日付として、日付時刻、作成日時、更新日時とある。あと Rust で得られるものとして最後にアクセスした日時もある。
これは実際に検証用のフォルダを作っているときに気づいたことである。新規に作ったフォルダでサブフォルダを不規則にいくつも作り、50個ほどの画像ファイルをそこにコピーした。こうした場合の、各ファイルの作成日時はすべて全く同じ同日同時刻となった。Rust では fs::metadata().created() で得られる値がこの作成日時だった。作成日時はコピーした場合に更新されるので、まとめて一度にコピーすれば全て同じ日時になってしまうということ。
一方、fs::metadata().modified() で得られるのは更新日時の方で、これはファイル自体を上書き保存しない限り更新されない。作ったまま更新していなければ、そのファイルのオリジナルの本来の作成日時ということになると思う。と、いうことでこの場合に使用するのは迷うことなく modified の方となる。しかし、それでも、同日同時刻が存在することは完全には否定できない。秒数まで同じっていうのはなかなかありそうもないとは思うものの。
ディレクトリ構造を取得する部分を別関数としたので、main 関数はそのデータを BTreeMap で受け取って蓄積していくようにした。その場合、受け取ったデータを本データの方へ結合させていくわけなんだけれど、その場合に、キーとして使用してるタイムスタンプに重複があると上書きされて具合が悪いことになってしまう。そこのところで、append が使えないのでちょっと面倒なことをしてる。結局、この点においても、面倒なことをしなければいけないのなら、普通に Vector をつかって最後にソートさせたほうが手間がかからなかったのかも?などと思いつつ・・・。まぁ、いろいろと学ぶことは多かったので良しとしよう。
たったこれだけのことなんだけれど、これがちゃんと動くまでは相当な時間、型の不一致と戦ってしまった。フォルダを取得するとかファイルを読み出すとかの関数においての、引数やら返り値なんかとか、i64 と &i64 で不一致だとか(これは所有権の問題だと思う)、でつまづいてしまう。いったいどうすりゃいいのさ!となって深渕にはまってしまう。なんというか、自分で考えて作っているという気が全くしない。自分的には php や Javascript と違って、作っていてもなんだかあまり楽しくない、という感じが Rust はする。
ちなみに結果はこんな具合に表示される
Rust でのファイルの日付を取得する方法での失敗
最後にある、ファイルの日付をタイムスタンプにて返す関数 read_file() において、最初は間違ったやり方をしていたので、自戒をこめて忘れないようにと。ファイルの日付をいかにして取得できるのかと、ネットを探していてたどり着いたのが metadata()。まぁ、それはよかったのだけれど、その時のページにはファイルを開いて、metadata を使うやり方が示してあったので、自分で書いていたのが以下のごとく。
これで動いてはいたのだけれど・・・。
問題はこう。PC の電源をいれ Windows が立ち上がって、初めてこのプログラムを動かした時に、ものすごく時間がかかる。ファイルが千以上もあるものなら、フリーズしているんじゃないかと思うほど。二度目からは同じフォルダならパッとあっさり表示されるのだけれど、やはりなんだかおかしい。そもそもファイルの日付を取得するのに、わざわざファイルを開く必要などないのでは?という、疑問を懐きながら関数を書いてはいたのだけれど。たしか、あまり記憶が定かではないけれど、ファイルの日付情報って FAT にあったんじゃなかろうか?と。
Post : 2022/09/11 11:45
Comments feed
Trackback URL : https://strix.main.jp/wp-trackback.php?p=172261