Gå til innhold

parsing av tekst


Anbefalte innlegg

Jeg er på jakt etter en effektiv parser som kan konvertere slike strenger til array:

href="http://link.com" alt="he's a \"man\"" style=default heigth='30px'

skal da gi samme arrayet som man ville fått med denne koden:

$ar = array(
"href" => "http://link.com",
"alt" => "he's a \"man\"",
"style" => "default");

(dette er bare eksempel for å illustrere utfordringen)

 

Utfordringen her ligger i at value delen av attirbuttene kan være omgitt av anførselstegn (") eller apostrof (') eller dersom det bare er ett ord trenger det ikke være noen av delene. Teksten i value delen kan også inneholde både anførselstegn og apostrofer, så parseren må kunne takle at disse skal ignoreres når det står et escape tegn (\) forran.

 

Noen som kjenner til en slik parser, eller har noen gode tips å komme med?

Lenke til kommentar
Videoannonse
Annonse

Gjør susen denne tror jeg.. ;)

 

<?php

 $input = "href=\"http://link.com\" alt=\"he's a \\\"man\\\"\" style=default heigth='30px'";

 preg_match_all("/(\w+)=/", $input, $names);
 $names = $names[1];
 $values = preg_split("/(\w+)=/", $input, -1, PREG_SPLIT_NO_EMPTY);

 $output = array();

 foreach ($names as $index => $name) {
   $output["$name"] = preg_replace("/(^[\"\']?)(.*)\\1$/", "\$2", trim($values[$index]));
 } 

 var_dump($output);

?>

 

Men hvor effektiv den er får noen andre ta seg av å vurdere.. :blush:

Lenke til kommentar

Om du tenker HTML burde man ikke tenke på escaping av verdier. For det skal ikke forekomme escaping av tegn i HTML. I stedet har man egne verdier for dette.

 

alt="hei på deg \"SNUPPA\""

 

Dette er feil. Alt atributten vil bli tolket av nettleseren som om den inneholder "hei på deg \" og SNUPPA vil bli sett på som en egen attributt. Skal man ha med " må man erstatte dette tegnet med " for å få det riktig.

 

Med andre ord burde det ikke være nødvendig for parseren å tolke slik tekst, da dette vil være feil med tanke på HTML-standaren. Det vil også hjelpe oss med å forenkle uttrykket betraktelig.

 

Dette utrykket burde vel være tilstrekkelig?

(\w+)="?(.*?)["| ]

Lenke til kommentar

Tror det foreslåtte uttrykket der nok kommer til a ta med seg flere attributter fra HTML-koden i ett jafs..

 

Den følgende koden *krever* at attributtenes verdi er inneholdt i anførselstegn eller apostrofer, og escapes av anførselstegn og apostrofer kan inngå i verdien:

 

<?php

$input = "href=\"http://link.com\" alt=\"he's a \\\"he=man\\\"\" style=default heigth='30px'";

preg_match_all("/(\w+)=(\"[^\"\\\]*(\\\.[^\"\\\]*)*\"|\'[^\'\\\]*(\\\.[^\'\\\]*)*\')/", $input, $tmp);

$names = $tmp[1];
$values = $tmp[2];

$output = array();

foreach ($names as $index => $name) {
 $output["$name"] = preg_replace("/^([\"\'])(.*)\\1$/", "\$2", $values[$index]);
}

var_dump($output);

?>

 

Det tillates altså ikke verdier som style=default, da det her mangler anførselstegn eller apostrofer. Men skal du følge HTML-standarden støttes jo egentlig bare anførselstegn.. ;)

Lenke til kommentar

Jeg tok fram prinsippene med tilstandsmaskin fra hardware design og endte med denne koden:

<?php 
function error_msg($msg) { 
       echo "\n<h1>$msg</h1>\n"; 
} 

$str = "href=\"http://link.com\" alt=\"he's a \\\"he=man\\\"\" style=default heigth='30px'";
$state = 0; // s0:prekey, s1:key, s2:postkey, s10:preval s11:', s12:", s13:unquoted, s14:postval
$len = strlen($str);
$ar = array();
$key = "";
$val = "";
$activeescape = false;
for($i = 0; $i <= $len; $i++) {
 switch ($state) {
 	case 0: 
   $key = "";
   if(strlen(trim($str{$i}))) { 
   	$key = $str{$i};
   	$state = 1;
   }
   break;
 	case 1:
   if(strlen(trim($str{$i}))) { 
   	if($str{$i} == "=") { $state = 10; }
   	else { $key .= $str{$i}; }
   }
   else { $state = 2; }
   break;
 	case 2:
   if(strlen(trim($str{$i}))) { 
   	if($str{$i} == "=") { $state = 10; }
   	else { error_msg("error parsing attirbs at char $i: $str"); }
   }
   break;
 	case 10: 
   $val = "";
   if(strlen(trim($str{$i}))) { 
   	if($str{$i} == "\"") { $state = 12; }
   	else {
     if($str{$i} == "'") { $state = 11; }
     else {
     	$val = $str{$i};
     	$state = 13;
     }
   	}
   }
   break;
 	case 11:
 	case 12:
   if($activeescape) {
   	$val .= $str{$i};
   	$activeescape = false;
   }
   else {
   	if($str{$i} == "\\") {
     $activeescape = true;
     $val .= $str{$i};
   	}
   	else {
     if(($str{$i} == "'" && $state == 11) || ($str{$i} == "\"" && $state == 12)) { $state = 14; }
     else { $val .= $str{$i}; }
   	}
   }
   break;
 	case 13:
   if($activeescape) {
   	$val .= $str{$i};
   	$activeescape = false;
   }
   else {
   	if($str{$i} == "\\") {
     $activeescape = true;
     $val .= $str{$i};
   	}
   	else {
     if(strlen(trim($str{$i}))) { $val .= $str{$i}; }
     else {
     	$state = 0;
     	$ar[$key] = $val;
     }
   	}
   }
   break;
 	case 14:
   $ar[$key] = $val;
   $state = 0;
   break;
 }
}
print_r($ar); 
echo "$str\n"; 
?>

 

Mye kode, men den leser bare igjennom input en gang, og bør derfor være rask i drift. Har ikke klart å lure den til å gi feil resultat ennå.

Lenke til kommentar

Opprett en konto eller logg inn for å kommentere

Du må være et medlem for å kunne skrive en kommentar

Opprett konto

Det er enkelt å melde seg inn for å starte en ny konto!

Start en konto

Logg inn

Har du allerede en konto? Logg inn her.

Logg inn nå
×
×
  • Opprett ny...