4

I'm trying to use a regex to find cells in a range that have a comma, but no space after that comma. Then, I want to simply add a space between the comma and the next character. For example, a cell has Wayne,Bruce text inside, but I want to turn it to Wayne, Bruce.

I have a regex pattern that can find cells with characters and commas without spaces, but when I replace this, it cuts off some characters.

Private Sub simpleRegexSearch()
    ' adapted from http://stackoverflow.com/questions/22542834/how-to-use-regular-expressions-regex-in-microsoft-excel-both-in-cell-and-loops
    Dim strPattern As String: strPattern = "[a-zA-Z]\,[a-zA-Z]"
    Dim strReplace As String: strReplace = ", "
    Dim regEx As New RegExp
    Dim strInput As String
    Dim Myrange As Range

    Set Myrange = ActiveSheet.Range("P1:P5")

    For Each cell In Myrange
        If strPattern <> "" Then
            strInput = cell.Value

            With regEx
                .Global = True
                .MultiLine = True
                .IgnoreCase = False
                .Pattern = strPattern
            End With

            If regEx.TEST(strInput) Then
                Debug.Print (regEx.Replace(strInput, strReplace))
            Else
                Debug.Print ("No Regex Not matched in " & cell.address)
            End If
        End If
    Next

    Set regEx = Nothing
End Sub

If I run that against "Wayne,Bruce" I get "Wayn, ruce". How do I keep the letters, but separate them?

Wiktor Stribiżew
  • 607,720
  • 39
  • 448
  • 563
BruceWayne
  • 22,923
  • 15
  • 65
  • 110

4 Answers4

4

Change the code the following way:

Dim strPattern As String: strPattern = "([a-zA-Z]),(?=[a-zA-Z])"
Dim strReplace As String: strReplace = "$1, "

Output will be Bruce, Wayne.

The problem is that you cannot use a look-behind in VBScript, so we need a workaround in the form of a capturing group for the letter before the comma.

For the letter after the comma, we can use a look-ahead, it is available in this regex flavor.

So, we just capture with ([a-zA-Z]) and restore it in the replacing call with a back-reference $1. Look-ahead does not consume characters, so we are covered.

(EDIT) REGEX EXPLANATION

  • ([a-zA-Z]) - A captured group that includes a character class matching just 1 English character
  • , - Matching a literal , (you actually do not have to escape it as it is not a special character)
  • (?=[a-zA-Z]) - A positive look-ahead that only checks (does not match, or consume) if the immediate character following the comma is and English letter.
Wiktor Stribiżew
  • 607,720
  • 39
  • 448
  • 563
  • 1
    That'll do it! Thanks so much! I'm just learning Regex, so will look in to the use of () and the `?=` but if you have a 'quick and dirty' explanation, it'd be more than appreciated! – BruceWayne Jun 08 '15 at 16:06
  • 2
    @user3578951: I added some more explanation of the regex used. Mind that the comma does not have to be escaped in any regex flavor I know, I removed the escape symbol. – Wiktor Stribiżew Jun 09 '15 at 09:29
  • 1
    @user3578951 my friend Patrick Matthews wrote the best description of using regexp with VBA that I have seen [here](http://www.google.com.au/url?sa=t&rct=j&q=&esrc=s&frm=1&source=web&cd=2&ved=0CC4QFjABahUKEwiu_biflYTGAhVH7RQKHZ6vAPg&url=http%3A%2F%2Fwww.experts-exchange.com%2Farticles%2F1336%2FUsing-Regular-Expressions-in-Visual-Basic-for-Applications-and-Visual-Basic-6.html&ei=gKx3Va69KMfaU57fgsAP&usg=AFQjCNESJKvvlJR1kTeTffUcRjzwwdNX5Q&sig2=r52YYj49aP5slGO_chXpxg&bvm=bv.95039771,d.d24) – brettdj Jun 10 '15 at 03:19
  • @brettdj - thanks! I actually found that page Googling around, and it's quite helpful. – BruceWayne Jun 10 '15 at 14:09
2

If we replace all commas with comma+space and then replace comma+space+space with comma+space, we can meet your requirement:

Sub NoRegex()
   Dim r As Range
   Set r = Range("P1:P5")
   r.Replace What:=",", Replacement:=", "
   r.Replace What:=",  ", Replacement:=", "
End Sub
Gary's Student
  • 95,722
  • 10
  • 59
  • 99
  • Ahh, clever - that's a great way to do it as well. Thanks! For what it's worth: I chose the regex answer as "the" answer just because I'm trying to learn Regex too, but other than that this would work 100% just as well. – BruceWayne Jun 08 '15 at 16:06
  • 1
    @user3578951 I agree that Regex is worth learning! ..............I tend to avoid it because others have had difficulty maintaining the regex code that I have written in the past. – Gary's Student Jun 08 '15 at 16:22
  • @Gary'sStudent - Regex looks pretty intimidating, I understand the super basics, but you can get really crazy with it, and that's where I get confused. As I mentioned above, if you have any tips/sites/etc. that help I'm more than open to it! Quick clarification - are regex patterns the same over all languages? I.e. would the Excel pattern above do the same thing if I was using Python, C++, Javascript, etc? – BruceWayne Jun 08 '15 at 16:25
  • 1
    @user3578951 They are similar..........because of my lack of expertise.....I can't answer your question. – Gary's Student Jun 08 '15 at 16:33
2

Uses the same RegExp as in the solution from stribizhev but with two optimisations for speed

  1. Your current code sets the RegExp details for every cell tested, these only need setting once.
  2. Looping through a varinat array is much faster than a cell range

code

Private Sub simpleRegexSearch()
    ' adapted from http://stackoverflow.com/questions/22542834/how-to-use-regular-expressions-regex-in-microsoft-excel-both-in-cell-and-loops
    Dim strPattern As String:
    Dim strReplace As String:
    Dim regEx As Object
    Dim strInput As String
    Dim X, X1
    Dim lngnct

    Set regEx = CreateObject("vbscript.regexp")

    strPattern = "([a-zA-Z])\,(?=[a-zA-Z])"
    strReplace = "$1, "

    With regEx
           .Global = True
           .MultiLine = True
           .IgnoreCase = False
           .Pattern = strPattern

    X = ActiveSheet.Range("P1:P5").Value2

    For X1 = 1 To UBound(X)
            If .TEST(X(X1, 1)) Then
                Debug.Print .Replace(X(X1, 1), strReplace)
            Else
                Debug.Print ("No Regex Not matched in " & [p1].Offset(X1 - 1).Address(0, 0))
            End If
    Next
    End With

    Set regEx = Nothing
End Sub
brettdj
  • 54,857
  • 16
  • 114
  • 177
0

What you are doing via Regex is to find a pattern

(any Alphabet),(any Alphabet)

and then replace such pattern to

,_ 

where _ implies a space.

So if you have Wayne,Bruce then the pattern matches where e,B. Therefore the result becomes Wayn, ruce.

Try

Dim strPattern As String: strPattern = "([a-zA-Z]),([a-zA-Z])"
Dim strReplace As String: strReplace = "$1, $2"

.

Jeong Jinmyeong
  • 208
  • 2
  • 10