IT女子がお届けするオフィスワーク効率化・VBA技術紹介

ノンプログラマーによるノンプログラマーのためのやさしい解説付き

スポンサーリンク

【ノンプロ研_お題】VBAでパスワードジェネレータを作ってみた(ランダムな文字列を作成)

2018年6月~ノンプロ研のSlackでスタートした「今週のお題」で作成したマクロの紹介です。

1つのお題に対して、各自の得意言語(VBA、GAS、Python)で解答を投稿してみんなでガヤガヤしています。

同じ言語の解答はもちろん、他言語の解答をみてヒントをもらったり、とても勉強になっています!

今週のお題

「パスワードジェネレータを作る」

f:id:excel-accounting:20180728151234p:plain:w300

毎回ざっくりした感じで、特に明記されてない部分については各自が仕様を自由に決めて作っています。

今回のお題は実務で使用できるレベルに仕上げてみますよ!

まずは仕様を決める

✔生成するパスワードの桁数は、ユーザが自由に設定できる

※ただし、8桁以上とする。

✔パスワードに使用する文字は下記の4種類とする。

  • アルファベット半角大文字
  • アルファベット半角小文字
  • 数字
  • 記号

※ただし、それぞれの文字を最低1つ以上含ませることとする。

それではコーディングしてみよう

処理ごとに解説を付けていきますよ~

まずは共通部品のFunctionプロシージャを作成します。

受け取った文字列から、ランダムに1文字抽出して返す機能です。

共通部品

Function GetRndChar(charX As String) As String

    Dim n As Long
    n = WorksheetFunction.RandBetween(1, Len(charX))
    GetRndChar = Mid(charX, n, 1)

End Function


このパスワードジェネレータは「いかにランダムで複雑な文字列を作成するか」が最大のポイントです。

候補の文字列の中からランダムな1文字を抽出する処理が繰り返し出てくるので、この処理を部品化しておきます。

【処理1】桁数の設定と形式チェック

Dim digit As Variant
digit = InputBox("何桁のパスワードを生成しますか??(※8桁以上必要です)")

If IsNumeric(digit) = False Or digit < 8 Then
    MsgBox "8以上の数字を入力してください", vbExclamation: End
End If

ユーザが自由に桁数設定できるよう、InputBox関数の返り値を使用します。

★ポイント★
なぜ、変数digitをLong型ではなくVariant型にしている?

ユーザが誤って数値以外を入力した場合、Long型だと型不一致でエラーとなります。

f:id:excel-accounting:20180728144236p:plain:w450

実務で利用することを想定するとなるべくエラーを発生させないようにしたいですよね。

そのため、いったんはVariant型でどのような文字列でも受け付けて、次の処理で形式チェックをします。

8以上の数字でない場合は、MsgBoxで警告を出して、マクロ終了とします。

【処理2-1】パスワード候補の文字列を設定

Dim char1 As String, char2 As String, char3 As String, char4 As String
char1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
char2 = "abcdefghijklmnopqrstuvwxyz"
char3 = "0123456789"
char4 = "!@#$%^&*_+-?"

下記の4種類を準備します。

  • アルファベット半角大文字
  • アルファベット半角小文字
  • 数字
  • 記号

ひとつの変数にまとめず、char1~4に分けているのは理由があります。
詳しくは【処理3】で。

【処理2-2】パスワード文字列を格納する配列変数を準備

Dim arrPass() As String
ReDim arrPass(digit - 1)

配列変数の要素数もあわせて宣言したいのですが、要素数の指定に変数は使用できない決まりです。

Dim arrPass(digit - 1) As Stringと記述すると…

f:id:excel-accounting:20180728145053p:plain:w300


そのため、

動的配列を宣言してから、要素数を設定する流れにしています。


なお、配列の要素数は0から始まるのでdigit - 1です。

ここからは、8ケタのパスワードを生成することを想定して解説していきます。

配列変数arrPass
f:id:excel-accounting:20180728150132p:plain

【処理3】必須文字の取得

arrPass(0) = GetRndChar(char1) '大文字
arrPass(1) = GetRndChar(char2) '小文字
arrPass(2) = GetRndChar(char3) '数字
arrPass(3) = GetRndChar(char4) '記号

共通部品GetRndCharに文字列を渡し、返ってきた1文字を配列変数に順番に格納していきます。

大文字・小文字・数字・記号を最低1文字以上含ませるために、候補文字列をchar1~4で分けておいたのです。

(イメージ)

f:id:excel-accounting:20180729151331p:plain

【処理4】残りの桁数分をランダムに取得

Dim i As Long
For i = 4 To digit - 1
    arrPass(i) = GetRndChar(char1 & char2 & char3 & char4)
Next i

残りの5ケタ目~8ケタ目(配列の要素数4~7)に文字列を設定します。

候補文字のいずれかをランダムに1文字取得すればよいので、候補の文字列をまとめて(結合して)GetRndCharに渡しています。

(イメージ)

f:id:excel-accounting:20180728162407p:plain

【処理5】全体を適当にシャッフル

Dim password As String
password = ShuffleChar(arrPass)

最後に配列の中身をランダムにシャッフル(入れ替え)します。

必須文字を取得したので、要素数0~3が大文字・小文字・数字・記号の順番で固定されているためです。

念には念を、徹底的にランダムな文字列を作りましょう!

Function ShuffleChar(ByRef arrChar() As String) As String
'機能:受け取った配列の値を適当に並び替えて文字列形式で返す

    Dim i As Long
    For i = LBound(arrChar) To UBound(arrChar) '要素数の回数分適当に並び替える
        
        Dim x As Long
        x = WorksheetFunction.RandBetween(LBound(arrChar), UBound(arrChar))
        
        Dim tmp As String '一時変数を利用して入替
        tmp = arrChar(x)
        arrChar(x) = arrChar(i)
        arrChar(i) = tmp
        
    Next i
    
    '配列変数を文字列形式になおす
    Dim j As Long, retPass As String
    For j = LBound(arrChar) To UBound(arrChar)
        retPass = retPass & arrChar(j)
    Next j
    
    ShuffleChar = retPass

End Function


この関数の一番のポイントは、For~Next文での入れ替え処理ですね。

Dim tmp As String '一時変数を利用して入替
tmp = arrChar(x)
arrChar(x) = arrChar(i)
arrChar(i) = tmp


具体的な処理の流れをみていきましょう。

For文1回目(i = 0)

arrPass(0)の値とarrPass(x)の値を入れ替えます。

変数xには、RandBetween関数で要素数の下限~上限の数字が毎回ランダムに格納されます。
(今回の場合、0~7)

ここではx = 3とします。

f:id:excel-accounting:20180728162802p:plain

うっかりこのように書いてしまいそうですが、
arrPass(0) = arrPass(3)
arrPass(3) = arrPass(0)

これじゃ入れ替えできませんね。
f:id:excel-accounting:20180601142920p:plain:w150

そこで、一時変数tmpを用意します。


【手順①】arrChar(3)の値を一時変数に格納(退避)する
tmp = arrChar(x)(x = 3)
f:id:excel-accounting:20180728163918p:plain


【手順②】arrChar(0)の値をarrChar(3)に格納する
arrChar(x) = arrChar(i)(x = 3, i = 0)
f:id:excel-accounting:20180728163930p:plain


【手順③】一時変数に退避しておいたarrChar(3)の値をarrChar(0)に格納する
arrChar(i) = tmp(i = 0)
f:id:excel-accounting:20180728163942p:plain


これでarrPass(0)の値とarrPass(3)の値を入れ替えることができました。


For文2回目(i = 1)

arrPass(1)の値とarrPass(x)の値を入れ替えます。

xには同様に0~7のランダムな値が格納されます。

 :
 :

このようにi = 7まで値の入れ替え処理を繰り返します。


配列の入れ替えが終わったら、配列の値を結合して一つの変数(retPass)に格納します。

【処理6】生成された文字列を出力

Debug.Print digit & "桁のパスワード生成しました。" & password

イミディエイトウィンドウに出力します。

コードまとめ

Sub GeneratePassword()

    '【1】生成するパスワードの桁数を設定
    Dim digit As Long
    digit = InputBox("何桁のパスワードを生成しますか??(※8桁以上必要です)")
    
    If IsNumeric(digit) = False Or digit < 8 Then
        MsgBox "8以上の数字を入力してください", vbExclamation: End
    End If
    
    '【2-1】パスワード候補の文字列を設定
    Dim char1 As String, char2 As String, char3 As String, char4 As String
    char1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    char2 = "abcdefghijklmnopqrstuvwxyz"
    char3 = "0123456789"
    char4 = "!@#$%^&*_+-?"
    
    '【2-2】パスワード文字列を格納する配列変数を準備
    Dim arrPass() As String
    ReDim arrPass(digit - 1)
    
    '【3】必須文字の取得(大文字・小文字・数字・記号を最低1文字以上含ませるため)
    arrPass(0) = GetRndChar(char1) '大文字
    arrPass(1) = GetRndChar(char2) '小文字
    arrPass(2) = GetRndChar(char3) '数字
    arrPass(3) = GetRndChar(char4) '記号
    
    '【4】残りの桁数分を、すべての候補文字からランダムに取得
    Dim i As Long
    For i = 4 To digit - 1
        arrPass(i) = GetRndChar(char1 & char2 & char3 & char4)
    Next i
    
    '【5】要素番号0~3に規則性ができてしまうため、全体を適当にシャッフル
    Dim password As String
    password = ShuffleChar(arrPass)
    
    '【6】出力
    Debug.Print digit & "桁のパスワード生成しました。" & password

End Sub

Function GetRndChar(charX As String) As String
'機能:受け取った文字列からランダムに1文字抽出して返す

    Dim n As Long
    n = WorksheetFunction.RandBetween(1, Len(charX))
    GetRndChar = Mid(charX, n, 1)

End Function

Function ShuffleChar(ByRef arrChar() As String) As String
'機能:受け取った配列の要素を適当に並び替えて文字列形式で返す

    Dim i As Long
    For i = LBound(arrChar) To UBound(arrChar) '要素数の回数分適当に並び替える
        
        Dim x As Long
        x = WorksheetFunction.RandBetween(LBound(arrChar), UBound(arrChar))
        
        Dim tmp As String '一時変数を利用して入替
        tmp = arrChar(x)
        arrChar(x) = arrChar(i)
        arrChar(i) = tmp
        
    Next i
    
    '配列変数を文字列形式になおす
    Dim j As Long, retPass As String
    For j = LBound(arrChar) To UBound(arrChar)
        retPass = retPass & arrChar(j)
    Next j
    
    ShuffleChar = retPass

End Function


いかがでしたか?

今回紹介したこの2つは実務でも流用できそうですね。

✔ランダムな文字列を作成する処理

✔配列の値を入れ替える処理(文字列をランダムに並び替える処理)


プログラミングの練習として書いてみたコードを下記のカテゴリで紹介しています。

こちらもぜひ見ていってくださいね~!

スポンサーリンク