PHP

Parsing en PHP : 4 techniques

substr, explode, sscanf et des expressions régulières !

→ Corrigé du Challenge : Bulma et la Capsule Corp.

Dans le challenge DBZ_2, il faut parser ce type de chaîne de caractères :

RNBOAPHELSK-182

Pour en sortir 2 informations :

  • Le code, RNBOAPHELSK (longueur variable)
  • Le poids, 182

Il faut donc parser la chaîne de caractères pour récupérer ces 2 valeurs. Passage en revue de plusieurs techniques de parsing.

Pour les exemples ci-dessous, on considère que RNBOAPHELSK-182 est stocké dans $object.

substr

Cette fonction permet de récupérer la portion d’une chaîne de caractères.

Dans ce cas précis, pour récupérer le code, il faut que je connaisse la longueur de celui-ci. Une possibilité est de déterminer la position du tiret « – » dans la chaîne principale.

$position = strpos($object, '-');
$code = substr($object, 0, $position);
// Et donc :
$weight = substr($object, $position + 1, 3);

Le code ci-dessus peut être optimisé :

$code = substr($object,
            0,
            strpos($object, '-')
);
$weight = substr($object, -3);

Je n’ai finalement plus besoin de $position et je passe donc strpos dans l’appel à substr.

Pour récupérer la fin d’une chaîne de caractères, je peux utiliser une valeur négative. Ici je récupère donc les 3 derniers caractères de $object.

La doc complète : substr.

explode

Cette fonction permet de découper une chaine de caractères, selon un séparateur. Ce séparateur peut être 1 seul caractère ou 1 chaine de caractères plus longue.

Ici, le tiret est le séparateur.

$informationss = explode('-', $object);
$code = $informations[0];
$weight = $informations[1];

explode retourne un tableau avec les différentes portions de la chaîne de caractères principale. Ici il y a 1 seul tiret, donc 2 portions, une avant (0, le code), une après (1, le poids). Le code ci-dessus peut être optimisé avec une double affectation :

[$code, $weight] = explode('-', $object);

La doc complète : explode.

sscanf

Cette fonction est « l’inverse » de la fonction sprintf.

sprintf permet d’intégrer les valeurs de variables dans une chaine de caractères, à la manière d’un publipostage.

$fruit = 'pomme';
echo sprintf('Je mange une %s', $fruit);
// Affiche : Je mange une pomme

s représente une chaîne de caractères.

sscanf va donc se comporter à l’inverse. C’est-à-dire que l’on va pouvoir définir le format d’une chaîne et en récupérer les différentes valeurs. Le problème avec notre challenge, c’est que scan utilise les espaces comme séparateur. Ce qui veut dire que ce code ne fonctionne pas comme souhaité :

[$code, $vert] = sscanf($object, '%s-%d');

Il n’y a pas d’espace, donc $code va récupérer toute la chaîne, et il n’y aura rien dans $weight.

On peut donc remplacer le tiret pour commencer :

$object = str_replace('-', ' ', $object);
[$code, $weight] = sscanf($object, '%s %d');

Cette fois-ci ça fonctionne 🙂

La doc complète : sscanf et sprintf.

Expressions régulières

Ah ces bonnes vieilles regex ! Analysons $object, il contient :

  • Un code, composé d’un nombre inconnu de lettres majuscules
  • Un poids, composé de 3 chiffres

On peut donc écrire ces 2 récupérations, par des expressions régulières :

preg_match('/[A-Z]+/', $object, $code);
$code = $code[0];

preg_match('/[0-9]{3}/', $object, $weight);
$weight = $weight[0];

Attention au fonctionnement de cette fonction, elle retourne le nombre de correspondances trouvées dans la chaîne de caractères principale. Et les correspondances sont assignées par référence, dans un tableau. Ici je sais que je ne vais avoir qu’une seule correspondance. Je récupère donc la valeur souhaitée avec [0].

La doc complète : preg_match

2 utilitaires pour rédiger ou tester des regex : phpliveregex et regex101.

Corrigé du challenge

Revenons au challenge !

Je choisis d’utiliser ici explode.

$total = 0;
foreach ($objects as $object) {

	[$code, $weight] = explode('-', $object);

	$codeMini = substr($code, 0, 2) . substr($code, -2) . '-' . floor($weight / 10);

	if (in_array($codeMini, $capsules)) {
		$total += $weight;
	}
}

J’initialise $total à 0, puisque je cherche une somme.

Je parcours tous mes objects (attention, le challenge fournit une clé « objets » et non « objects », mais on va corriger ça :p )

J’utilise donc explode pour extraire le code et le poids.

Je définis le codeMini. J’utilise ici substr puisque ce sont bien des portions de $code que je cherche. J’utilise d’ailleurs « -2 » pour les 2 derniers caractères.

Si $codeMini est dans $capsules, j’incrémente $total avec $weight.

Version fun condensée

$total = 0;
foreach ($objects as $object) {
	$total += (in_array(
			substr(explode('-', $object)[0], 0, 2) . 
			substr(explode('-', $object)[0], -2) . '-' . 
			floor(explode('-', $object)[1] / 10)
		, $capsules)
	) ? explode('-', $object)[1] : 0;
}

On remplace le if par un ternaire. Soit j’ajoute le poids, soit j’ajoute zéro.

Et je récupère à la volée les résultats de explode. Ici c’est pour le fun puisque mon explode est utilisé plusieurs fois, j’ai plutot intérêt à stocker le résultat. Mais cette syntaxe est possible.

On met en application ?

Voici 3 challenges qui démarrent avec des données à parser :


Qui a codé ce superbe contenu ?


Ta newsletter chaque mois

Corrigés, challenges, actualités, veille technique... aucun spam.