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 :

Comment sélectionner différentes lignes dans un fichier .xml ?


Sujet :

Langage PHP

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Nouveau candidat au Club
    Femme Profil pro
    Architecte matériel
    Inscrit en
    Avril 2015
    Messages
    2
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 38
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Architecte matériel

    Informations forums :
    Inscription : Avril 2015
    Messages : 2
    Par défaut Comment sélectionner différentes lignes dans un fichier .xml ?
    Bonjour à tous,

    Je dispose d'un fichier .xml de plus de 300 000 lignes.
    Certaines de ces lignes contiennent des URL que je modifie :

    Au départ j'ai ça :

    F:/Livre/2/Adams_Douglas/H2G2 Le Guide du Routard Galactique (6)/cover.jpg

    et en remplaçant les espaces, les parenthèses etc, j'obtiens ça :

    http://XXXXXX/2/Adams_Douglas/H2G2%2...6%29/cover.jpg (j'ai masqué la véritable adresse volontairement)

    Jusque là tout va bien, le problème c'est que je veux pouvoir selectionner uniquement les lignes comportant les URL, et non pas l'ensemble du document (j'ai besoin des espaces etc... dans mes autres lignes).

    Les lignes que je veux changer commencent toutes par la balise <cover> et se terminent par </cover> ou <format> et </format>

    J'arrive à poser des marqueurs (signets) sur les lignes qui m'intéressent mais impossible de toutes les selectionner en même temps de manière à "remplacer la sélection".

    Je peux uniquement copier les lignes qui ont été "marquées" dans un autre fichier, effectuer les modifications, mais ensuite je ne peux pas les réimporter dans le document initial à leurs bonnes places.

    Voici un bout de mon fichier pour que les choses soient plus clair :

    Code xml : 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
    <id>5</id>
        <size>296777</size>
        <isbn>9782266135955</isbn>
        <title sort="Stephanie Plum [4] Quatre ou double">Stephanie Plum [4] Quatre ou double</title>
        <authors sort="Evanovich,Janet">
          <author>Evanovich,Janet</author>
        </authors>
        <tags>
          <tag>Policier</tag>
        </tags>
        <comments>...</comments>
        <series index="4.0">Stephanie Plum</series>
        <cover>F:/Livre/2/Evanovich_Janet/Stephanie%5DPlum [4] Quatre ou double (5)/cover.jpg</cover>
        <formats>
          <format>F:/Livre/2/Evanovich_Janet/Stephanie Plum [4] Quatre ou double (5)/Stephanie Plum [4] Quatre ou double - Evanovich_Janet.epub</format>
        </formats>
      </record>

    ... puis ça continue toujours dans le même ordre avec d'autres livres.

    Est-il possible avec les expressions régulières de sélectionner toutes les chaînes de caractères d'une même page comprises entre les balises <cover></cover> et <format></format> ; de manière à ce que je puisse utiliser la fonction "remplacer la sélection" ? (je possède notepad++, pspad, et ultraedit) ?

    Existent-ils d'autres solutions ?

    Merci d'avoir pris le temps de lire tout ça !

  2. #2
    Membre éprouvé
    Avatar de TiranusKBX
    Homme Profil pro
    Développeur C, C++, C#, Python, PHP, HTML, JS, Laravel, Vue.js
    Inscrit en
    Avril 2013
    Messages
    1 476
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Seine et Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur C, C++, C#, Python, PHP, HTML, JS, Laravel, Vue.js
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2013
    Messages : 1 476
    Billets dans le blog
    6
    Par défaut
    je te proposerait cette solution
    Code PHP : 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
     
    //Remplacement de la souche site
    $xml = str_replace('F:/Livre/','http://XXXXXX/',$xml);
    //Récupération des lignes à traiter
    $tab = null;$tab2 = null;
    preg_match_all('#<cover>(.*)</cover>#',$xml,$tab);
    preg_match_all('#<format>(.*)</format>#',$xml,$tab2);
    $tab = array_merge($tab,$tab2);//fusion des tableaux
    $array1 = array(); $array2 = array();
    foreach($tab as $val)
        {
    	if($val[0][0] == '<')continue;//échappement pour tous les retour dans le tableau commençant par <cover> ou <format>
        $aval = str_replace('http://XXXXXX/','',$val[0]);//enlèvement des éléments à ne pas toucher
        $tab2 = explode('/',$aval);//création du tableau des chaines à encoder
        foreach($tab2 as $val2){$array1[$val2]='/'.$val2;$array2[$val2]='/'.rawurlencode($val2);}//préparation des tableaux de remplacement
        }
    $xml = str_replace($array1,$array2,$xml);//remplacement des chaines à encoder

  3. #3
    Nouveau candidat au Club
    Femme Profil pro
    Architecte matériel
    Inscrit en
    Avril 2015
    Messages
    2
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 38
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Architecte matériel

    Informations forums :
    Inscription : Avril 2015
    Messages : 2
    Par défaut
    Merci beaucoup pour votre réponse. Je vais tester votre code dans le week end.

    Cordialement,

    Otton

  4. #4
    Expert confirmé Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    2 986
    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 986
    Par défaut
    Basiquement on pourrait faire:
    Code php : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    define('INPUT_PATH', './path/books.xml');
    define('OUPUT_PATH', './writeable_dir/result.xml');
     
    $xml = file_get_contents(INPUT_PATH);
    $xml = preg_replace_callback('~<(?:format|cover)>\KF:/Livre/([^<]+)~', function ($m) {
        return 'http://XXXXXX/' . implode('/', array_map('rawurlencode', explode('/', urldecode($m[1]))));
    }, $xml);
    file_put_contents(OUTPUT_PATH, $xml);
    Ce qui consiste en résumé à tout fourrer d'un coup dans une chaîne, à appliquer la transformation des dits urls, puis à enregistrer la chaîne résultante dans un fichier.

    A: c'est imbattable du point de vue de la vitesse (~0,1s) et on peut difficilement faire plus court.
    I: c'est très gourmand en RAM car il faut charger l'intégralité du fichier en mémoire. À noter que cette méthode n'exploite pas la structure XML pour atteindre les urls, elle fait une simple recherche de chaînes de caractères.

    autre méthode:
    Pour préserver la RAM, on peut charger le fichier source ligne par ligne, traiter éventuellement la ligne si elle contient une URL et enregistrer le résultat au fur et à mesure:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    $fhr = fopen(INPUT_PATH, 'r');
    $fhw = fopen(OUTPUT_PATH, 'w');
    while (($line = fgets($fhr)) !== false) {
        if (strpos($line, '>F:/Livre/')) // test rapide pour ne pas appliquer la regex sur chaque ligne
            $line = preg_replace_callback('~<(?:format|cover)>\KF:/Livre/([^<]+)~', function ($m) {
                return 'http://XXXXXX/' . implode('/', array_map('rawurlencode', explode('/', urldecode($m[1]))));
            }, $line);
     
        fwrite($fhw, $line);
    }
    fclose($fhw);
    fclose($fhr);
    A: La performance est moins bonne que la méthode précédente mais reste plus qu'honorable (~0,6s). La consommation de mémoire est ridicule.
    I: Cette méthode ne s'appuie toujours pas sur la structure XML.

    Dernière méthode:
    Cette fois-ci on exploite la structure XML. Pour ce faire on va utiliser XMLReader qui permet de lire un document XML élément par élément sans pour autant charger tout le fichier source. Chaque élément (balise, texte, attribut, autre) est chargé, transformé en chaîne et puis enregistré au fur et à mesure.
    Si la balise ouvrante <cover> ou <format> est rencontrée, on passe un flag a true pour que le prochain nœud texte soit transformé.
    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
    $reader = new XMLReader;
    $reader->open(INPUT_PATH);
    $fh = fopen(OUTPUT_PATH, 'w');
     
    $containsURL = false;
     
    while($reader->read()) {
        if ($reader->nodeType === XMLReader::ELEMENT) { // c'est une balise ouvrante
            // si c'est une balise format ou cover, on passe le flag a true
            if ($reader->name === 'format' || $reader->name === 'cover')
                $containsURL = true;
     
            $result = '<' . $reader->name;
     
            // on vérifie s'il y a des attributs et on les ajoute au besoin
            if ($reader->moveToFirstAttribute())
                do {
                    $result .= ' ' . $reader->name . '="' . $reader->value . '"';
                } while ($reader->moveToNextAttribute());
     
            // si la balise est "auto-fermante", on ajoute un slash avant le >
            $result .= ($reader->isEmptyElement ? '/' : '' ) . '>';
     
        } elseif ($reader->nodeType === XMLReader::END_ELEMENT) { // c'est une balise fermante
            $result = '</' . $reader->name . '>';
     
        } elseif ($reader->nodeType === XMLReader::TEXT && $containsURL) { // c'est du texte après une balise ouvrante format ou cover
            $result = $reader->readString();
            if (strpos($result, 'F:/Livre/') === 0) {
                $result = substr($result, 9);
                $result = urldecode($result);
                $urlParts = explode('/', $result);
                $result = 'http://XXXXXX/' . implode('/', array_map('rawurlencode', $urlParts));
            }
            $containsURL = false;
        } else {
            $result = $reader->readString();
        }
        fwrite($fh, $result);
    }
    $reader->close();
    fclose($fh);
    A: la structure XML est exploitée pour atteindre les URLs. Ça consomme très peu de mémoire puisque tout se fait au fur et à mesure. La vitesse bien que moindre reste correcte (~2.1s) vue la taille du fichier.
    I: Le code est beaucoup plus long et complexe.

    À noter qu'il devient impensable de traiter des fichiers de cette taille avec DOMDocument ou simple_xml car le temps de construction de l'arbre DOM passe la barre de la minute et consomme une énorme quantité de mémoire.

    Détail de la pattern utilisée:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    ~                # Délimiteur
    <
    (?:format|cover) # groupe non-capturant: "format" OU "cover"
    >
    \K               # enlève ce qui précède du résultat
    F:/Livre/
    ([^<]+)          # capture tous les caractères qui ne sont pas des "<" (donc jusqu'à la balise fermante)
    ~
    Les codes précédents sont des illustrations de différentes manières de procéder. Il est clair que dans un "vrai code", on doit vérifier si l'ouverture des fichiers a réussi avant de faire quoi que ce soit.

Discussions similaires

  1. Comment stocker des tables dans un fichier XML ?
    Par TicTacToe dans le forum Décisions SGBD
    Réponses: 2
    Dernier message: 24/09/2006, 16h18
  2. [C# 2.0] Comment sauver une image dans un fichier Xml ?
    Par Louis-Guillaume Morand dans le forum C#
    Réponses: 4
    Dernier message: 08/09/2006, 17h47
  3. Réponses: 4
    Dernier message: 09/05/2006, 11h33
  4. Comment sélectionner une ligne dans une TStringGrid ?
    Par Ben_Le_Cool dans le forum Composants VCL
    Réponses: 11
    Dernier message: 22/08/2005, 12h38

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