[[20180511013843]] 『正規表現を使って0埋めパディング しコードの安定』(かず) ページの最後に飛ぶ

[ 初めての方へ | 一覧(最新更新順) | 全文検索 | 過去ログ ]

 

『正規表現を使って0埋めパディング しコードの安定性を確保したい』(かず)

正規表現を使って0埋めパディング
しコードの安定性を確保したい

最小1桁最大3桁の自然数の文字列を左0埋め
パディングしたいです。
例えば
1→001
99→099
としたいです

桁数が固定で
Dim s AS string
s = right("000" & s, 3)
のようなコードをよ時々使っています。

今般、古いURLを新しいURLに変換する必要に
迫られ、繰り返し処理が必要なので正規表現を
使って対応する必要があります。

ゆくゆくマクロでシートに記載した正規表現の文字列を
与えて、ExecuteやReplace メソッドで使おうという
方向です

1.現状のコード

  Dim reg As Object
 Dim s As String

  Set req = CreateObject("Microsoft.XMLHTTP")    
  Set reg = CreateObject("VBScript.RegExp")

  With reg
     .Global = True
  ' ◆
    .Pattern = "(\d{1,3}\b)" '\d数字が最小1桁最大3桁 
               ' \b終りが空白やタブ単語の切れ目
                  ' ()でグルーピング 後で$1として使用
  s = .Replace(s,"000$1")
        .Pattern ="\d+(\d{3}$)" '000d〜000ddd (\d)$で後ろから3桁数字
                ’をグルーピング
  s = .Replase(s, "$1") 
  End With

  としました。これで sが55なら 055にゼロ埋め可能です。
 (これだけならRight関数の方が単純でいいでしょう...)

2.改善案? 
 ゼロ埋めして合わせたい桁数nを、定数ではなく変数として
 引数で与えたいです。
 
 
 .Pattern = "(\d{1,n}\b)" ' 最大N桁 
 s = .Replace(s,"0000000$1") ' 左の0の個数>N の個数

                'となるよう十分にゼロ埋め

   .Pattern ="\d+(\d{N}$)"   ' 実際にはNは変数として扱われていない。
  s = .Replase(s, "$1") 

  s = .Replace(s,"000000$1")

        .Pattern ="\d+(\d{N}$)"  ' 000d〜000dddを(\d)$で後ろN桁を識別
  s = .Replase(s, "$1") 
 
  上のように 正規表現に変数として桁数を指定できる
  ようにする方法をご存知の方が入ましたら是非教えてください 

< 使用 Excel:Excel2016、使用 OS:Windows10 >


ものすごく釈迦に説法な気がしますけど作戦変えて単純にString関数かワークシート関数のRept関数つかうっていうのはどうでしょうか?

    Sub Sample()
        Const MySTR As String = "123"

        Debug.Print "5桁"
        Debug.Print String(5 - Len(MySTR), "0") & MySTR
        Debug.Print WorksheetFunction.Rept("0", 5 - Len(MySTR)) & MySTR

        Debug.Print vbCrLf

        Debug.Print "3桁"
        Debug.Print String(3 - Len(MySTR), "0") & MySTR
        Debug.Print WorksheetFunction.Rept("0", 3 - Len(MySTR)) & MySTR

    End Sub

【参考】
http://www.moug.net/tech/exvba/0140035.html

(もこな2) 2018/05/11(金) 09:36


 >.Pattern = "(\d{1,n}\b)" ' 最大N桁
 これだと、長さが n か n以上の数値の末尾の n桁の数値が対象になりますよ?

 サンプル

 Sub test()
     MsgBox GetFormatNumber("avc12ccc2" & vbCrLf & "12aaa2ddd12345", 3)
 End Sub

 Function GetFormatNumber(ByVal txt As String, ByVal n As Long) As String
     Dim i As Long, mtch As Object, m As Object
     With CreateObject("VBScript.RegExp")
         .Global = True: .MultiLine = True
         .Pattern = "(^|\D)(\d+)(?=\D|$)"
         Set mtch = .Execute(txt)
         If mtch.Count Then
             For i = mtch.Count - 1 To 0 Step -1
                 Set m = mtch(i)
                 txt = Application.Replace(txt, m.firstindex + Len(m.submatches(0)) + 1, _
                 Len(m.submatches(1)), Format$(m.submatches(1), String(n, "0")))
             Next
         End If
     End With
     GetFormatNumber = txt
 End Function

(seiya) 2018/05/11(金) 11:48


もこな2さん seiyaさん
 かずです。見ず知らずの私のために貴重な時間を割いて頂いて有難うございます。
 とても勉強になります。
 
■もこな2さんのコード
 ワークシート関数String関数やRept関数など知りませんでした
 教えて頂いたWepページで
 ・String関数:文字コードや文字列の1文字目を、指定回数繰り返す
 ・rept 関数: 文字列を 指定関数繰り返す
 など知りませんでした
 
 これらの関数を 
 WorksheetFunction.Rept("0", 5 - Len(MySTR)) & MySTR
 などと利用して 0埋めできるのですね。
 有難うございます。今後ユーザ定義関数などにして活用したいと思います。

■seiya さんのコード
 "(^|\D)(\d+)(?=\D|$)" の (?=\D|$) の意味は
 正規表現の構文

  https://msdn.microsoft.com/ja-jp/library/cc392020.aspx
  に
 "Windows(?=95|98|NT|2000)" は "Windows2000 " の "Windows"
  には一致しますが、"Windows3.1" の "Windows " には一致し
 ませんとありどうにか理解できました。

 (\d+)(?=\D|$) は \d+で 1個以上の数字 例 12345
 に続いて \D 数字以外か$ 行末 があるときパターンに一致
 と言う意味ですね。
 
 これが
 1番目のグループ(^|\D)      行頭か数字以外の文字
 2番目のグループ(\d+)(?=\D|$)  1個以上数字で、数字以外か行末が続く
 としてExecuteメソッドで得られるmtchオブジェクトで、mtch.Count +1 個
 それぞれで2つのSubmatchesプロパティが得られる
 
 マッチングした箇所をmtch(i)→mとして (mtch.Count -1) から0 まで
 ループでnで指定された桁数でゼロ埋め処理を行う

 ループをmtch.Count -1から0までmtch(i)を後方から処理するのはtxtを後ろから
 処理=マッチングで検知した文字列をリプレース関数で別の文字列に置き換えるの 
 ことを考慮している 例えば10文字目から2文字の数字を0埋めして3文字にしたら、 今までの12文字目は13文字目の位置になるので、Executeメソッドで得られた夫々の
 数字の箇所の位置 m.firstindexを利用するため。

 mのsubmatchesは最初に指定した2個のグループで引数は最初0と1で扱う
 
 ワークシート関数 REPLACE(元の文字列,開始位置,文字数,置換文字)なので
 元の文字列 txt

  開始位置  m.firstindex+len(m.submatches(0)) +1 
              → 今処理しているマッチング箇所が
         ・・2ddd12345 の所なら 
         m.firstindex は2の所
         dddが submatches(0)
                 12345が submatches(1) なので
         m.firstindex + Len(m.submatches(0)) + 1
         が変更の開始位置 
 文字数 : 12345の長さlen(submatches(1)) 
  置換文字: Format$ は戻り値がStringと明示したFormat関数
       gogle でWebサイト調べましたが解説見つからず
       結局エクセルの学校に戻ってseiyaさんの解説見つけました。
       この場合はformat関数のままでも問題なかったです
       戻り値がFormat関数の戻り値variantのままだとエラー
       になったりすることもあるのでしょうか?

       Sting(n,"0") はn個の"0"なので

              m.submatches(1)を n個"0"の形で整形
       これをReplace関数で処理ということですね
 
 実機で試してみるとnが処理対象の数字列より小さいとFormat関数は
 何もしないので、例えば n = 1 の時 ddd12345の12345 は12345
  のまま 他の数字も同様。
 
 振り返ってみると n桁の数字にゼロ埋めすると言うとき現状の数字
 データの桁数がそうなっているかは意識していなくて、処理の手順
 のイメージが数字の前に"0"をつければいいとしか考えていませんでした
 単に3桁ゼロ埋めにしようと言うとき、3桁未満はこうする、3桁以上は
 そのままなのか、切り詰めるのかなど明確にしてからコード化しない
 といけないのですね。要求仕様を吟味して設計して実装しないと手戻り
 してしまうと認識しました。

 本当に色々と勉強になりました。
 ちなみに皆さんは、コードに注釈を書かないのは単に投稿なので入れて
 いないだけであって、ご自分のお仕事で他の人が保守するツールのコード
 にはコメントを入れていらっしゃるのでしょうか?
 今回 正規表現 を使ったコードを初めて書いて、パターンの書き方など
 勉強して、自分でも2、3月もすれば細かいルール忘れてしまうと思いました
 
 自衛策としてツールに逐次的にコメント書いておこうと思った次第です
 皆さんはどのようにされているか参考に教えて頂ければ助かります
 
 以上
(かず) 2018/05/13(日) 00:44


 > ちなみに皆さんは、コードに注釈を書かないのは単に投稿なので入れて 
 > いないだけであって、ご自分のお仕事で他の人が保守するツールのコード 
 > にはコメントを入れていらっしゃるのでしょうか?

 仕事上は別ですが、掲示板では理解しようとする気のない人に無駄な労力は使いたくないので、
 提示されたコードの関して詳細な質問が追加投稿された場合、その都度コメントするようにしています。

 ちなみに 正規表現で ()内の最初が?で始まる場合は後方参照から除外されます。
 (?=xx) xxのみにマッチ
 (?!xx) xx以外にマッチ
 (?:xx) xxのみにマッチ (私はあまり使用しません。)

 なのでマッチした文字列からは除外されます。

(seiya) 2018/05/13(日) 10:03


seiyaさん もこな2さん

コメントありがとうございます
教えて頂いたコードと
正規表現パズル 
http://www.geocities.jp/oraclesqlpuzzle/regex/
と言うサイトの 6-5 IPアドレスの左に0埋め
という以下のロジック
select Val as beforeIP,
RegExp_Replace(RegExp_Replace(Val,'([0-9]+)','000\1'),

               '0*([0-9]{3})','\1') as afterIP
を見て、これであれば私が最初に考えていた方向とおなじだなと
思い教えて頂いたコードを以下のように変更してみました

 Sub test2()
     MsgBox GetFormatNumber("avc12ccc2" & vbCrLf & "12aaa2ddd12345", 1)
 End Sub

 Function GetFormatNumber(ByVal txt As String, ByVal n As Long) As String
     Dim i As Long, mtch As Object, m As Object
     Dim reg As Object
     Dim RpPtn As String

     Set reg = CreateObject("VBScript.RegExp")
     With reg
         .Global = True: .MultiLine = True
         'この?はグループに続き\Dか$が来た時だけパターンに合致の意味
         .Pattern = "(\d+)(?=\D|$)"
      txt = .Replace(txt, String(n, "0") & "$1")

         .Pattern = "\d*(\d{" & n & "})(?=\D|$)" 
         txt = .Replace(txt, "$1")

     End With
     GetFormatNumber = txt

 End Function

 仕様 m桁の数字をn桁に合わせて左0埋めパディング 
    m<=n の時 左0埋めしn桁
    m>nの時は 右揃え
 としてn = 1,3,6 で動作しました。

 自分としてはこれでまず満足です。
 話題がそれて恐縮ですが、せっかくそれなりのサブルーチンが
 を作ったので、個人用マクロ personal.xls とか会社のファイルサーバ
 上の共用マクロブックにこのユーザ関数を置いて、別のブックのマクロから
 参照設定して利用できるようにすれば便利と考えました

 ただ初心者が最初に作ったもので、自分が忘れずに置くためのものですが
 マクロをブック本体の外に置いて利用するなど、ちゃんとやれるものでしょうか

 共用マクロの場所を忘れたり、異動などでマクロの所在が変わると使えなくなる
 ?等の注意事項、制約事項はないのでしょうか? そういった点見通しについて
 ご経験ありましたらぜひ教えてください

 以上

(かず) 2018/05/14(月) 03:17


 AddInにすればよいのでは?
 検索すれば色々出てくるはずです。

 その関数はMacでは使用できませんよ?
(seiya) 2018/05/14(月) 11:31

コメント返信:

[ 一覧(最新更新順) ]


YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki. Modified by kazu.