えくせるちゅんちゅん

ことりがエクセルをちゅんちゅんするブログ

ExcelVBAで重複しないファイル名の生成方法について考えてみた

今回はVBAで重複しないファイル名を作成する方法について考えてみました。

f:id:Kotori-ChunChun:20190622011958p:plain


きっかけ

ちょっと面白いネタだったので、今後の参考資料としてメモ

一部論外のものがあるものの、各種方法には一長一短があるので、状況に応じて使い分けると良い。


コード紹介

とりあえず確認用に作ってみたソースコード

カレントディレクトリにテキストデータを書き出したい場合は、カレントディレクトリをブックのパスへを実行すること

一部の情報の取得については関数化されているので、そのままコピペしても動かないが、検索すれば出てくるのでここでは記載しない。

Option Explicit

Dim fso As New FileSystemObject

Function GetFileName(Key As String) As String
    GetFileName = "hoge_" & Format(Now, "yyyymmddhhnnss") & "_" & Key & ".txt"
End Function

Sub CreateTestFile(Key As String)
    fso.CreateTextFile GetFileName(Key), False
End Sub

Sub カレントディレクトリをブックのパスへ()
    ChDir ThisWorkbook.Path
End Sub
    
Sub YMDHMS()
    Call CreateTestFile("")
End Sub

Sub Random()
    Randomize
    Call CreateTestFile(Int(Rnd * 1000))
End Sub

Sub OfficeUserName()
    Call CreateTestFile(Application.UserName)
End Sub

Sub OfficeWindowHandle()
    Call CreateTestFile(Application.Hwnd)
End Sub
    
Sub NewworkUserName()
    Call CreateTestFile(CreateObject("WScript.Network").UserName)
End Sub
    
Sub GUID()
    Call CreateTestFile(GetGUID())
End Sub
    
Sub IPAddress()
    Call CreateTestFile(Get_IPAddress())
End Sub
    
Sub MacAddress()
    Call CreateTestFile(Replace(Get_MacAddress(), ":", "-"))
End Sub

Sub DoLoop()
    Do
        If Not fso.FileExists(GetFileName(Key)) Then
            fso.CreateTextFile GetFileName(Key), False
        End If
        Application.Wait [Now() + "00:00:00.5"]
    Loop
End Sub


各種方法の説明

基本形(YYYYMMDDHHMMSS)

とりあえず重複を防ぐための方法として、一番楽なのが年月日時分秒を付ける方法。

この方法だと書き込み頻度が高い場合は衝突する危険性がある。


乱数

お手軽なのが日付に乱数を付与する方法

桁数を増やせば事故率が皆無となるまで精度を上げる事も可能。ある程度の桁数なら日付は必要ないが。

実行する度に値が変わるので、「再保存」に対応するにはスタックしておく必要があるのが厄介か。

当然、ファイルから保存者の特定もできない。(前回保存者から特定出来る場合もあるが)


Officeのユーザー名

Application.UserNameを使って、Officeの設定で決めたユーザー名を取得する方法

この設定は自由に変える事ができ、職場環境などによっては衝突することがままある。

  • 社内統一、部内統一
  • イニシャルのみの記載
  • 内線番号の記載(この方法は、読み取り専用となった際に、とても、とても合理的だと思う)


ウィンドウハンドル

  • Application.Hwndを使って、現在起動中のExcelのウィンドウハンドルを取得する方法

  • 日付と組み合わせればまず衝突することはない。

  • 衝突の起こりやすいマルチプロセスの際も別のハンドル値がつくので、安心してゴリゴリ回せる。

  • 保存者の特定は別途ハンドル情報を書き出しておかないと難しい。(前回保存者からry)


PCのユーザー名

  • CreateObject("WScript.Network").UserName)などを使って、PC/ネットワークユーザー名を取得する方法

  • 誰が保存したのか一目で分かるので、ユーザビリティに最も優れていると思う。

  • 欠点は上記スペルを暗記する必要があることと、マルチプロセス非対応となる点か。


GUID

  • APIを駆使して絶対に重複の発生し得ないGUIDを生成する方法
  • こんなの誰も求めてない。
  • ネタとしか言わざる負えない。


MACアドレス

  • パソコンのLANアダプタのMACアドレス(製造番号的なにか)を使った方法
  • これも誰も求めてない。


IPアドレス

  • 同上につき以下略


後半のは

  • 生成プログラムが長い
  • 生成された文字列が長い
  • 根本的に本件に適していない。

という感じなので、基本的には論外ですね。


Do~Loopで保存に成功するまで繰り返す

「絶対にエラーを起こさず保存したい」という話であれば、究極的に言えばこれしか無いでしょう。

エクスプローラで良くある(2)(3)を付与していくような付け方をお求めなら、

akashi-keirin.hatenablog.com

が参考になるかもしれません。


各手法の比較

重複リスク マルチプロセス 保存者の特定 手軽さ
日時 × ×
乱数4桁 1/10000 ×
Officeのユーザー名 結構ある × △1
アプリケーションハンドル 16711657通り △2
PCのユーザー名 皆無 × △3
GUID 皆無 × ×
MACアドレス 皆無 × ×
IPアドレス 皆無 × ×
Do~Loop × ×

△1 : ユーザー名が重複する環境では特定できない。

△2 : ウィンドウハンドルとユーザー名の対応表を記したログも合わせて書き出さないと特定出来ない。

△3 : 構文は短いので覚えちゃえばOK。

重複リスク

日時のみの場合に重複は「有」、Do~Loopで保存に成功するまで巡回する方法は「無」と決めて、その範囲内で重複リスクの度合いを適当に書いてみました。

ウィンドウハンドルは16711657通りと書きましたが、偏りがあって1/16711657とはならないだろうという予想から分数にはしていません。実態がどうなのかまでは分かりません。

マルチプロセス

こんな事をするのは稀だと思いますが、単一PCでマルチプロセスで並行処理している場合もあります。

そんな時はプロセスごとに異なる文字列を生成しないと、衝突してエラーになる恐れがあります。

保存者の特定

後から保存したのが誰か確認する必要がある場合は、確認可能な方法を使うべきでしょう。

例えば、同じ人間からの保存は最後の1つだけ。と言った場合。

例えば、同じプロセスで繰り返し上書き保存している場合は、古いファイルは消したい。と言った場合。

一方で特定されたくない場合。特定できる必要がない場合は、もっとお手軽な方法でも良さそうです。

既に述べているようにExcelブックなどであれば、「前回保存者」というファイルのプロパティから特定する手法もあるにはある。


まとめ

ただ単に重複を防ぐなら好きな方法でどうぞ。

  • ファイル名を見て誰か識別したい時は「ユーザー名」

  • なんでも良いから重複を回避させたい場合は「ウィンドウハンドル」

が楽なような気がします。

以上


何か御座いましたらコメント欄、またはTwitterからどうぞ♪

それではまた来週♪ ちゅんちゅん(・8・)