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

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

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

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

2回答

2949閲覧

javascript ファイル作成時に同じ名前のファイルが存在していたら連番を付ける方法

---stax---

総合スコア148

Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

0クリップ

投稿2018/11/08 05:33

表題の件で質問させてください。
現在ひな形となるExcelファイルをコピーし、システムの日付を付与して新たなファイルを作成しています。
例20181106.xlsx, 20181107.xlsx, 20181108.xlsxのように作成

同じ日にもう一枚ファイルを作成したい場合があるのですが
例20181108.xlsx, 20181108_2.xlsx, 20181108_3.xlsxのように番号を付けたい
そのままでは同じ日付のファイルに上書きをしてしまいます。

配置するべきフォルダ内のファイルのリストを取得し、一番最後の要素を取得して同じ日付が付いているかの確認をさせて条件分岐させれば実現できるかと考えたのですが、もしすでに同じ日付が付いているファイルがあった場合に連番を付ける、という処理をどのようにすれば実現できるか分からず悩んでいます。
以下の自分のコードでは同じ日付のファイルが2枚目までは対応できるのですが3枚目以降の場合が実現できずアドバイスを頂きたいです。
自分なりに考えたのでもっと分かりやすいやり方があると思うのでそういったアドバイスも頂けたら嬉しいです。
初歩的な質問で申し訳ありませんが宜しくお願い致します。

js

1const fs = require('fs'); 2const moment = require('moment'); 3 4var date = moment().format('YYYYMMDD') 5//ファイル連番用に使う? 6file_num = 1; 7 8// コピー元ファイル(ひな形になるファイル) 9const src = 'C:\Users\Desktop\test\test_excel.xlsx'; 10 11// フォルダ内のファイル名一覧 12const filenames = fs.readdirSync("C:\Users\Desktop\test\11月"); 13console.log(filenames); 14 15//最後の要素取得 16latest_filename = filenames[filenames.length - 1]; 17console.log(latest_filename); 18console.log(date); 19 20//日付が一致する部分があるか確認 21if(latest_filename.includes(date)){ 22 //コピー先ファイル 23 const dest = "C:\Users\Desktop\test\" + month + "月\" + date + "_" + file_num + ".xlsx"; 24 //ファイル作成 25 fs.copyFileSync(src, dest); 26 27}else{ 28 //コピー先ファイル 29 const dest = "C:\Users\Desktop\test\" + month + "月\" + date + "_" + file_num + ".xlsx"; 30 //ファイル作成 31 fs.copyFileSync(src, dest); 32} 33

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

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

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

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

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

guest

回答2

0

while文で、ファイル名の存在チェックを行い、存在していればfile_num を+1していって存在しないものになるまでループを回せばいいかと。

投稿2018/11/08 06:36

y_waiwai

総合スコア87747

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

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

---stax---

2018/11/09 01:07 編集

回答ありがとうございます 返答遅くなってすみません 頂いた回答を元に以下のようなコードを作成しました // コピー元ファイル(ひな形になるファイル) const src = 'C:\Users\Desktop\test\test_excel.xlsx'; // フォルダ内のファイル名一覧 const filenames = fs.readdirSync("C:\Users\Desktop\test\11月"); var count = 0; while(count < filenames.filter(name => name.includes(date)).length){ file_num++ count++; } //コピー先ファイル const dest = "C:\Users\\Desktop\test\" + month + "月\" + date + "_" + file_num + ".xlsx"; //ファイル作成 fs.copyFileSync(src, dest); 連続で順番に作っていくと連番がついて発行されました。 ただ、この状態だとfile_1 ~ file6 までの6つのファイルを作成した状態でどれか一つを消した場合、次に作るものも6つ目のファイルとなりfile_6という名前が付くので上書きされてしまうような状態となります。 上で回答を頂いた方が指摘されている内容だと思います。 y_waiwai様から頂いた回答のファイル名の存在チェックを行うという部分が出来ていないためだと思うのですがどのようにwhile文でファイル名の存在チェックを書けばよいでしょうか・・・
miyabi-sun

2018/11/09 04:07

数を数えるのが本筋ではないので違います。 毎回仮のファイル名をwhile文の中身で生成してください。 「このファイル名で保存しようと思うんだけど、このファイル名は存在する?なーんだ、あるんだ。じゃあ次のファイル名候補作ってみるわ」という思想で設計してください。 whileは「while(true) {」という風に無限ループ一択です。 衝突しないファイル名の候補を見つけるのに成功したら while内で「break;」を使ってループから抜けてください。
guest

0

ベストアンサー

アヒルのおもちゃの人の回答で基本的にはうまく動作すると思いますが、
パズルとして考えた場合別のアプローチもありますね。

注目すべきコードはこれ。
const filenames = fs.readdirSync("C:\Users\Desktop\test\11月");

そのディレクトリ配下のファイルの一覧ですね。

JavaScript

1// 基礎調査 2"hogehoge".includes("hoge"); // true 3 4// だったらこういう結果取れるよね? 5filenames.filter(name => name.includes(date)); 6// ['20181108.xlsx', '20181108_2.xlsx', '20181108_3.xlsx'] 7 8// .lengthつければ同じ日付のファイルの数がわかるよね。 9filenames.filter(name => name.includes(date)).length; 10// 3 11 12// つまりこれで解決 13const length = filenames.filter(name => name.includes(date)).length; 14const fileName = length === 0 ? `${date}.xlsx` : `${date}_${length + 1}.xlsx`;

ただし、この場合日付がxxxxxxxxという8桁の数字なので、
同じ日にファイルを2000万個生成するケースで不具合が出そうなので注意してくださいね。
ちゃんと見張るならfilter内部の関数の条件式をもう少し詳細にすると良いでしょう。

また、今回はディレクトリにファイルを追加するのがこのスクリプト1個の想定ですので、
手作業で今日はまだファイルが作られていないのに、20181108_3.xlsxというファイルを放り込んでしまった場合、私のロジックでは2度目の実行でファイルを上書きしてしまうと思うので一手間加える必要があります。
forやwhileを使ったループ文でファイルのありなしを見ていく場合はそういった事はないと思うので、思うようなやり方で実装してみてください。

投稿2018/11/08 07:15

編集2018/11/08 07:27
miyabi-sun

総合スコア21158

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

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

---stax---

2018/11/09 02:28

回答ありがとうございます 返答遅くなってすみません 頂いた回答を元に以下のようなコードを作成しました // コピー元ファイル(ひな形になるファイル) const src = 'C:\Users\Desktop\test\test_excel.xlsx'; // フォルダ内のファイル名一覧 const filenames = fs.readdirSync("C:\Users\Desktop\test\11月"); var count = 0; while(count < filenames.filter(name => name.includes(date)).length){ file_num++ count++; } //コピー先ファイル const dest = "C:\Users\\Desktop\test\" + month + "月\" + date + "_" + file_num + ".xlsx"; //ファイル作成 fs.copyFileSync(src, dest); これだとご指摘いただいたように例えば、file_1 ~ file6 までの6つのファイルを作成した状態でfile6以外のどれか一つを消した場合、次に作るものも6つ目のファイルとなりfile_6という名前が付くので上書きされてしまうような状態となります。 これを回避するために今日の日付のファイル名の’_’以降の数字が一番大きい物を取得して、その数に+1してファイルを出力すればいいのかなと思うのですが、質問文にあるlatest_filenameで一番数字が大きいファイルを取得できるのですが、’_’以降の数字だけを切り抜けなくて悩んでいます。 スライスなども検討したのですが、ファイル名が●●●20181109_1のように日付の前に文字が加えられる可能性も考慮して文字の位置での指定ではなく’_’以降としたいです。 つたない質問で申し訳ないのですがアドバイス頂けたら嬉しいです
miyabi-sun

2018/11/09 04:23

JavaScriptの文字列は"20181112_12.xlsx".slice('_')のように、 `.xxxx()`の構文でメソッドを生やして文字列を加工できます。 MDNのドキュメントの左列の「メソッド」欄に羅列されているのでざっと読んでみてください。 https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String これで出来るだろうなと言う候補は「slice」「replace」「match」ですかね。 後ろ2つは正規表現の知識が必要になるのでもう少し勉強する必要があると思います。
---stax---

2018/11/09 06:20

回答ありがとうございます。 自分なりに考えていろいろやってみるのですがどうやっても思いつきません・・・ var count = 0; while(count < filenames.filter(name => name.includes(date)).length){ //var test = filenames.filter(name => name.includes(date))[count].replace('.xlsx', ''); //console.log(test); file_num++; var test = date + "_" + file_num + ".xlsx" console.log(test); if(filenames.indexOf(test) >= 0){ console.log('存在する'); file_num++; //コピー先ファイル const dest = "C:\Users\Desktop\test\" + month + "月\" + date + "_" + file_num + ".xlsx"; //ファイル作成 fs.copyFileSync(src, dest); console.log('存在しない') break; }else{ console.log('存在しない') break; } count++; } このようにしたのですが上手くいきませんでした。 2枚目と同時に3枚目が出来たり4枚目以降作れなかったり・・・ 仮のファイル名に見立てたものがvar testの部分なのですが回答頂いた次のファイル名候補作ってみるわという部分のイメージがわきません きっとほぼ答えに近いアドバイスをいただいていると思うのですがアドバイス頂けますでしょうか・・・
miyabi-sun

2018/11/09 06:49 編集

whileの中身は(true)一択、countもlengthも不要です。 今日のファイルが6個あって、6回目でループが終了したら出来るわけないでしょ??? なので無限ループを明示するtrue一択で、確実に抜けるようにbreak;を用意しておきましょう。 そしてif文の中身が相当やばいですね。 console.log('存在する')のすぐ下にconsole.log('存在しない')があってお前どっちやねん。 そしてすぐ上でfile_num++もあるから2回file_num足して2つ増えてるし、毎回breakしてるから1度しかループしないじゃん。 これ本当に考えてやってるんですか? まぁ考えてるんでしょうけど、 文章校生してる間に削除するつもりの文章がまるっと残したまま製本されたような酷い本みたいになってます。 私もよくやりますが、コードは書いてあるようにしか動きませんので、 こういう凡ミスは読めばすぐ気づくものなので定期的に読み直す癖をつけましょう。
---stax---

2018/11/09 07:31 編集

回答ありがとうございます。 先ほどの質問に一部消し忘れていたconsole.logなどぐちゃぐちゃな状態ですいませんでした。 頂いた回答を元に修正してみました while(filenames.filter(name => name.includes(date))){ var test = date + "_" + file_num + ".xlsx" if(filenames.indexOf(test) >= 0){ console.log('存在する'); file_num++; }else{ console.log('存在しない'); break; } } //コピー先ファイル const dest = "C:\Users\530325\Desktop\node-test\" + month + "月\" + date + "_" + file_num + ".xlsx"; //ファイル作成 fs.copyFileSync(src, dest); この状態だとfile1~file6まで連番での作成可能 file5を消した場合file5が生成される という状態になりました。 恐らくmiyabi-sun様が回答して頂いた想定と違うと思のでここは変という個所がありましたらアドバイス頂けたら嬉しいです
miyabi-sun

2018/11/09 10:53 編集

そもそもwhileを使っている時点で想定の「そ」の字もカスッてませんが、for文やwhile文を使ったロジックの方が実装が楽だと思うのでこれで良いと思います。 お疲れ様です。よく頑張りましたね。 実践的にはその位置でvar testと宣言すると、どこで作られた変数なのかが分からなくなるので、更に上部で空の文字列として宣言しておくと良いでしょう。 ついでにいうとconstを使っているのでletの方が自然ですね。 また、実際のコードではconsole.logで存在するorしないを表示する必要はありませんので、 更にコードをブラッシュアップさせて下記のようなコードが完成形に近い形になるでしょう let filename = ''; for (let file_num = 1; true; file_num++) { filename = date + "_" + file_num + ".xlsx"; if (filenames.indexOf(filename) < 0) break; }
---stax---

2018/11/13 01:10

返答遅くなって申し訳ありません。 回答ありがとうございます。 for文でfile_numの初期化だったりをしてしまえばすっきりしますね。 これだけの機能でも相当時間がかかってしまったのでもっと勉強しようと思います。 丁寧に回答して頂きありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問