Remplacement nombres dans string

Bonsoir,

Je dois parser une string qui peut avoir un contenu du type :

informazione
3 twitter.com/signupcf53.html
2 play.google.com/store/apps/detailsc2bc.html
1 arcmc.hypotheses.org/category/veille-scientifique/portails.html
1 www.addthis.com/bookmarkda7d.html
=7
(0)

Il faut supprimer les nombres suivis d'un espace, les trucs du type =nombre avec nombre entre 0 et 1000000 exclut, même chose pour (nombre)

mais il faut absolument que les nombres dans les urls ne soient pas supprimés

Par exemple, dans twitter.com/signupcf53.html 53 doit rester

J'ai essayé ça :

infos[1] = infos[1].replaceAll("[0-9] ","");

infos[1] = infos[1].replaceAll("=[0-9]","");

infos[1] = infos[1].replaceAll("([0-9])","");

infos[1] = infos[1].replaceAll(" ","");

infos[1] = infos[1].replaceAll("=","");

infos[1] = infos[1].replaceAll("\\(","");

infos[1] = infos[1].replaceAll("\\)","");

mais ça ne marche pas

Merci d'avance, j'ai vraiment besoin que ça marche...

6 réponses

Résumé de la discussion

Problème central: concevoir une expression régulière en Java qui supprime les nombres hors des URL tout en préservant ceux présents dans les adresses. Des réponses variées proposent des patterns pour ignorer les chiffres internes aux URL, traiter les chiffres entourés de signes et parenthèses, et filtrer les nombres suivis d'un espace ou d'un signe égal. Des résultats montrent des échecs: des chiffres hors URL comme =9 ou =101, des espaces après le signe égal, ou des cas où les chiffres entre 1 et 999999 ne sont pas correctement distingués. En cas de besoin, certains conseillent d'utiliser plusieurs passes ou trois expressions distinctes et de s'appuyer sur des ressources externes comme des tutoriels Regex ou des tests en ligne.

Bobot (l’IA à votre service)
  1. Bonjour

    je ne sais pas comment s'implémente une Regex en Java, mais ça peut répondre à ton besoin.

    Un exemple ici
    http://regexstorm.net/tester?p=%28%3f%3a%5cd%2b+%29%7c%28%3f%3a%28%3f%3c%3d%5c%28%29%5cd%2b%28%3f%3d%5c%29%29%29&i=3+twitter.com%2fsignupcf53.html+%0d%0a2+play.google.com%2fstore%2fapps%2fdetailsc2bc.html+%0d%0a1+arcmc.hypotheses.org%2fcategory%2fveille-scientifique%2fportails.html+%0d%0a1+www.addthis.com%2fbookmarkda7d.html+%0d%0a%3d7+%0d%0a%280%29+&r=remplacement


    Cet exemple ne tient pas compte de la contrainte
    entre 0 et 1000000 exclut
    mais ça peut se gérer.
    Click sur l'onglet "Context" pour voir le résultat final
    1. Voilà qui fait
      http://regexstorm.net/tester?p=%28%3f%3a%28%3f%3c%3d%5b%5e%5cd%5d%7c%5e%29%28%5b1-9%5d%7c%5cd%7b2%2c6%7d%29+%29%7c%28%28%3f%3c%3d%5c%28%29%28%5b1-9%5d%7c%5cd%7b2%2c6%7d%29%28%3f%3d%5c%29%29%29&i=3+twitter.com%2fsignupcf53.html+%0d%0a2+play.google.com%2fstore%2fapps%2fdetailsc2bc.html+%0d%0a1+arcmc.hypotheses.org%2fcategory%2fveille-scientifique%2fportails.html+%0d%0a1+www.addthis.com%2fbookmarkda7d.html+%0d%0a%3d7+%0d%0a%280%29+%0d%0a%283%29%0d%0a123456+1000000+100000+999999+%28999999%29&r=remplacement
    2. @Utilisateur anonymeMerci de la réponse,

      J'ai essayé de modifier pour supprimer n'importe quel nombre qui n'est pas dans l'URL, ça semble fonctionner.

      Pouvez-vous expliquer comment fonctionne la syntaxe des regexs ?

      Par exemple, il faudrait rajouter quoi, pour supprimer les = et les parenthèses ouvrantes et fermantes ?
  2. Alors non, je ne vais pas t'expliquer toutes les syntaxes possibles, y'a des tutos tout faits pour ça
    Celui là est orienté vers php, vers il me semble bien introduire la chose
    https://www.lucaswillems.com/fr/articles/25/tutoriel-pour-maitriser-les-expressions-regulieres
    Celui-ci concerne l'implémentation dans Java
    https://cyberzoide.developpez.com/tutoriels/java/regex/

    Sur RegexStorm (et beaucoup d'autres sites de tests en ligne), tu as des exemples (page Référence)
    Pour les parenthèses et les = je pensais qu'il fallait les exclure

    http://regexstorm.net/tester?p=%28%3f%3a%28%3f%3c%3d%5b%5e%5cd%5d%7c%5e%29%3d%3f%28%5b1-9%5d%7c%5cd%7b2%2c6%7d%29+%29%7c%28%5c%28%28%5b1-9%5d%7c%5cd%7b2%2c6%7d%29%5c%29%29&i=3+twitter.com%2fsignupcf53.html+%0d%0a2+play.google.com%2fstore%2fapps%2fdetailsc2bc.html+%0d%0a1+arcmc.hypotheses.org%2fcategory%2fveille-scientifique%2fportails.html+%0d%0a1+www.addthis.com%2fbookmarkda7d.html+%0d%0a%3d7+%0d%0a%280%29+%0d%0a%283%29%0d%0a123456+1000000+100000+999999+%28999999%29&r=remplacement

    Quand j'étais petit, la mer Morte n'était que malade.
    George Burns
    1. J'ai essayé (?:(?<=[^\\d]|^)([1-9]|\\d*) )|((?<=\\()([1-9]|\\d*)(?=\\))) avec replaceAll, mais ça ne supprime pas les nombres quand ils ne sont pas dans l'URL, par exemple pour une ligne avec =9, il y a toujours le 9
    2. Dans ton exemple, =7 est suivi d'un espace, ça m'a servit.
      Si ça n'est pas le cas, il faut poster un exemple représentatif.

      De plus cette syntaxe, exclut les = et les parenthèse et ne tient pas compte de 1 à 999999
    3. désolé, y a pas d'espace après =nombre.

      Le seul probleme restant est que la suppression de =nombre ne marche pas avec un nombre >9, =101 se transforme en 01
  3. Poste un texte représentatif.
    1. Ce que j'ai besoin de connaitre c'est ce qu'il y a derrière le nombre précédé d'un =
    2. Là ça marche s'il y a un espace, une fin de ligne ou la fin du texte, mais si c'est du texte, je ne peux pas savoir si on est dans une URL ou pas.
      http://regexstorm.net/tester?p=%28%3f%3a%28%3f%3c%3d%5b%5e%5cd%5d%7c%5e%29%3d%3f%28%5b1-9%5d%7c%5cd%7b2%2c6%7d%29%28%3f%3d%28+%7c%5cr%7c%24%29%29%29%7c%28%5c%28%28%5b1-9%5d%7c%5cd%7b2%2c6%7d%29%5c%29%29&i=3+twitter.com%2fsignupcf53.html+%0d%0a2+play.google.com%2fstore%2fapps%2fdetailsc2bc.html+%0d%0a1+arcmc.hypotheses.org%2fcategory%2fveille-scientifique%2fportails.html+%0d%0a1+www.addthis.com%2fbookmarkda7d.html+%0d%0a%3d7+%0d%0a%280%29+%0d%0a%283%29%0d%0a123456+1000000+100000+999999+%28999999%29%0d%0a%3d101+coucou%0d%0a%3d101coucocu%0d%0a%3d101%0d%0a%3d101&r=remplacement
  4. Modérateur
    Bonjour,

    Il n'y a pas vraiment de spécificités d'expressions régulières en Java ce sont les même règles que partout ailleurs (avec peut-être quelques ajouts).

    La documentation est sur la classe Pattern (c'est indiqué dans la javadoc de la méthode replaceAll que tu as utilisé)

    https://docs.oracle.com/javase/9/docs/api/java/util/regex/Pattern.html

    Cependant, est-ce bien utile de faire une expression régulière ici ?
    Tu as un nombre, un espace, et tu prends tout le reste, donc un indexOf pour trouver l'espace, un substring pour récupérer la fin et c'est bon...

    String line = "42 blabla17toto";
    int n = line.indexOf(" ");
    line = line.substring(n+1); // blabla17toto
    1. Salut, c'est un peu plus compliqué, il y a les nombres suivis d'espaces, mais aussi les nombres précédés d'un = ou ceux entre parenthèses.
    2. Modérateur
      @Utilisateur anonymeSi je me base juste sur les exemples donnés, les autres cas concernent des lignes qui ne commencent pas par des nombres et qui sont donc à ignorer.

      Il faut donc d'enrichir un petit peu pour vérifier que la première partie est un nombre ou pas, mais je pense que ça reste plus simple (et peut-être même plus performant) qu'une expression régulière.

      String line = "42 blabla17toto";
      int n = line.indexOf(" ");
      if (n > 0) {
          String header = line.substring(0, n);
          try {
              int headerPart = Integer.parseInt(header);
              if (headerPart > 0 && headerPart < 1000000) {
      
                  String result = line.substring(n + 1);
                  // ...
              }
          } catch (RuntimeException e) {
              // header n'est pas un int
          }
      }

      Après oui, on peut sûrement le faire avec une expression régulière, je propose
      ^\d+ ([^\d].*)
      mais j'ai peut-être effectivement mal compris la question...
    3. Niveau performance, faut essayer et comparer les durées d’exécution.
      Sur un texte entier, j’ai généralement constaté une meilleure efficacité des regex, sur une seule ligne pas forcément.
    4. Mon fichier est composé de lignes comme celles-ci :

      1 www.umrespace.org/credits.htm
      1 www.unicaen.fr/recherche/mrsh/forge.html
      1 www.univ-angers.fr/fr/recherche/actualites/les-territoires-du-cinema/janvier.html
      1 www.viamichelin.fr/tpl/fr/divers/htm/legalnotice.htm
      1 www.zdnet.fr/actualites/skype-pret-a-changer-pour-gagner-sa-place-dans-les-entreprises-39361540.htm
      1 www2.cnrs.fr/presse/communique/3705.htm
      1 www2.cnrs.fr/presse/communique/3709.htm
      1 www2.cnrs.fr/presse/communique/3711.htm
      1 www2.cnrs.fr/presse/communique/3713.htm
      1 www2.cnrs.fr/presse/communique/3715.htm
      1 www2.cnrs.fr/presse/communique/3717.htm
      1 www2.cnrs.fr/presse/communique/3719.htm
      1 www2.cnrs.fr/presse/communique/3723.htm
      =1726
      (0)

      Je veux supprimer les "1 " (ça peut aussi être 25 comme 46)

      et les "=2736" et "(0)" mais ne pas toucher aux URLs, dans ma version actuelle, j'ai un problméme avec les lignes du type 1 www.legifrance.gouv.fr/affichTexte.do;jsessionid=73323E2953E34259F5F3E45F69925E1F537a.html

      ou le "=" de l'URL est supprimé et ça fait planter mon moteur de recherche

      Au niveau du filtrage, j'ai mis ça :

      infos[1] = infos[1].replaceAll("(?:(?<=[^\\d]|^)([0-9]|\\d*) )|((?<=\\()([0-9]|\\d*)(?=\\)))","");

      infos[1] = infos[1].replaceAll(" ","");

      infos[1] = infos[1].replaceAll("=","");

      infos[1] = infos[1].replaceAll("\\(","");

      infos[1] = infos[1].replaceAll("\\)","");
      infos[1] = infos[1].replaceAll("^[0-9]","");

    5. Tu veux supprimer (0), mais 0 n'est pas contenu entre 1 et 999999?
      Et dans cet exemple il y a un espace après =1726.
  5. Modérateur
    Pour le challenge, je viens de tester les deux codes (le mien et celui de Whismeril), les deux donnent le même fichier texte en sortie (à un espace près... car "=1726 " est remplacé chez moi par "" contre " " pour Whismeril)

    Entrée :
    1 www.umrespace.org/credits.htm 
    1 www.unicaen.fr/recherche/mrsh/forge.html
    1 www.univ-angers.fr/fr/recherche/actualites/les-territoires-du-cinema/janvier.html
    1 www.viamichelin.fr/tpl/fr/divers/htm/legalnotice.htm
    1 www.zdnet.fr/actualites/skype-pret-a-changer-pour-gagner-sa-place-dans-les-entreprises-39361540.htm
    1 www2.cnrs.fr/presse/communique/3705.htm
    1 www2.cnrs.fr/presse/communique/3709.htm
    1 www2.cnrs.fr/presse/communique/3711.htm
    1 www2.cnrs.fr/presse/communique/3713.htm
    1 www2.cnrs.fr/presse/communique/3715.htm
    1 www2.cnrs.fr/presse/communique/3717.htm
    1 www2.cnrs.fr/presse/communique/3719.htm
    1 www2.cnrs.fr/presse/communique/3723.htm
    =1726
    (0)

    Sortie :
    www.umrespace.org/credits.htm 
    www.unicaen.fr/recherche/mrsh/forge.html
    www.univ-angers.fr/fr/recherche/actualites/les-territoires-du-cinema/janvier.html
    www.viamichelin.fr/tpl/fr/divers/htm/legalnotice.htm
    www.zdnet.fr/actualites/skype-pret-a-changer-pour-gagner-sa-place-dans-les-entreprises-39361540.htm
    www2.cnrs.fr/presse/communique/3705.htm
    www2.cnrs.fr/presse/communique/3709.htm
    www2.cnrs.fr/presse/communique/3711.htm
    www2.cnrs.fr/presse/communique/3713.htm
    www2.cnrs.fr/presse/communique/3715.htm
    www2.cnrs.fr/presse/communique/3717.htm
    www2.cnrs.fr/presse/communique/3719.htm
    www2.cnrs.fr/presse/communique/3723.htm

    J'ai joué les deux codes sur un fichier texte de 500'000 lignes, et même si les temps sont assez aléatoires (accès I/O...), on peut quand même en déduire que les temps sont environ 5 fois plus long avec une regex (dans ce cas précis).

    import java.io.IOException;
    import java.nio.file.Files;
    import java.nio.file.Paths;
    import java.util.function.Function;
    
    public class Test {
    
        public static long test(String src, String dst, Function<String, String> mapper) throws IOException {
            long n = System.nanoTime();
            Files.write(Paths.get(dst), (Iterable<String>) Files.lines(Paths.get(src)).map(mapper)::iterator);
            return System.nanoTime() - n;
        }
    
        public static String testWhismeril(String line) {
            return line.replaceAll("(=([1-9]|\\d{2,6})\\b)|(\\b([1-9]|\\d{2,6}) )|(\\(\\d+\\))", "");
        }
    
        public static String testKx(String line) {
            int n = line.indexOf(" ");
            if (n > 0) {
                String header = line.substring(0, n);
                try {
                    int headerPart = Integer.parseInt(header);
                    if (headerPart > 0 && headerPart < 1000000) {
                        return line.substring(n + 1);
                    }
                } catch (RuntimeException e) {
                    // header n'est pas un int
                }
            }
            return "";
        }
    
        public static void main(String[] args) throws IOException {
            System.out.println(test("C:/test.txt", "C:/testKx.txt", Test::testKx)); // 191.178337 ms
            System.out.println(test("C:/test.txt", "C:/testWhismeril.txt", Test::testWhismeril)); // 665.538444 ms
            System.out.println(test("C:/test.txt", "C:/testKx.txt", Test::testKx)); // 57.867188 ms
            System.out.println(test("C:/test.txt", "C:/testWhismeril.txt", Test::testWhismeril)); // 268.716917 ms
            System.out.println(test("C:/test.txt", "C:/testKx.txt", Test::testKx)); // 35.440601 ms
            System.out.println(test("C:/test.txt", "C:/testWhismeril.txt", Test::testWhismeril)); // 252.767325 ms
        }
    }
    1. 5 fois quand même!
      Faut dire que la Regex à 3 pattern, c'est un peu tordu, il faudrait voir ce que ça donne avec 3 regex.
    2. Modérateur
      @Utilisateur anonymeIl faut voir aussi qu'avec un replaceAll on n'utilise pas toute la puissance des expressions régulières, puisqu'on se limite à un seul groupe de capture, de plus Java va devoir reconstruire la regex à chaque ligne, ce qui va lui prendre du temps, alors que c'est toujours la même (mais il ne le sait pas).

      Voici comment s'en sortir avec une vrai regex, compilée une seule fois au début.

      private static final Pattern pattern = Pattern.compile("^(\\S+\\s)?(\\D.*)");
      
      public static String testKx2(String line) {
          Matcher matcher = pattern.matcher(line);
          if (matcher.find() && matcher.group(1) != null) {
              return matcher.group(2);
          } else {
              return "";
          }
      }

      Le résultat est un meilleur, seulement 2x le temps de mon code sans regex, à comparer au 5x qu'on avait avec la grosse regex du replaceAll.

      NB. Ici je ne gère pas le cas où l'entier est supérieur à 1000000 mais j'ai l'impression que c'est une propriété qui n'est pas discriminante pour le découpage (si on a un entier ce sera toujours un nombre inférieur à cette limite).
  6. Merci pour votre aide, la fonction semble marcher maintenant.

    Mais j'ai un autre problème : J'ai une autre fonction qui se charge de scanner un fichier composé de liens de pages et de liens vers la page qui mène à cette page (origine).

    Normalement je devrais avoir 27394 pages dans la HashMap après scan, mais j'en ai que 27387.

    J'ai essayé de faire des prints un peu partout pour voir ce qui se passe, mais ça semble pourtant bien fonctionner, mais le scan doit manquer des pages. J'ai passé 3 après-midi sur cette fonction (readWebPages(), de la classe SearchEngine) sans arriver à trouver le problème et j'aimerai bien le trouver.

    Je mets les fichiers du projet dans un lien lien supprimé par la modération

    Le code (pour éviter les robots trop curieux) 88cPvRc4

    Les fichiers txt scannés sont dans le répertoire data du projet...
    1. Modérateur
      Vu que ce n'est pas la même question, créé une nouvelle discussion, et postes ton code sur le site, c'est un forum communautaire, donc les réponses doivent être utiles à d'autres et s'ils n'ont pas la question (donc le code qui va avec) ça ne sert qu'à toi et ce n'est pas le but.
    2. @KXLe liens vers le zip ne suffit pas pour "le code" ?