2009-07-16 7 views
1

ces expressions requièrent me rendre fou. Je suis coincé avec celui-ci:Expressions régulières: comment faire "option split" remplace

test1:[[link]] test2:[[gold|silver]] test3:[[out1[[inside]]out2]] test4:this|not 

Tâche:
Supprimer tous les [[et]] et s'il y a une option Regrouper une suite si la sortie doit être:

test1:link test2:silver test3:out1insideout2 test4:this|not 

je suis venu avec (PHP)

$text = preg_replace("/\\[\\[|\\]\\]/",'',$text); // remove [[ or ]] 

cela fonctionne pour part1 de la tâche. mais avant que je pense que je devrais faire l'option Split, ma meilleure solution:

$text = preg_replace("/\\[\\[(.*\|)(.*?)\\]\\]/",'$2',$text); 

Résultat:

test1:silver test3:[[out1[[inside]]out2]] this|not 

Je suis coincé. quelqu'un avec quelques minutes gratuites peut-il m'aider? Merci!

Répondre

0

Ceci est impossible de le faire dans une expression régulière puisque vous voulez garder le contenu dans plusieurs « hiérarchies » du contenu. Il serait être possible sinon, en utilisant une expression régulière récursive.

De toute façon, voici l'expression régulière la plus simple et la plus gourmande à laquelle je puisse penser. Il faut seulement remplacer si le contenu correspond exactement à vos besoins.

Vous aurez besoin d'échapper à tous les antislash lors de la mise en une chaîne (\ devient \\.)

\[\[((?:[^][|]+|(?!\[\[|]])[^|])++\|?)*]] 

Comme d'autres l'ont déjà expliqué, vous utilisez ce avec plusieurs passes. Gardez en boucle alors qu'il ya des matchs, remplaçons (seulement garder Grouper 1.)

est que

de différence d'autres expressions régulières, il vous permettra d'avoir entre crochets simples dans le contenu, sans casser:

test1:[[link]] test2:[[gold|si[lv]er]] 
test3:[[out1[[in[si]de]]out2]] test4:this|not 

devient

test1:[[link]] test2:si[lv]er 
test3:out1in[si]deout2 test4:this|not 
+0

oh. quelque chose que je n'ai pas pensé. laissez-moi jeter un oeil – cydo

+0

Oui, je suis probablement allé trop loin avec mon expression régulière ... Mais j'aime les optimiser pour la précision et la vitesse parce que les expressions régulières sont lentes et il peut être vraiment perceptible dans certains cas si vous ne faites pas attention. – Blixt

+0

test1:[[works]] test2:[[failed|works]] test3:[[out1[[inside]]out2]] test4:dont|replace test5:[[with[inner]bracket]] test6:[[nested[[link]]]] test7:[[it[[failed|works]]yesit[[failed|works]]]] fonctionne tous. Merci! – cydo

0

Pourquoi essayer de tout faire en une seule fois. Supprimez d'abord [[]], puis traitez les options, faites-le en deux lignes de code. Lorsque vous essayez d'obtenir quelque chose, privilégiez la clarté et la simplicité.

On dirait que vous avez toutes les pièces.

+0

ne fonctionne pas. si je supprime le [[premier, alors "this | not" est divisé aussi. et mon problème est que je n'ai pas une expression de division d'option de travail ... – cydo

1

Je pense que la façon la plus simple de le faire serait de faire plusieurs passes. Utilisez une expression régulière comme:

\[\[(?:[^\[\]]*\|)?([^\[\]]+)\]\] 

Ceci remplacera les chaînes d'options pour vous donner la dernière option du groupe. Si vous l'exécutez plusieurs fois jusqu'à ce qu'il ne corresponde plus, vous devriez obtenir le bon résultat (le premier passage remplacera [[out1 [[inside]] out2]] par [[out1insideout2]] et le second abandonnera les parenthèses

Edit 1: a titre d'explication,

\[\[  # Opening [[ 
(?:   # A non-matching group (we don't want this bit) 
    [^\[\]] # Non-bracket characters 
    *  # Zero or more of anything but [ 
    \|  # A literal '|' character representing the end of the discarded options 
)?   # This group is optional: if there is only one option, it won't be present 
(   # The group we're actually interested in ($1) 
    [^\[\]] # All the non-bracket characters 
    +  # Must be at least one 
)   # End of $1 
\]\]  # End of the grouping. 

Edit 2. changé l'expression d'ignorer ']', ainsi que '[' (il fonctionne un peu mieux comme ça)

Modifier 3: Il n'est pas nécessaire de connaître le nombre de crochets imbriqués que vous pouvez faire quelque chose comme:

$oldtext = ""; 
$newtext = $text; 
while ($newtext != $oldtext) 
{ 
    $oldtext = $newtext; 
    $newtext = preg_replace(regexp,replace,$oldtext); 
} 
$text = $newtext; 

Fondamentalement, cela empêche l'exécution de l'expression régulière remplacer jusqu'à ce que la sortie est identique à l'entrée.

Notez que je ne connais pas PHP, donc il y a probablement des erreurs de syntaxe dans ce qui précède.

+0

Cela est applicable si le nombre de parenthèses imbriquées est connu (par exemple si n <= MAX, puis passez-le MAX fois). –

+0

@streetpc: Je ne pense pas que vous ayez besoin de connaître le nombre de parenthèses imbriquées, je vais modifier ce qui précède pour expliquer pourquoi. – DrAl

+0

gentil, j'essaie toujours de bricoler comment cela fonctionne, merci pour l'explication! – cydo

0

Pourquoi ne pas simplement enlever les crochets restants?

$str = 'test1:[[link]] test2:[[gold|silver]] test3:[[out1[[inside]]out2]] test4:this|not'; 
$str = preg_replace('/\\[\\[(?:[^|\\]]+\\|)+([^\\]]+)\\]\\]/', '$1', $str); 
$str = str_replace(array('[', ']'), '', $str); 
+0

semble bien, mais j'ai remplacé le dernier avec $ str = str_replace (array ('[[', ']]'), '', $ str); afin de ne pas supprimer quelque chose comme [ThisNot]. – cydo

+0

J'aime cette solution "2 passes", même si je dois comprendre comment vous avez divisé cette option. – cydo

+0

Son expression régulière ne correspondra qu'à tous les groupes avec options, puis les remplacera par la dernière option, sans crochets. Ensuite, le remplacement supprime tous les crochets restants. C'est plus rapide que des passes multiples, au détriment de la précision (mais cela pourrait ne pas poser de problème à l'OP.) – Blixt

0

Eh bien, je ne colle pas juste regex, parce que je suis d'un esprit qui essaie de faire des trucs comme ça avec un grand regex vous mène à la vieille blague sur « Maintenant, vous avez deux problèmes » .Cependant, donner quelque chose comme un coup de feu:

$str = 'test1:[[link]] test2:[[gold|silver]] test3:[[out1[[inside]]out2]] test4:this|not'; $reg = '/(.*?):(.*?)(|$)/'; 
preg_match_all($reg, $str, $m); 
foreach($m[2] as $pos => $match) { 
    if (strpos($match, '|') !== FALSE && strpos($match, '[[') !== FALSE) { 
    $opt = explode('|', $match); $match = $opt[count($opt)-1]; 
    } 
    $m[2][$pos] = str_replace(array('[', ']'),'', $match); 
} 

foreach($m[1] as $k=>$v) $result[$k] = $v.':'.$m[2][$k]; 
+0

Bien sûr, ce n'est qu'un premier coup - vous voudrez probablement faire le travail du 2ème foreach dans la première boucle foreach, avec une certaine logique pour décider quand vous devriez être tirer des valeurs, etc. – TML

0

Ceci est C# en utilisant uniquement en utilisant des chaînes non-échappé, par conséquent, vous devrez doubler les antislashs dans d'autres langues.

String input = "test1:[[link]] " + 
       "test2:[[gold|silver]] " + 
       "test3:[[out1[[inside]]out2]] " + 
       "test4:this|not"; 

String step1 = Regex.Replace(input, @"\[\[([^|]+)\|([^\]]+)\]\]", @"[[$2]]"); 
String step2 = Regex.Replace(step1, @"\[\[|\]\]", String.Empty); 

// Prints "test1:silver test3:out1insideout2 test4:this|not" 
Console.WriteLine(step2); 
0
$str = 'test1:[[link]] test2:[[gold|silver]] test3:[[out1[[inside]]out2]] test4:this|not'; 
$s = preg_split("/\s+/",$str); 
foreach ($s as $k=>$v){ 
    $v = preg_replace("/\[\[|\]\]/","",$v);   
    $j = explode(":",$v); 
    $j[1]=preg_replace("/.*\|/","",$j[1]); 
    print implode(":",$j)."\n"; 
} 
Questions connexes