IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage PHP Discussion :

Matches à l'envers


Sujet :

Langage PHP

  1. #1
    Membre régulier
    Profil pro
    Développeur Web
    Inscrit en
    Octobre 2010
    Messages
    153
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Octobre 2010
    Messages : 153
    Points : 107
    Points
    107
    Par défaut Matches à l'envers
    Bonjour à tous,
    J'ai un système de données dont l'ordre est important et contenant une information supplémentaire codée par le caractère '_'. Les données sont dans un format un peu comme CSV, séparées par des |.
    Par exemple |data1|data2_|data3|data4|data5|data6|

    J'ai ensuite une liste de chaines de données plus petites et il faut que je vois si elles matchent. Par exemple |data4|data5| est bien retrouvé dans ma chaine.

    Là où ça se complique c'est que lorsque j'ai le caractère '_' dans la grande chaine de données, ça veut dire que je permets que les petites aient ou non ce caractère.
    Par exemple les petites chaines |data2|data3| et |data2_|data3| sont bonnes car data 2 dans la grande chaine permet d'avoir un '_'.
    Mais |data4_|data5| n'est pas bonne car data4 ne permet pas de '_' au niveau de la grande chaine.

    L'utilisation de PCRE veut que ce soit la chaine qui permet de multiples possibilités qui serve de pattern, donc |data1|data2(_?)|data3|data4|data5|data6|
    MAIS l'utilisation de PCRE exige que ce soit la plus petite chaine qui soit cherchée dans la grande, et donc là je sous bloqué.
    Si j'ai la petite chaine |data4_|data5| et que j'en fait le pattern |data4(_?)|data5| ca va être trouvé dans al grande chaine alors qu'il ne faut pas. C'est à la grande chaine de dicter l'autorisation de caractère.

    Voilà pour l'exposé du problème, je vous suis d'avance reconnaissant pour votre aide

  2. #2
    Expert éminent Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    2 858
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Justicier interdimensionnel

    Informations forums :
    Inscription : Mars 2009
    Messages : 2 858
    Points : 6 556
    Points
    6 556
    Par défaut
    un petit extrait de données avec le résultat correspondant ne serait pas de refus.
    Brachygobius xanthozonus
    Ctenobrycon Gymnocorymbus

  3. #3
    Membre régulier
    Profil pro
    Développeur Web
    Inscrit en
    Octobre 2010
    Messages
    153
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Octobre 2010
    Messages : 153
    Points : 107
    Points
    107
    Par défaut
    Ce n'est pas plus compliqué que ça puisque les données sont numériques.

    Donc du genre |12_|324|74_|321_|10|25|
    Je peux avoir une liste comme :
    - |12|324| qui peut être retenu
    - |12_|324| qui peut être retenu
    - |324|74| qui peut être retenu
    - |10_|25| qui ne peut pas être retenu cas

  4. #4
    Expert éminent Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    2 858
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Justicier interdimensionnel

    Informations forums :
    Inscription : Mars 2009
    Messages : 2 858
    Points : 6 556
    Points
    6 556
    Par défaut
    Le principe ne pose pas de problème, il est clair. Ce qui n'est pas clair c'est la manière dont se présente tes données et aussi ce que tu souhaites en faire (s'agit il de valider une chaîne, d'effectuer un remplacement, autre chose?). Donc si tu veux rendre ton problème compréhensible, merci de poster du code (entre balises code) avec la ou les chaînes à traiter, et tes éventuelles tentatives, genre:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    $str = '...';
    preg_match('/.../', $str);
    Parce que pour l'heure on ne sait même pas si tu parles d'une chaîne de caractères, de plusieurs, d'un tableau de chaînes ou de je ne sais quoi d'autre. Il est actuellement impossible de te fournir la moindre réponse.
    Brachygobius xanthozonus
    Ctenobrycon Gymnocorymbus

  5. #5
    Membre régulier
    Profil pro
    Développeur Web
    Inscrit en
    Octobre 2010
    Messages
    153
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Octobre 2010
    Messages : 153
    Points : 107
    Points
    107
    Par défaut
    Explication de l'objectif :
    Chaque nombre est un ID représentant une chaine de caractères. Pour des raisons de conservation de données, on doit réduire les chaines à leur ID. C'est un genre d'encodage compact.
    Le caractère '_' montre qu'il y a un espace (suite de caractères non définis) avant l'élément suivant de la chaine. S'il n'y est pas, cela veut dire que les chaines sont collées sans caractère supplémentaire entre les 2 chaines.
    L'utilisateur choisi ces chaines et oblige ou non que les éléments soient collés.

    Par exemple :
    1 = AAAAA
    2 = BBBBB
    Si l'utilisateur veut créer A collé à B (contrainte), on aura AAAAABBBBB codé |1|2|
    Si l'utilisateur ne met pas la contrainte de coller les chaines, c'est qu'on permettrait AAAAABBBBB codé |1|2| ET aussi la chaine AAAAA???BBBBB codée |1_|2|. La version |1_|2| veut dire qu'on peut ou non être collé.

    On a des sources en base de données qui seront par exemple AAAAABBBBB ou AAAAAxxxxxBBBBB, et n'importe quoi d'autre CCCDDDxxxEEE et autres
    L'idée est que sur une longue chaine choisie par l'utilisateur, on trouve les petites chaines présentent en base de données, qu'on les remplace dans la grande chaine (en commençant par les petites chaines qui contiennent le plus d'éléments possible) et ensuite on aura à travailler les morceaux qui n'ont pas trouvé de source.

    Voici un bout de code qui explique la démarche.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    // objet source (simplifié)
    class Source{
    	public $id;
    	public $data;
    	public $nbElements;
    	__construct($id,$data){
    		$this->id=$id;
    		$this->data=$data;
    	}
    }
     
    $sources = []; // Les sources sont récupérées en base de données
    $sources[0] = new Source(1, '|12|324|');
    $sources[1] = new Source(2, '|324|74|9_|87');
    $sources[2] = new Source(3, '|94|');
    $sources[3] = new Source(4, '|10|25|');
    $sources[4] = new Source(5, '|10_|25|');
    sortSources($sources); // une fonction qui trie les sources en ordre descendant du nombre d'éléments. Ce n'est pas la longueur de la chaine mais la quantité d'identifiants présents. Ici la source d'id 2 au début et d'id 3 à la fin en conservant les clés associées
     
    $data = '|12_|324|74_|321_|10|25|'; // exemple de la chaine longue d'après les données de l'utilisateur
     
    // pour chaque élément des sources, on veut remplacer dans la chaine data les parties qui peuvent correspondre
    // C'est LA que j'ai le soucis car c'est à la grande chaine de dicter la présence/absence de '_' et non à la petite
    foreach ($sources as $k => $s) {
    	$data = preg_replace(toRegexp($s->data), '|s'.$k.'|', $data);
    }
     
    // maintenant $data devrait valoir |s0|74_|321_|s3|
    $objets = []
    foreach (explode('|', substr($data , 1, -1)) as $d) {
    	if ($d[0] == 's') {
    		$k = intval(str_replace('s', '', $d));
    		$objets[] = $sources[$k];
    	} else {
    		$objets[] = new Source();
    	}
    }
    // ... on traite les WorkingObject selon qu'ils existent déjà en base ou non
     
    function toRegexp($string) {
    	return '#'.str_replace('_', '(_?)', str_replace('|', '\|', $string)).'#';
    }
    J'espère que ça éclairci le contexte. Merci.

  6. #6
    Expert éminent Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    2 858
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Justicier interdimensionnel

    Informations forums :
    Inscription : Mars 2009
    Messages : 2 858
    Points : 6 556
    Points
    6 556
    Par défaut
    Ça doit se résoudre assez simplement. Juste une petite chose, dans quel ordre la fonction de tri des sources place-t-elle deux éléments similaires comme |10|25| et |10_|25|? Est-ce qu'après le nombre d'éléments c'est l'ordre ascii qui est pris en compte?
    Brachygobius xanthozonus
    Ctenobrycon Gymnocorymbus

  7. #7
    Membre régulier
    Profil pro
    Développeur Web
    Inscrit en
    Octobre 2010
    Messages
    153
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Octobre 2010
    Messages : 153
    Points : 107
    Points
    107
    Par défaut
    Il ne différencie pas les 2
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function sortSources() {
    	// Sort to have the sources with greater number of features first
    	usort($this->sources, function ($a, $b) {
    		return $b->nbElements - $a->nbElements;
    	});
    }
    // nbElements est calculé comme ça
    function numberOfElements($string) {
    	return substr_count(str_replace('|_|', '|', $string), '|') - 1;
    }

  8. #8
    Expert éminent Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    2 858
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Justicier interdimensionnel

    Informations forums :
    Inscription : Mars 2009
    Messages : 2 858
    Points : 6 556
    Points
    6 556
    Par défaut
    Bon, je ne vois pas trop ce que les regex viennent faire là dedans, et m'est avis que si tu aboutis à une situation comme celle-là c'est peut-être qu'il y a des simplifications à faire en amont.
    Cela-dit tu peux procéder comme suit (arrange toi pour produire le tableau $sources de départ:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    // sources déjà triées
    $sources = [ 
      '|s1|' => '|324|74|9_|87|',
      '|s0|' => '|12|324|',
      '|s3|' => '|10|25|',
      '|s4|' => '|10_|25|',
      '|s2|' => '|94|'
    ];
     
    $data = '|12_|324|74_|321_|10|25|';
     
    // on créé une table de correspondance à partir de $data
    $corr = ['|' => '||'];
     
    foreach(explode('|', trim($data, '|')) as $v) {
        if (strrpos($v, '_', -1))
            $corr['|' . rtrim($v, '_') . '|'] = "|$v|";
    }
     
    // on transforme $sources en plaçant systématiquement le underscore sur les nombres qui en possèdent dans $data
    $trans = array_merge(['|'=>'||'], array_flip(array_unique(array_map(function ($i) use ($corr) {
        return '|'. trim(str_replace(array_keys($corr), $corr, $i), '|') . '|';
    }, $sources))), ['||'=>'|']);
     
    $result = str_replace(array_keys($trans), $trans, $data);
    Brachygobius xanthozonus
    Ctenobrycon Gymnocorymbus

  9. #9
    Membre régulier
    Profil pro
    Développeur Web
    Inscrit en
    Octobre 2010
    Messages
    153
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Octobre 2010
    Messages : 153
    Points : 107
    Points
    107
    Par défaut
    Merci pour cette approche intéressante. Il y a une logique qui pourrait me permettre d'optimiser quelques algorithmes.

    Cela dit, je n'ai pas réussi à être clair par rapport aux underscore et du coup en prenant en compte cette donnée, je pense ne pas pouvoir me passer des regex.
    Pour résumer leur importance : la grande chaine de données a des _ aux endroits ou on peut OU NON avoir un espacement. D'où un regex. C'est la grande chaine qui décide. S'il n'y a pas de _ cela veut dire qu'il faut coller les éléments. Sinon on peut les coller ou non.
    Par contre les petites chaines décrivent un objet qui est fixe, donc si l'un contient un espacement (_) alors il ne doit pas matcher une partie de $data sans '_' (collage obligatoire) mais peut matcher une partie de $data qui permet un espacement.

    De plus, la chaine de données peut tout à fait comporter plusieurs fois le même identifiant, voir la même séquence d'identifiants mais avec des espacements ('_') placés à des endroits différents. Donc le découpage de la grande chaine avec l'utilisation des éléments comme clé n'est pas possible.

    Je me suis donc résolu, un peu comme tu le propose, à découper la chaine de données à un moment, mais pas tout de suite.
    Je fais une première passe avec les petites chaines sans me soucier des _ (je crée la grande et chaque petite chaine sans '_'). Je sélectionne ainsi les sources potentielles en fonction de la trame des IDs. Rapide et simple.
    Ensuite je fais une seconde passe plus stringente, en permettant tous les espaces possibles (regex de la petite chaine contre la grande). S'il y a un match, je retourne la situation et vérifie que la zone de la grande chaine qui matche (c'est elle qui détermine les endroits où on peut avoir des espacements ou non) est bien compatible avec la petite qui "ressemblait" à ce qu'il faut.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    $sources = []; // liste des sources utilisables
    $dbSources = [......]// je récupère toutes les sources de la base
     
    // Récupérer les sources dont les séquences d'ID sont bonnes sans se soucier des espacements
    $dataSansEspace = sansEspace($data);
    foreach ($dbSources as $s) {
    	if (@strpos($dataSansEspace , sansEspace($s->data)) !== false) $sources[] = $s;
    }
     
    // Sélectionner les sources qui correspondent vraiment
    foreach ($sources as $k => $s) {
    	if (preg_match_all(tousEspacesRegex($s->data), $data, $matches)) {
    		// On a reconnaissance d'une petite chaine sur la chaine EXACTE de données (une ou plusieurs fois), traiter les matches
    		foreach ($matches[0] as $m) {
    			if (preg_match(toRegexp($m), $s->data)) {
    				// la portion de la grande chaine correspond bien, remplacer par la clé de la source avec un 's' pour la retrouver
    				if ($m[0] == '_') $m = substr($m, 1); // on ne veut pas manger l'info d'espace de l'élément précédent
    				$data = str_replace($m, '|s'.$k.'|', $data);
    			}
    		}
    	}
    }
     
    // On traite la chaine $data modifiée
    foreach (explode('|', substr($data , 1, -1)) as $d) {
    	...
    }
     
    function sansEspace($string) {
    	return str_replace('||', '|', str_replace('_', '', $string));
    }
    function tousEspacesRegex($string) {
    	$s = str_replace('|', '\|', str_replace('|', '(_?)|', $string));
    	return '#'.$s.'#';
    }
    function toRegexp($string) {
    	// Il se peut qu'on ait un espace en début de chaine, ce qui se concrétise par |_|123..., donc on enlève le premier | pour retrouver 456_|123...
    	if (substr($string, 0, 2) ==  '|_') $string= substr($string, 1); 
    	return '#'.str_replace('_', '(_?)', str_replace('|', '\|', $string)).'#';
    }
    Je ne sais pas s'il y a un moyen d'améliorer en prenant un peu des 2 solutions...
    Un preg_replace_callback() pourrait améliorer la lisibilité, évitant des imbrications de boucles et de tests, mais ne changerait peut-être pas la quantité de ressources serveurs utilisée.
    En tout cas ça fait le job !

Discussions similaires

  1. [FLASH MX] Lecture à l'envers
    Par Mouf dans le forum ActionScript 1 & ActionScript 2
    Réponses: 9
    Dernier message: 30/04/2006, 00h04
  2. Réponses: 2
    Dernier message: 22/12/2003, 15h23
  3. Requete select pour récupérer les no match entre 2 tables
    Par Celina dans le forum MS SQL Server
    Réponses: 4
    Dernier message: 16/12/2003, 11h59
  4. "Match nul"
    Par Sylvain James dans le forum XML/XSL et SOAP
    Réponses: 4
    Dernier message: 17/06/2003, 10h27
  5. template match="node() mais pas text()"
    Par Manu_Just dans le forum XSL/XSLT/XPATH
    Réponses: 4
    Dernier message: 26/03/2003, 10h52

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo