質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Rust

Rustは、MoFoが支援するプログラミング言語。高速性を維持しつつも、メモリ管理を安全に行うことが可能な言語です。同じコンパイル言語であるC言語やC++では困難だったマルチスレッドを実装しやすく、並行性という点においても優れています。

Q&A

解決済

1回答

518閲覧

Rustにてフォルダ内のファイル数を事前に把握してからfor inを回したい

watya1

総合スコア4

Rust

Rustは、MoFoが支援するプログラミング言語。高速性を維持しつつも、メモリ管理を安全に行うことが可能な言語です。同じコンパイル言語であるC言語やC++では困難だったマルチスレッドを実装しやすく、並行性という点においても優れています。

0グッド

0クリップ

投稿2023/06/07 07:08

編集2023/06/07 07:33

実現したいこと

ここに実現したいことを箇条書きで書いてください。

  • フォルダ内の特定の拡張子ファイルに対して処理を行う
  • どのくらいのファイル数があり、どのくらいの数を処理したのか表示をしたい

前提

勉強を兼ねてRustでCLIツールを作っているのでRustで作りたいです。
表示に関してindicatifクレートを使う予定です
フォルダ内のファイル捜査に関してはread_dirを使っています。

発生している問題・エラーメッセージ

rust

1error[E0382]: use of moved value: `files` 2 --> src\main.rs:35:17

ファイル数を把握する時点で所有権が移ってしまっています。
所有権を移さずに個数を把握することはできないのでしょうか?

該当のソースコード

rust

1fn main() -> Result<(), Box<dyn Error>> { 2 println!("ファイルをまとめます"); 3 // エクセルファイルが格納されているフォルダのパス 4 let folder_path = r"\example"; 5 let files = read_dir(folder_path)?; 6 7 // 出力先のパス 8 let output_path = r"\example.csv"; 9 // csv::WriterBuilderに渡す前にBOMを書き込んでおく。 10 11 println!("保存フォルダ:{}",folder_path); 12 println!("ファイル作成中...:{}",output_path); 13 14 let output = File::create(output_path)?; 15 let mut wtr = BufWriter::new(output); 16 wtr.write_all(BOM)?; 17 18 let mut wtr = WriterBuilder::new() 19 .terminator(Terminator::CRLF) 20 .from_writer(wtr); 21 22 let max_count = files.cloned(); 23 24 let mut is_head_writed = false; 25 // フォルダ内の各Excelファイルに対して処理を実行 26 for file in files { 27 let file_path = file?.path(); 28 if let Some(extension) = file_path.extension() { 29 // 拡張子がxlsxのファイルのみ処理する 30 if extension == "xlsx" { 31 // エクセルファイルを開く 32 let mut workbook: Xlsx<_> = open_workbook(&file_path)?; 33 workbook 34 .load_tables() 35 .unwrap_or_else(|err| eprintln!("IO Error -> {}",err)); 36 37 if let Some(Ok(table)) = workbook.table_by_name("exampleTable"){ 38 if false == is_head_writed { 39 wtr 40 .write_record(table.columns()) 41 .unwrap_or_else(|err| eprintln!("IO Error -> {}",err)); 42 is_head_writed = true; 43 } 44 for row in table.data().rows(){ 45 if "" != row[1].to_string() { 46 wtr 47 .write_record(row.iter().map(|f|f.to_string()).collect::<Vec<_>>()) 48 .unwrap_or_else(|err| eprintln!("IO Error -> {}",err)); 49 }else { 50 // データが空なのでskip 51 } 52 } 53 } 54 } 55 } 56 } 57 wtr.flush()?; 58 println!("作成完了!"); 59 Ok(()) 60}

試したこと

以下のようにしたら実現するのですが、
カウント時とファイル処理時の2回、フォルダ捜査と絞り込みを行っているのが気に食わないです。

rust

1fn main() -> Result<(), Box<dyn Error>> { 2 println!("ファイルをまとめます"); 3 // エクセルファイルが格納されているフォルダのパス 4 let folder_path = r"\example"; 5 6 // 出力先のパス 7 let output_path = r"\example.csv"; 8 // csv::WriterBuilderに渡す前にBOMを書き込んでおく。 9 10 println!("保存フォルダ:{}",folder_path); 11 println!("ファイル作成中...:{}",output_path); 12 13 let output = File::create(output_path)?; 14 let mut wtr = BufWriter::new(output); 15 wtr.write_all(BOM)?; 16 17 let mut wtr = WriterBuilder::new() 18 .terminator(Terminator::CRLF) 19 .from_writer(wtr); 20 21 let file_count: u64 = read_dir(folder_path)? 22 .filter_map(|entry| { 23 let path = entry.ok()?.path(); 24 if path.extension().map_or(false, |ext| ext == "xlsx"){ 25 Some(path) 26 }else { 27 None 28 } 29 }) 30 .count() as u64; 31 let pb = ProgressBar::new(file_count); 32 33 let mut is_head_writed = false; 34 35 // フォルダ内の各Excelファイルに対して処理を実行 36 for file in read_dir(folder_path)? { 37 let file_path = file?.path(); 38 if let Some(extension) = file_path.extension() { 39 // 拡張子がxlsxのファイルのみ処理する 40 if extension == "xlsx" { 41 // エクセルファイルを開く 42 let mut workbook: Xlsx<_> = open_workbook(&file_path)?; 43 workbook 44 .load_tables() 45 .unwrap_or_else(|err| eprintln!("IO Error -> {}",err)); 46 47 if let Some(Ok(table)) = workbook.table_by_name("exampleTable"){ 48 if false == is_head_writed { 49 wtr 50 .write_record(table.columns()) 51 .unwrap_or_else(|err| eprintln!("IO Error -> {}",err)); 52 is_head_writed = true; 53 } 54 for row in table.data().rows(){ 55 if "" != row[1].to_string() { 56 wtr 57 .write_record(row.iter().map(|f|f.to_string()).collect::<Vec<_>>()) 58 .unwrap_or_else(|err| eprintln!("IO Error -> {}",err)); 59 }else { 60 // データが空なのでskip 61 } 62 } 63 } 64 } 65 } 66 thread::sleep(Duration::from_millis(5)); 67 pb.inc(1); 68 } 69 wtr.flush()?; 70 pb.finish_with_message("done"); 71 println!("作成完了!"); 72 Ok(()) 73}

補足情報(FW/ツールのバージョンなど)

[dependencies]
calamine = "0.20.0"
csv = "1.2"
indicatif = "0.15.0"

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hoshi-takanori

2023/06/07 20:16

Rust よく分かりませんが、read_dir の結果は Iterator っぽいので、個数カウントを含め、一度読み込んだら消費済みになるのかも。配列か何かに読み込んで処理すれば良いのでは?
watya1

2023/06/08 00:06

コメントありがとうございます。 ひとまず試してみましたがいけそうです。 基本的にイテレータは1回こっきりで消費されるみたいですね。 覚えておきます。
guest

回答1

0

自己解決

以下のコメントをいただき、試したところうまくいきました

read_dir の結果は Iterator っぽいので、 個数カウントを含め一度読み込んだら消費済みになるのかも。 配列か何かに読み込んで処理すれば良いのでは?

rust

1fn main() -> Result<(), Box<dyn Error>> { 2 println!("棚卸し原表をまとめます"); 3 // エクセルファイルが格納されているフォルダのパス 4 let folder_path = r"\example" 5 6 // 出力先のパス 7 let output_path = r"\example.csv" 8 // csv::WriterBuilderに渡す前にBOMを書き込んでおく。 9 10 println!("保存フォルダ:{}",folder_path); 11 println!("ファイル作成中...:{}",output_path); 12 13 let output = File::create(output_path)?; 14 let mut wtr = BufWriter::new(output); 15 wtr.write_all(BOM)?; 16 17 let mut wtr = WriterBuilder::new() 18 .terminator(Terminator::CRLF) 19 .from_writer(wtr); 20 21// -----------------------↓↓ここでイテレータ取得/xlsxが含まれるファイル走査/配列にmove/を行う 22 let files_path = read_dir(folder_path)? 23 .filter_map(|entry| { 24 let path = entry.ok()?.path(); 25 // 拡張子がxlsxのファイルのみを対象とする。 26 if path.extension().map_or(false, |ext| ext == "xlsx"){ 27 Some(path) 28 }else { 29 None 30 } 31 }) 32 .collect::<Vec<_>>(); 33// ---------------------------------------------------------------------------------------------------------- 34 35 let pb = ProgressBar::new(files_path.len() as u64); // ← moveされない! 36 let mut is_first_file = true; 37 38 // フォルダ内の各Excelファイルに対して処理を実行 39 for file_path in files_path { 40// -----------------------↓↓files_pathはxlsxのpathのみになっているので条件分岐も必要なくなった。moveもされていない。 41 // ファイル処理 42 let mut workbook: Xlsx<_> = open_workbook(&file_path)?; 43 workbook 44 .load_tables() 45 .unwrap_or_else(|err| eprintln!("IO Error -> {}",err)); 46 47 if let Some(Ok(table)) = workbook.table_by_name("T_Data"){ 48 // 最初のファイルだけヘッダを取り込む 49 if is_first_file { 50 wtr 51 .write_record(table.columns()) 52 .unwrap_or_else(|err| eprintln!("IO Error -> {}",err)); 53 is_first_file = false; 54 } 55 for row in table.data().rows(){ 56 if "" != row[1].to_string() { 57 wtr 58 .write_record(row.iter().map(|f|f.to_string()).collect::<Vec<_>>()) 59 .unwrap_or_else(|err| eprintln!("IO Error -> {}",err)); 60 }else { 61 // データが空なのでskip 62 } 63 } 64 } 65 thread::sleep(Duration::from_millis(5)); 66 pb.inc(1); 67// ---------------------------------------------------------------------------------------------------------- 68 } 69 wtr.flush()?; 70 pb.finish_with_message("done"); 71 println!("作成完了!"); 72 Ok(()) 73}

投稿2023/06/08 00:14

watya1

総合スコア4

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問