Rust でファイル名選出のコマンドラインツール
習得中 Rust で再び簡単なコマンドラインツールの作成
このページ 「習得中 Rust で 日付順 のファイル一覧取得と php でのコマンドラインツール」で、あるフォルダ下にある、サブフォルダにあるファイルも含めた総ファイルにおいての、日付逆順(新ー>旧)での一覧を取得するコマンドラインツールを作った記事を書いた。
今回はそれを元にして作った、もっとシンプルな、連番となっているファイルが入っているフォルダにおいて、その連番の最後(最大)の数値を取得するコマンドラインツール。ただ、それらファイルは連番となってはいるものの3つほど属性があって、その属性により連番の前に “_”、”x”、と符号がついているもの、そして符号無しのもの、という具合に三種類に分別がつけられている。
要は画像のフォルダなのであって、連番にはなっているものの符号が付けてあるので、そのフォルダをウィンドウで開いて見た場合、符号別の順番になっているので、連番の最大値がわかりづらいということなのである。そこでこのコマンドラインツールの出番ということになる。まぁ、php で書けばあっけなく簡単に書けるものではあるけれど、あえて Rust にて書いてみる。
Rust main.rs
それでは、ということでいきなり main.rs。
今回のは外部のクレートは何も使わないので、Cargo.toml に書き加えることはなし。
とりあえず、下にて目的は達成できた。フォルダにサブフォルダがない場合は、再帰関数である fn read_directry() を呼ばず、fn main() だけで完結する。どちらかというと fn main() にある フォルダ取得部分も全く同じなのでその部分も fn read_directry() にやらせれば良いのだけれど・・・。
use std::{ io, fs, path::Path };
fn get_input( mes: String ) -> String {
// コマンドラインから入力を取得する関数
let mut linestr = String::new();
loop {
println!( "\n=>>> {}", mes );
io::stdin()
.read_line( &mut linestr )
.expect( "xxx Failed to read line! xxx" );
let linestr = linestr.trim().to_string();
if linestr.len() > 0 {
break;
}
}
linestr
}
fn read_directry( tarpath: &Path, host: &str ) -> Vec< ( String, String ) > {
// フォルダ内のフォルダとファイルを取得し、ファイルなら連番の最大のものを取得して返す
// フォルダの場合は再び自分自身の関数を呼ぶ
let mut data: Vec< ( String, String ) > = Vec::new();
let mut data_: u64 = 0;
let mut datax: u64 = 0;
// fs::read_dir() でフォルダ下にあるフォルダ、ファイルの一覧を取得
match fs::read_dir( tarpath ) {
Err( why ) => println!( "! {:?}", why.kind() ),
Ok( fls ) => {
for fl in fls {
let flp = fl.unwrap().path();
if flp.is_dir() {
// それがフォルダの場合は自分自身を呼び出す
let chil: Vec< ( String, String ) > = read_directry( flp.as_path(), &host );
let mut subtmp_ = String::from( &chil[0].0 );
let mut subtmpx = String::from( &chil[0].1 );
// それぞれの一文字目("_"か"x")を消去
subtmp_.remove(0);
subtmpx.remove(0);
// 数値に変換
let namenum_: u64 = match subtmp_.trim().parse() {
Ok( num ) => num,
Err(_) => 20
};
let namenumx: u64 = match subtmpx.trim().parse() {
Ok( num ) => num,
Err(_) => 20
};
// 値の大小の比較
if data_ < namenum_ {
data_ = namenum_;
}
if datax < namenumx {
datax = namenumx;
}
} else {
let flname = flp.file_name().unwrap().to_string_lossy();
let subtmp = flname.chars().take(40).collect::<String>();
let firstc = subtmp.as_str().chars().nth(0).unwrap();
let mut tmps = String::from( "" );
// 一文字づつ数値に変換してみることでファイル名から数値ではない文字列を消去
for i in flname.chars() {
let inum: i8 = match i.to_string().parse() {
Ok( num ) => num,
Err(_) => -1
};
if -1 != inum {
tmps = format!( "{}{}", tmps, inum.to_string() );
}
}
// 数値へ変換
let namenum: u64 = match tmps.trim().parse() {
Ok( num ) => num,
Err(_) => 20
};
// それぞれ値の大小の比較
if "_".to_string() == firstc.to_string() {
if data_ < namenum {
data_ = namenum;
}
} else if "x".to_string() == firstc.to_string() {
if datax < namenum {
datax = namenum;
}
}
}
}
}
}
data.push( ( format!( "{}{}", "_".to_string(), data_.to_string() ), format!( "{}{}", "x".to_string(), datax.to_string() ) ) );
data
}
fn main() {
println!( "\n\n=>>> Welcome Search biggest number named File under directry!" );
let showstr = String::from( "Input target directry. ※need to full pass. q -> Quit. d -> default directry." );
// get_input はコマンドラインから read_line で入力を取得する独自関数、上にあり
let mut directry = get_input( showstr ).trim().to_string();
if "q" == directry {
println!( "=>>> Bye!" );
} else {
// d 一文字を入力された場合はデフォルトの文字列を使う
if "d" == directry {
directry = String::from( "c:\\Apache24\\htdocs\\react_tmp\\imgsx" );
println!( "Target directry is used default value : {}", directry );
}
if directry.len() > 5 {
let mut data_: u64 = 0;
let mut datax: u64 = 0;
match fs::read_dir( &directry ) {
Err( why ) => println!( "! {:?}", why.kind() ),
Ok( paths ) => {
let host: &str = directry.as_str();
for path in paths {
let fpath = path.unwrap().path();
if fpath.is_dir() {
let chil: Vec< ( String, String ) > = read_directry( fpath.as_path(), &host );
let mut subtmp_ = String::from( &chil[0].0 );
let mut subtmpx = String::from( &chil[0].1 );
subtmp_.remove(0);
subtmpx.remove(0);
let namenum_: u64 = match subtmp_.trim().parse() {
Ok( num ) => num,
Err(_) => 20
};
let namenumx: u64 = match subtmpx.trim().parse() {
Ok( num ) => num,
Err(_) => 20
};
if data_ < namenum_ {
data_ = namenum_;
}
if datax < namenumx {
datax = namenumx;
}
} else {
let flname = fpath.file_name().unwrap().to_string_lossy();
let subtmp = flname.chars().take(40).collect::<String>();
let firstc = subtmp.as_str().chars().nth(0).unwrap();
let mut tmps = String::from( "" );
for i in flname.chars() {
let inum: i8 = match i.to_string().parse() {
Ok( num ) => num,
Err(_) => -1
};
if -1 != inum {
tmps = format!( "{}{}", tmps, inum.to_string() );
}
}
let namenum: u64 = match tmps.trim().parse() {
Ok( num ) => num,
Err(_) => 20
};
if "_".to_string() == firstc.to_string() {
if data_ < namenum {
data_ = namenum;
}
} else if "x".to_string() == firstc.to_string() {
if datax < namenum {
datax = namenum;
}
}
}
}
println!( "\n\n{}, {}\n", format!( "{}{}", "_".to_string(), data_.to_string() ), format!( "{}{}", "x".to_string(), datax.to_string() ) );
}
}
}
}
}
php で同じ機能のもの
結果的に、これで目的は達成できているのだけれど、そのフォルダにあるファイルの数が多いせいかあまり速いという感じがしない。特に、Windows10 を立ち上げて初めてこのツールを動かした時は、プログラムが動き出すまでに異様に時間がかかる。これは、Rust 自体を起動している時間なのかと思ったりもしたが、しかし動かしたプログラムは build した後の exe ファイルであるからにして、Rust 本体を起動させる必要はないはずである。二度目以降はすぐにメッセージが表示され、一瞬の間の後、結果が表示されるようになる。
これ、php だったら?とやはり思い、試してみずにはいられなくなる。
で、ささっと php でも書いてみた。が、こちらはフォルダ情報の読み出しに再帰関数は使用していないので、あくまで、一階層だけのフォルダが対象ということになるが。
<?php
ini_set( 'memory_limit', '300M' );
// デフォルトの対象フォルダ
$tarfile = 'c:\\Apache24\\htdocs\\images\\imgsx';
$noarg = true;
// コマンドラインでフォルダを指定された場合にその値を取得
if ( isset ( $argv[1] ) ) {
$tarfile = $argv[1];
$noarg = false;
}
// コマンドラインで対象フォルダの引数を指定されなかった場合は、入力を促す
if ( $noarg ) {
echo "\n" . '<< Check the biggest number file\'s name from under the directry!! >>' . "\n\n";
echo 'Insert target directy : need full path.' . "\n";
$stdind = trim( fgets( STDIN ) );
if ( $stdind ) {
$tarfile = $stdind;
echo 'Target directry is < ' . $stdind . " >\n\n";
} else {// 入力がリターンだけの場合は、でデフォルトを使う
echo 'Target directry is default : < ' . $tarfile . " >\n\n";
}
}
$data_ = array();
$datax = array();
if ( file_exists( $tarfile ) ) {
echo 'target dir : ' . $tarfile . "\n\n";
$files = scandir( $tarfile );
if ( $files ) {
foreach ( $files as $file ) {
if ( '.' !== $file and '..' !== $file ) {
$filenum = ( int ) substr ( $file, 1, strpos( $file, '.' ) );
if ( '_' === $file[0] ) {
$data_[ $file ] = $filenum;
} elseif ( 'x' === $file[0] ) {
$datax[ $file ] = $filenum;
}
}
}
if ( $data_ ) {
echo "\n" . 'Result - "_" files count : ' . count ( $data_ ) . "\n";
echo "\n" . 'Result - "_" files max number : ' . '_' . ( string ) max ( $data_ ) . "\n";
} else {
echo "\n" . 'Result - "_" files none.' . "\n";
}
if ( $datax ) {
echo "\n" . 'Result - "x" files count : ' . count ( $datax ) . "\n";
echo "\n" . 'Result - "x" files max number : ' . 'x' . ( string ) max ( $datax ) . "\n";
} else {
echo "\n" . 'Result - "x" files none.' . "\n";
}
}
} else {
echo 'That file or directry is none.';
}
?>
php でも Windows を起動して初めて動かすときは、実際のプログラムが動き出して最初のメッセージが表示されるまでに少し時間がかかる。これはやはり、php 自体を起動している時間なのだろうと思う。が、Rust よりははるかに少ない時間である。実際にプログラムが動き出してから結果が表示されるまでも一瞬のことで、これにしても Rust よりも速いと感じる。???である。
どう考えてみても、Rust の方が速くないといけないはずなんだけれど・・・。ちなみにこの時に試験的に対象にしたフォルダは、一層に全てのファイルを入れてあり、そのファイルの数は 26,000 ほど。ひとつのフォルダに入っているファイルの数としてはちょっと多すぎるけれど、初めての起動でなければ、それでも php では一瞬で結果が出て、Rust ではわずかに間がある。
やはり、まだ始めたばかりなので、その書き方というか使うものをうまく使っていないのだろうと思う。そもそもこの程度の簡単なことに Rust は使うものではないようにも思う。個人的なツール程度のものなら、Rust などは不必要ということなのだろう。まぁ、あまり個人的には使い所もなさそうなので、とりあえずもう Rust はいいということにして、つぎは Go と戯れてみようと思ったりして。
Post : 2022/10/08 10:10
Comments feed
Trackback URL : https://strix.main.jp/wp-trackback.php?p=172582