[[20220121082541]] 『指定文字列内の指定文字を最後から検索して削除』(Inoue) ページの最後に飛ぶ

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

 

『指定文字列内の指定文字を最後から検索して削除』(Inoue)

指定文字列内の指定文字削除をこちらで教えてもらった下記のコードで行ってきました。

    '指定文字を削除

    For n = 1 To Ws4.Cells(Rows.Count, "A").End(xlUp).Row
         Buf = Cells(n, "A")
         'UBound関数…引数に指定した配列の、最も大きい要素番号を返す
         For i = 0 To UBound(Replace_moji) - 1
             Buf = Replace(Buf, Replace_moji(i), "", 1, 1, 1)
         Next i
         Ws4.Cells(n, "B") = Buf
    Next n

指定文字が「(」及び「)」の場合
指定文字列の最初では無く、最後から先頭に向かって逆向きに検索して
削除する必要がある事があります。

この場合、指定文字列を「さかさまに並び替えるマクロ」で変更して

    Cells(i,"A").Value = StrReverse(Cells(i,"A").Value)
    Buf = Cells(n, "A")
Reolace処理した後でもう一度「さかさまに並び替えるマクロ」で
正規の文字列に変換する方法が想像できますが、

 他には、どんな方法がありますか ?

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


以前のスレッドというのを改めて探して、学習する気になれません。
実行されたいことを、省略せずに改めて説明してもらえませんか?

1. Replace_mojiとは何ですか?
2. 処理前と処理後の具体例を示して、説明してください。
(γ) 2022/01/21(金) 08:37


すいません。

>以前のスレッドというのを改めて探して、学習する気になれません。

以下が相談した旧スレです。
https://www.excel.studio-kazu.jp/kw/20220115111625.html

>1. Replace_mojiとは何ですか?

    削除すべき文字列の事です。

>2. 処理前と処理後の具体例を示して、説明してください。

指定文字列が以下だとして

    kamen (Vol_1) iroirokamen (00:02:25)

最後の「(」及び「)」を削除すべき文字列として
最終形は、以下

    kamen (Vol_1) iroirokamen 00:02:25

現在利用しているのコードでは、以下のように
先頭から検索されるので結果は

    kamen Vol_1 iroirokamen (00:02:25)

(Inoue) 2022/01/21(金) 09:02


 題意は了解しました。

 私は貴兄の案で、十分エレガントだと思います。
 あえて書くなら正規表現の使用です。
 たとえば、こんな感じです。

 Sub test()
     Dim s As String

     s = "kamen (Vol_1) iroirokamen (00:02:25)"
     With CreateObject("VBScript.RegExp")
         .Pattern = "(.*)\("
         s = .Replace(s, "$1")
         .Pattern = "(.*)\)"
         s = .Replace(s, "$1")
     End With
     Debug.Print s  'kamen (Vol_1) iroirokamen 00:02:25 が出力される
 End Sub

(γ) 2022/01/21(金) 09:10


 正規表現は、パターン検索のためのDSL(ドメイン固有言語)つまり、道具のことです。
 Excelから自由に使えます。

 今回の例を説明するとこんなことです。

 "(.*)\)"  というパターンマッチは、
 任意の文字列(このなかにはカッコも含む)の連続、の次にカッコが来たとき、を意味します。
 そのとき、
 そのマッチした文字列を、
 「最初の任意の文字列部分だけ」($1がその意味です)に置換します。
 つまり、 
 "kamen (Vol_1) iroirokamen ("が
 "kamen (Vol_1) iroirokamen " に置換されます。

 もう一つのほうも同じ趣旨です。

 (なお、"(" や ")"は、特別な機能を持つ文字なので、
   単なる文字列としてのカッコそれ自身を表す場合は、
   \をつけて\)などとして、エスケープする決まりです。)

 たぶん、了解するのには時間がかかります。
 ということで、あなたの案を使用してください。

(γ) 2022/01/21(金) 09:26


 InStrRevを使うのも普通にある手法でしょう。

 Sub test2()
    Dim s As String
    Dim p As Long
    Dim e As Variant

    s = "kamen (Vol_1) iroirokamen (00:02:25)"
    For Each e In Array("(", ")")
        p = InStrRev(s, e)
        s = Left(s, p - 1) & Replace(s, e, "", p)
    Next
    Debug.Print s   'kamen (Vol_1) iroirokamen 00:02:25 が出力される
 End Sub
 # 存在しない場合のエラー対応は省略しています。
(γ) 2022/01/21(金) 11:01

γさん、アドバイス感謝します。

StrReverseを利用して上手く処理できたので
アドバイスにある「正規表現」ですが、

個人的に「正規表現」は、
パーターンの指定がまるで呪文のようで取っ付きにくくて苦手です。

今回は、アドバイスに有るように素直にStrReverseを利用します。

’-------------------------------------------

以下が現在のコードです。
何かアドバイスがあればお願いします。

    Kt関数Addinを利用しています。
    ImputFrmと言うフォームを追加しています。
    (ImputFrmは、ユーザーフォームで作成したInputBoxでInputBoxと同じ機能です。)

'検索方向の指定FLAG (KtMsgBoxを利用)

Dim Prompt As kt_MsgBoxPromptType
Dim rc As Variant
Call ktMsgBoxPromptTypeInit(Prompt)
With Prompt

  .Message(1) = "文字列の最初から削除文字を検索するなら" & vbCrLf & "    「はい(Y)」を" & vbCrLf & "" & vbCrLf & "最後から検索するなら" & vbCrLf & "    「いいえ(N)」を" & vbCrLf & "" & vbCrLf & "選択してください。"
  .FName(1) = "メイリオ"
  .FSize(1) = 11
  .FBold(1) = True
  .FColor(1) = vbBlack
End With
rc = ktMsgBoxEX(Prompt _
                , vbYesNo + vbQuestion, "検索方向の指定(前から、後ろから)" _
                , BackColor:=&H808080)

'rc=6 なら「はい(Y)」、rc=7なら「いいえ(N)」

'分割文字を一括して入手

     mg = "削除する文字列を全て指定してください。" & Chr(13) & _
                            Chr(13) & _
                            "例えば、「():」のように複数を一度に指定できます。" & Chr(13) & Chr(13) & _
                             "既定値は、最初の「(」のみを削除するようにしています。" & Chr(13) & _
                             "「(」を指定の場合、2回めの「(」は削除対象外です。" & Chr(13) & "削除文字を一度に複数指定"

     Replace_moji = InputFrm.Result(mg, "(")

'「キャンセル」がクリックされた場合
If Replace_moji = "" Then

    MsgBox "「キャンセル」が選択されました。" & Chr(13) & _
            "処理を中止します。"
    Exit Sub
Else
    ' 以下で指定文字列,1文字1文字の間にカンマ(,)を入れる

        Dim S_Moji
        Dim S_Moji_New
        Dim Moji
        Dim New_Moji As String

        S_Moji_New = ""

        For i = Len(Replace_moji) To 1 Step -1
            Moji = Mid(Replace_moji, i, 1)
            If i <> 1 Then
                S_Moji_New = "," & Moji & S_Moji_New
            Else
                S_Moji_New = Moji & S_Moji_New
            End If
        Next i

    '最後の指定文字にはカンマがないのでカンマを末尾に付けてから単独の分割文字としてそれぞれをカンマで分割する
    Replace_moji = Split(S_Moji_New & ",", ",")
End If

'指定文字を削除

 For n = 1 To Ws4.Cells(Rows.Count, "A").End(xlUp).Row

        '検索方向の指定FLAGでターゲット文字列の並び替えを変更
         If rc = 6 Then
            Buf = Ws4.Cells(n, "A")
         Else
            Buf = StrReverse(Ws4.Cells(n, "A"))
         End If

         'UBound関数…引数に指定した配列の、最も大きい要素番号を返す
         For i = 0 To UBound(Replace_moji) - 1
             Buf = Replace(Buf, Replace_moji(i), "", 1, 1, 1)
         Next i

         '検索方向の指定FLAGでターゲット文字列の並び替えを元に戻す
         If rc = 6 Then
            Ws4.Cells(n, "B") = Buf
         Else
            Ws4.Cells(n, "B") = StrReverse(Buf)
         End If

     Next n

MsgBox "処理が終了しました。"

Set Ws4 = Nothing

End Sub

(Inoue) 2022/01/21(金) 11:21


 こんな書き方ではまずいのですか?

     Replace_moji = InputFrm.Result(mg, "(")
     If Replace_moji = "" Then   '「キャンセル」がクリックされた場合
         MsgBox "「キャンセル」が選択されました。" & Chr(13) & _
                "処理を中止します。"
         Exit Sub
     End If

     '指定文字を削除
     For n = 1 To Ws4.Cells(Rows.Count, "A").End(xlUp).Row
         '検索方向の指定FLAGでターゲット文字列の並び替えを変更
         If rc = 6 Then                  '前から検索
             Buf = Ws4.Cells(n, "A")
         Else                            '後ろから検索
             Buf = StrReverse(Ws4.Cells(n, "A"))
         End If

         For i = 1 To Len(Replace_moji)
             s = Mid(Replace_moji, i, 1)
             Buf = Replace(Buf, s, "", 1, 1, vbTextCompare)
         Next i

         '検索方向の指定FLAGでターゲット文字列の並び替えを元に戻す
         If rc = 6 Then
             Ws4.Cells(n, "B") = Buf
         Else
             Ws4.Cells(n, "B") = StrReverse(Buf)
         End If
     Next n
     MsgBox "処理が終了しました。"
     Set Ws4 = Nothing

 ちなみに、
 | '最後の指定文字にはカンマがないのでカンマを末尾に付けてから
 | '単独の分割文字としてそれぞれをカンマで分割する
 こうした配慮は要らないと思います。
 カンマを追加したせいで、末尾に長さ0の文字列(つまり"")が挿入されてしまいます。

 仮に、配列にしたいとしても、こんな風に書けば済みます。 
    For i = 1 To Len(Replace_moji)
        s_new = s_new & Mid(Replace_moji, i, 1) & ","
    Next i
    s_new = Left(s_new, Len(s_new) - 1)
    ary = Split(s_new, ",")

 ちなみに、正規表現は使う必要がなければ使わなくて結構です。貴兄の態度は正しいです。
 いずれ必要な時が来ます。

(γ) 2022/01/21(金) 20:40


γさん、コードの修正感謝します。

 >ちなみに、
 >'最後の指定文字にはカンマがないのでカンマを末尾に付けてから
 >'単独の分割文字としてそれぞれをカンマで分割する

 >こうした配慮は要らないと思います。

まず最初に
InputBoxに削除文字候補をカンマ区切りで入力
例えば、「(,),:」を処理していたのを

カンマは入力時に面倒なので
削除文字候補をカンマ区切りをやめて「():」と入力して
処理するようにコードを改造しました。

改造時に最初のカンマ区切りのコードをそのまま利用することにしたので
削除候補を1文字ずつばらばらにしてカンマを間に入れる処理が残りました。

教えてもらったコードは
Midで削除候補を一つずつ取り出して
そのままReplaceで削除候補を削除(""に変換)するのですね。
初心者のわたしでも大変わかりやすいです。

アイデアを具体化するときに引き出しが少ないので
どうしても過去作成したコードをついつい使い回す事が多く
私のVBAの進歩は牛歩並みです。

(Inoue) 2022/01/22(土) 08:00


コメント返信:

[ 一覧(最新更新順) ]


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