web-dev-qa-db-fra.com

Comment utiliser Ruby gsub Regexp avec de nombreuses correspondances?

J'ai le contenu d'un fichier csv avec des guillemets dans le texte cité

test,first,line,"you are a "kind" man",thanks
again,second,li,"my "boss" is you",good

Je dois remplacer chaque guillemet double non précédé ou remplacé par une virgule par ""

test,first,line,"you are a ""kind"" man",thanks
again,second,li,"my ""boss"" is you",good

donc "est remplacé par" "

J'ai essayé

x.gsub(/([^,])"([^,])/, "#{$1}\"\"#{$2}")

mais n'a pas fonctionné

19
Mahmoud Khaled

Votre expression régulière doit être un peu plus en gras, au cas où les guillemets se produisent au début de la première valeur ou à la fin de la dernière valeur:

csv = <<ENDCSV
test,first,line,"you are a "kind" man",thanks
again,second,li,"my "boss" is you",good
more,""Someone" said that you're "cute"",yay
"watch out for this",and,also,"this test case"
ENDCSV

puts csv.gsub(/(?<!^|,)"(?!,|$)/,'""')
#=> test,first,line,"you are a ""kind"" man",thanks
#=> again,second,li,"my ""boss"" is you",good
#=> more,"""Someone"" said that you're ""cute""",yay
#=> "watch out for this",and,also,"this test case"

Le regex ci-dessus utilise des assertions de lookbehind négatif et d'anticipation négatif (ancres) disponibles dans Ruby 1.9.

  • (?<!^|,) - précédant immédiatement ce point, il ne doit pas y avoir non plus de début de ligne (^) ou une virgule
  • " - trouver un double guillemet
  • (?!,|$) - immédiatement après ce point, il ne doit y avoir ni virgule ni fin de ligne ($)

En prime, puisque vous n'avez pas réellement capturé les personnages des deux côtés, vous n'avez pas à vous soucier de l'utilisation de \1 correctement dans votre chaîne de remplacement.

Pour plus d'informations, consultez la section "Ancres" dans le documentation officielle Ruby regex .


Cependant, dans le cas où vous faites devez remplacer les correspondances dans votre sortie, vous pouvez utiliser l'une des options suivantes:

"hello".gsub /([aeiou])/, '<\1>'            #=> "h<e>ll<o>"
"hello".gsub /([aeiou])/, "<\\1>"           #=> "h<e>ll<o>"
"hello".gsub(/([aeiou])/){ |m| "<#{$1}>" }  #=> "h<e>ll<o>"

Vous ne pouvez pas utiliser l'interpolation de chaîne dans la chaîne de remplacement, comme vous l'avez fait:

"hello".gsub /([aeiou])/, "<#{$1}>"
 #=> "h<previousmatch>ll<previousmatch>"

… Parce que cette interpolation de chaîne se produit une fois, avant le gsub a été exécuté. En utilisant la forme de bloc de gsub ré-invoque le bloc pour chaque correspondance, à quel point le global $1 a été correctement renseigné et peut être utilisé.


Edit : Pour Ruby 1.8 (pourquoi diable utilisez-vous cela?), Vous pouvez utiliser:

puts csv.gsub(/([^,\n\r])"([^,\n\r])/,'\1""\2')
44
Phrogz

En supposant que s est une chaîne, cela fonctionnera:

puts s.gsub(/([^,])"([^,])/, "\\1\"\"\\2")
9
David Grayson