web-dev-qa-db-fra.com

Comment extraire la sous-chaîne entre parenthèses en utilisant un motif Regex

C'est probablement un problème simple, mais malheureusement, je n'ai pas pu obtenir les résultats souhaités ... 

Dis, j'ai la ligne suivante: 

"Wouldn't It Be Nice" (B. Wilson/Asher/Love)

Je devrais chercher ce modèle:

" (<any string>)

Pour récupérer:

B. Wilson/Asher/Love

J'ai essayé quelque chose comme "" (([^))]*)) mais cela ne semble pas fonctionner. Aussi, j'aimerais utiliser Match.Submatches(0) afin que cela puisse compliquer un peu les choses car il repose sur des crochets ...

12
Daan

Éditer : Après avoir examiné votre document, le problème est qu'il y a des espaces insécables avant les parenthèses, pas des espaces normaux. Donc, cette expression rationnelle devrait fonctionner: ""[ \xA0]*\(([^)]+)\)

""       'quote (twice to escape)
[ \xA0]* 'zero or more non-breaking (\xA0) or a regular spaces
\(       'left parenthesis
(        'open capturing group
[^)]+    'anything not a right parenthesis
)        'close capturing group
\)       'right parenthesis

Dans une fonction:

Public Function GetStringInParens(search_str As String)
Dim regEx As New VBScript_RegExp_55.RegExp
Dim matches
    GetStringInParens = ""
    regEx.Pattern = """[ \xA0]*\(([^)]+)\)"
    regEx.Global = True
    If regEx.test(search_str) Then
        Set matches = regEx.Execute(search_str)
        GetStringInParens = matches(0).SubMatches(0)
    End If
End Function
18
alan

Pas strictement une réponse à votre question, mais parfois, pour des choses aussi simples que cela, les bonnes vieilles fonctions de chaîne sont moins déroutantes et plus concises que Regex. 

Function BetweenParentheses(s As String) As String
    BetweenParentheses = Mid(s, InStr(s, "(") + 1, _
        InStr(s, ")") - InStr(s, "(") - 1)
End Function

Usage:

Debug.Print BetweenParentheses("""Wouldn't It Be Nice"" (B. Wilson/Asher/Love)")
'B. Wilson/Asher/Love

EDIT@alan indique que cela va correspondre faussement au contenu des parenthèses dans le titre de la chanson. Ceci est facilement contourné avec une petite modification: 

Function BetweenParentheses(s As String) As String
    Dim iEndQuote As Long
    Dim iLeftParenthesis As Long
    Dim iRightParenthesis As Long

    iEndQuote = InStrRev(s, """")
    iLeftParenthesis = InStr(iEndQuote, s, "(")
    iRightParenthesis = InStr(iEndQuote, s, ")")

    If iLeftParenthesis <> 0 And iRightParenthesis <> 0 Then
        BetweenParentheses = Mid(s, iLeftParenthesis + 1, _
            iRightParenthesis - iLeftParenthesis - 1)
    End If
End Function

Usage:

Debug.Print BetweenParentheses("""Wouldn't It Be Nice"" (B. Wilson/Asher/Love)")
'B. Wilson/Asher/Love
Debug.Print BetweenParentheses("""Don't talk (yell)""")
' returns empty string

Bien sûr, c'est moins concis qu'avant! 

C'est un regex agréable

".*\(([^)]*)

Dans VBA/VBScript:

Dim myRegExp, ResultString, myMatches, myMatch As Match
Dim myRegExp As RegExp
Set myRegExp = New RegExp
myRegExp.Pattern = """.*\(([^)]*)"
Set myMatches = myRegExp.Execute(SubjectString)
If myMatches.Count >= 1 Then
    Set myMatch = myMatches(0)
    If myMatch.SubMatches.Count >= 3 Then
        ResultString = myMatch.SubMatches(3-1)
    Else
        ResultString = ""
    End If
Else
    ResultString = ""
End If

Cela correspond

Put Your Head on My Shoulder

dans 

"Don't Talk (Put Your Head on My Shoulder)"  

Mise à jour 1

Je laisse la regex en vrac sur votre fichier doc et il correspond à la demande. Bien sûr, la regex va bien. Je ne parle pas couramment VBA/VBA, mais je suppose que c'est là que ça tourne mal

Si vous voulez discuter de la regex un peu plus, ça me convient. Je ne suis pas impatient de commencer à creuser dans cette API VBscript qui a l'air mystérieuse.

Compte tenu de la nouvelle entrée, la regex est peaufinée 

".*".*\(([^)]*)

Pour qu'il ne corresponde pas faussement (Mettez votre tête sur mon épaule) qui apparaît à l'intérieur des guillemets.

enter image description here

2
buckley

Cette fonction a fonctionné sur votre exemple de chaîne:

Function GetArtist(songMeta As String) As String
  Dim artist As String
  ' split string by ")" and take last portion
  artist = Split(songMeta, "(")(UBound(Split(songMeta, "(")))
  ' remove closing parenthesis
  artist = Replace(artist, ")", "")
End Function

Ex:

Sub Test()

  Dim songMeta As String

  songMeta = """Wouldn't It Be Nice"" (B. Wilson/Asher/Love)"

  Debug.Print GetArtist(songMeta)

End Sub

imprime "B. Wilson/Asher/Love" dans la fenêtre immédiate.

Il résout également le problème alan mentionné . Ex:

Sub Test()

  Dim songMeta As String

  songMeta = """Wouldn't (It Be) Nice"" (B. Wilson/Asher/Love)"

  Debug.Print GetArtist(songMeta)

End Sub

imprime également "B. Wilson/Asher/Love" dans la fenêtre immédiate. À moins bien sûr que les noms d’artistes incluent également des parenthèses.

1
JimmyPena

Je pense que vous avez besoin d’un meilleur fichier de données;) Vous pouvez envisager de pré-traiter le fichier dans un fichier temporaire afin de le modifier, afin que les valeurs aberrantes qui ne correspondent pas à votre modèle soient modifiées en fonction de leur correspondance avec le modèle. Cela prend un peu de temps à faire, mais c'est toujours difficile lorsqu'un fichier de données manque de cohérence.

0