2010-12-15 88 views
1

J'ai un fichier XML qui est trop gros. Pour le rendre plus petit, je veux remplacer tous les tags et noms d'attributs par des versions plus courtes de la même chose.Comment remplacer chaque occurrence d'un motif dans une chaîne à l'aide de Ruby?

Alors, je mis en œuvre ceci:

string.gsub!(/<(\w+) /) do |match| 
    case match 
    when 'Image' then 'Img' 
    when 'Text' then 'Txt' 
    end 
end 

puts string 

qui supprime tous les tags d'ouverture mais ne fait pas grand chose d'autre.

Qu'est-ce que je fais mal ici?

+3

* Qu'est-ce que je fais mal ici? * Snide, mais réponse sérieuse 1: ne pas utiliser un processeur XML. Snide, mais réponse sérieuse 2: [deux problèmes] (http://www.codinghorror.com/blog/2008/06/regular-expressions-now-you-have-two-problems.html). Snide, mais réponse sérieuse 3: ces changements vont probablement avoir une très petite diminution de la taille. Considérons un conteneur (gzip) ou un compresseur XML binaire, si * vraiment * est nécessaire. Codage heureux. –

+1

@pst: Oui, monsieur. Cependant, j'ai besoin de ce script non seulement pour XML mais aussi pour d'autres formats (partiellement personnalisés), donc un processeur XML ne le coupera pas. Une remarque encore plus correcte serait en fait «4: utiliser XML en premier lieu». Quelque chose comme JSON résoudrait tous mes problèmes à la rigueur - mais quand j'ai proposé cela, mes patrons l'ont rejeté. Triste mais vrai. – bastibe

+0

Comment pourrais-je oublier # 4? :(Heureux codage dans les confinements de boss –

Répondre

2

Voici une autre façon:

class String 
    def minimize_tags! 
    {"image" => "img", "text" => "txt"}.each do |from,to| 
     gsub!(/<#{from}\b/i,"<#{to}") 
     gsub!(/<\/#{from}>/i,"<\/#{to}>") 
    end 
    self 
    end 
end 

Ce sera probablement un peu plus facile à maintenir, puisque les modèles de remplacement sont en un seul endroit. Et sur des cordes de taille significative, cela peut être beaucoup plus rapide que celui de Kevin. Je l'ai fait un test de vitesse rapide de ces deux méthodes en utilisant la source HTML de cette page stackoverflow lui-même comme la chaîne de test, et mon chemin allais 6x plus rapide ...

+0

Oui, c'est mieux que le mien. – Kevin

1

Essayez ceci:

string.gsub!(/(<\/?)(\w+)/) do |match| 
    tag_mark = $1 
    case $2 
    when /^image$/i 
    "#{tag_mark}Img" 
    when /^text$/i 
    "#{tag_mark}Txt" 
    else 
    match 
    end 
end 
+0

Les balises de fermeture n'auront pas d'espace après la balise, donc cette tentative de faire correspondre les balises d'ouverture et de fermeture ne fonctionnera pas comme écrit ... –

+0

Merci @glenn, j'ai J'ai réalisé que l'espace n'est pas une faute de frappe J'ai fait une mise à jour de mon code – Kevin

+0

Non, vous ne pouvez pas sortir l'espace, à moins que vous ne sachiez qu'il n'y a plus de balises qui commencent par celles-ci, par exemple TEXTAREA ou IMAGEMAP –

2

est ici la beauté de l'aide d'un analyseur tel que Nokogiri:

Cela vous permet de manipuler des balises sélectionnées (nœuds) et leurs attributs:

require 'nokogiri' 

xml = <<EOT 
<xml> 
    <Image ImagePath="path/to/image">image comment</Image> 
    <Text TextFont="courier" TextSize="9">this is the text</Text> 
</xml> 
EOT 

doc = Nokogiri::XML(xml) 
doc.search('Image').each do |n| 
    n.name = 'img' 
    n.attributes['ImagePath'].name = 'path' 
end 
doc.search('Text').each do |n| 
    n.name = 'txt' 
    n.attributes['TextFont'].name = 'font' 
    n.attributes['TextSize'].name = 'size' 
end 
print doc.to_xml 
# >> <?xml version="1.0"?> 
# >> <xml> 
# >> <img path="path/to/image">image comment</img> 
# >> <txt font="courier" size="9">this is the text</txt> 
# >> </xml> 

Si vous devez parcourir tous les nœuds, peut-être à faire une transformation universelle sur l'étiquette-nom, vous pouvez utiliser doc.search('*').each. Ce serait plus lent que de rechercher des tags individuels, mais cela pourrait entraîner moins de code si vous devez changer chaque tag. La bonne chose à propos de l'utilisation d'un analyseur est qu'il fonctionnera même si la disposition du XML change car il ne se soucie pas des espaces, et fonctionnera même si l'ordre des attributs change, rendant votre code plus robuste.

+0

Bien que ce soit une très bonne solution en effet, je veux réellement transformer non seulement les noms de balises et les attributs, mais aussi les chaînes sélectionnées. Donc, malheureusement, cette solution ne fonctionnera pas pour moi. – bastibe

+0

@BastiBechtold, "mais les chaînes sélectionnées, aussi, malheureusement, cette solution ne fonctionnera pas pour moi." Seulement parce que vous ne savez pas comment le faire et parce que vous n'avez pas dit que c'était ce que vous vouliez faire dans votre question. C'est en fait faisable avec un analyseur d'une manière très similaire à ce que j'ai déjà démontré, parce que les «nœuds de texte» existent, sont accessibles et modifiables. J'ai écrit une réponse hier en faisant exactement cela. –

+0

Je suis corrigé. Je m'incline devant vous pour m'excuser. – bastibe