j'ai un soucis avec des expressions régulières Ruby. Je n'ai jamais réussi à avoir une chaîne vide en cas de non correspondance... uniquement si la chaîne vient d'un fichier (?!)
Par exemple je souhaite lire un fichier de configuration structuré comme suis:
# commentaire
PARAMETRE = VALEUR
AUTRE_PARAMETRE = AUTRE_VALEUR
ENCORE_UN = 123 # commentaire
UNE_RUSE AHAH
La dernière ligne contient volontairement une erreur. Il manque le signe '='.
Si je veux lire le nom du paramètre (en supprimant les blancs avant et après):
chaine = "AUTRE_PARAMETRE = AUTRE_VALEUR"
print chaine.sub!(/^\s*(.*)\s*=.*$/, '\1'), "\n"
==> AUTRE_PARAMETRE
chaine = "UNE_RUSE AHAH"
print chaine.sub!(/^\s*(.*)\s*=.*$/, '\1'), "\n"
==> nil
La première ligne affichée est bonne. La seconde affiche 'nil' ce qui est normal puisqu'il manque le signe '=', donc aucune correspondance n'est trouvée.
Maintenant je veux le lire "en vrai":
IO.readlines("fichier.conf").each do |ligne|
if ligne =~ /^\s*[;#]+|^\s*$/
next # sauter les lignes vides (vides, ou espaces, ou tabulations, ou commentaires)
end
print ligne.sub(/^\s*(.*)\s*=.*$/, '\1'), "\n"
end
==> PARAMETRE
==> AUTRE_PARAMETRE
==> ENCORE_UN
==> UNE_RUSE AHAH
La dernière ligne devrait être 'nil'. Je ne comprends pas pourquoi elle n'est pas 'nil' alors que c'est la même expression que dans le premier test. Je ne sais pas comment faire pour que ça retourne 'nil' ou une chaîne vide si il n'y a pas le signe '='.
Quelqu'un a une idée ?
# manque bang ?!
Posté par Sisyphe Plâtrier . Évalué à 1.
et en dessous
Èqça.
[^] # Re: manque bang ?!
Posté par Kerro . Évalué à 2.
Il me reste comme problème: je ne veux pas utiliser '!' justement, car j'ai encore besoin de la chaine de caractères complète par la suite.
Je souhaite faire:
parametre = ligne.sub(/^\s*(.*)\s*=.*$/, '\1')
valeur = ligne.sub(/=\s*(.*)\s*$/, '\1')
... et d'autre choses.
Je remarque également: si il y a plus d'un signe '=' sur la ligne, ce qui est retourné est incorrect. Ce n'est pas génant pour ce que je souhaite lire, mais intellectuellement ça me tracasse.
Si la ligne est
PARAM = 1+3=4
Alors ça me retourne
parametre = PARAM = 1+3
alors que j'aimerais
parametre = PARAM
[^] # Re: manque bang ?!
Posté par Antoine (site web personnel) . Évalué à 2.
^\s*(.*)\s*=.*$
||V
^\s*(.*?)\s*=.*$
[^] # Re: manque bang ?!
Posté par Sylvain Sauvage . Évalué à 4.
1. Comme tu veux les deux valeurs autour du =, il est plus logique de ne faire qu’une fois le boulot (une seule fois la même expression régulière).
2. Quand on veut s’arrêter au premier caractère X, l’expression régulière qui précède X se base sur [^X]*, pas sur .*.
Résultat :
File.open('test.txt').each_line do |line|
line.sub!(/[;#].*/, '') # enlever les commentaires de fin de ligne
line.strip! # enlever les espaces en trop
next if line.empty?
if line =~ /\s*([^=\s]*)\s*=\s*([^\s]*)/
name, value = $1, $2
print ".#{name}. == .#{value}.\n"
end
end
La variable value n’est jamais nil car $2 peut prendre la chaîne vide.
[^] # Re: manque bang ?!
Posté par Kerro . Évalué à 2.
L'indication "1" de Sylvain Sauvage m'a fait modifier mon code pour utiliser $1 et $2. Excellente idée. Pour l'indication "2" soit j'ai mal testé, soit ça ne fonctionne pas dans les cas tordus.
IO.readlines("test.conf").each do |ligne|
if ligne =~ /^\s*[;#]+|^\s*$/
next # sauter les lignes vides (vides, ou espaces, ou tabulations, ou commentaires uniquement)
end
if ligne =~ /^\s*(.*?)\s*=\s*(.*?)\s*[\n]$/
nom, valeur = $1, $2
print "[", nom, "] = [", valeur, "]\n"
end
end
note: comment vous faites pour indenter entre les balises 'code' ?
Ce code fonctionne maintenant pour toutes les situations tordues que j'ai imaginé: avec des espaces à la fin, avec plusieurs signes '=', avec le nom manquant, etc. Le résultat est soit 'nil' soit une valeur correcte.
Je vais maintenant supprimer les commentaires de fin de ligne. Une simple détection de ; et de # suffit en principe. Je vais juste ajouter la possibilité d'avoir un caractère d'échappement et la gestion des guillemets. Inutile pour smb.conf mais pas pour le futur.
Par exemple si je veux un parametre pouvant contenir n'importe quelle chaîne de caractères:
MESSAGE = "## ATTENTION: erreur détectée ; valeur trop élevée ###"
Cet exemple contient ; et #
[^] # Re: manque bang ?!
Posté par Sylvain Sauvage . Évalué à 2.
Pour le 2., mon code (qui élimine les commentaires et les espaces dès le départ) fonctionne sur les exemples que tu as donnés. Tu as des exemples qui ne passent pas ?
Pour *? et +?, ils se différencient de * et + par le fait que * et + essaient de correspondre à la plus longue chaîne possible alors que *? et +? s’arrêtent dès qu’ils peuvent. (* et + sont dits greedy, *? et +? lazy.) Exemple classique : pour attraper une balise HTML (et juste la balise), on utilise /<.+?>/ car /<.+>/ va tout gober entre la première et la dernière balises du texte.
Utiliser la négation ([^X]*) est moins gourmande car le moteur n’a pas besoin de faire du backtrack (ce qu’il doit faire avec .*?). Je trouve ça aussi plus clair.
Pour les guillemets, il va peut-être falloir utiliser autre chose (une grammaire p.ex., au moins des états), ne serait-ce que parce qu’il faudra aussi gérer les sauts de ligne entre les guillemets.
[^] # Re: manque bang ?!
Posté par Kerro . Évalué à 2.
mon code (qui élimine les commentaires et les espaces dès le départ) fonctionne sur les exemples que tu as donnés. Tu as des exemples qui ne passent pas ?
Au pif:
MESSAGE_ERREUR = "Bim-badaboum ; erreur entre deux balises dièse (#)"
Pour les guillemets je ne gèrerais pas les sauts de lignes puisque je ne les gère pas non plus pour le reste. Il va falloir que je tienne "seulement" compte des paires de guillemets, des caractères d'échapement, et des guillemets précédés d'un caractère d'échapement. Je cherche les problèmes :-)
CHAINE_BIDON = "ma chaîne \" avec un piège au milieu"
# Solution compliquée
Posté par yellowiscool . Évalué à 1.
En plus, t'auras une gestion des erreurs toute faîte.
Envoyé depuis mon lapin.
[^] # Superbe solution :-)
Posté par Kerro . Évalué à 1.
[^] # Re: Superbe solution :-)
Posté par Étienne . Évalué à 2.
Étienne
[^] # C'est un concours ?
Posté par Kerro . Évalué à -1.
Je vous imagine médecin du sport:
Comment éviter le tennis elbow ? Il suffit de faire du foot !
[^] # Re: C'est un concours ?
Posté par Jean B . Évalué à 5.
http://railsforphp.com/reference/filesystem/parse_ini_file
et
http://snippets.dzone.com/posts/show/4918
Testé ça marche :
conf = Ini.new('/path/le/chien/test.ini')
puts conf['PARAMETRE']
# => VALEUR
De rien ça m'a fait plaisir de t'aider.
[^] # Re: Superbe solution :-)
Posté par yellowiscool . Évalué à 2.
Envoyé depuis mon lapin.
[^] # Re: Superbe solution :-)
Posté par Kerro . Évalué à 3.
La question était "pourquoi mon premier morceau de code ne donne pas le même résultat que le second ?".
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.