Je suis actuellement en train d'ecrire un petit programme en Java qui est censé aller récuperer des dvds disponibles sur des sites marchands (une sorte de méta-moteur).
Là où le problème survient, c'est la lenteur avec laquelle mes Regexp sont matchées.
Prenons par exemple le code de la page suivante
http://mediabarre.2xmoinscher.com/DVD/liste.asp?strRech=robin%20des(...)
Et appliquons la regexp suivante :
()(<a TITLE=\"Cliquez[^>]*?href=(\".*?\")>)(.*?).+?Meilleur prix : <span class=\"DVD-TXT\">([^>]+?)
Sous visual-regexp, pas de problème, en 1 fraction de seconde, il m'isole les liens valides, avec leur prix, etc.
En revanche, sous mon programme, il mouline un ptit moment avant de me pondre le résultat, et ca j'aimerai bien savoir pourquoi (je ne parle pas d'optimiser ma regexp, pour ca c'est evident que ce n'est pas au point, mais meme non-optimisé, visual-regexp arrive a matché en 50 millisecondes, donc...)
Bref, ci joint le bout en question de mon code :
String mediabarre = new String("http://mediabarre.2xmoinscher.com/DVD/liste.asp?strRech=(...)");
String keywords=new String("robin des bois");
keywords=URLEncoder.encode(jTextField1.getText());
mediabarre = mediabarre + keywords;
String html="";
String tmp="";
try{
URL doc1 = new URL(mediabarre);
BufferedReader plop = new BufferedReader(new InputStreamReader(doc1.openStream()));
while((tmp=plop.readLine())!=null)
{
html = html + tmp;
}
plop.close();
Pattern p;
Matcher m;
p = Pattern.compile("()(<a TITLE=\"Cliquez[^>]*?href=(\".*?\")>)(.*?).+?Meilleur prix : <span class=\"DVD-TXT\">([^>]+?)");
m = p.matcher(html);
while (m.find())
{
txt_resultat.setText(txt_resultat.getText().concat("Mediabarre : " + m.group(4) + " pour " + m.group(5) + "\n"));
}
Voilà, si quelqu'un se sent l'âme charitable, je lui en serait très reconnaissant.
Merci
# arf...
Posté par cho7 (site web personnel) . Évalué à 1.
désolé...
->[]
# html = html + tmp
Posté par Wawet76 . Évalué à 4.
Tu peux même faire tes match dans la boucle qui récupère le code de la page en fait... (enfin il faut voir le code des pages)
[^] # Re: html = html + tmp
Posté par cho7 (site web personnel) . Évalué à 1.
[^] # \o/ Pierre Tramo
Posté par Pierre Tramo (site web personnel) . Évalué à 4.
me parait très sous-optimal, une boucle de lecture sur le InputStreamReader avec écriture vers un StringWriter, ça serait déjà moins violent. Bon, après , l'expression matchée tiens sur une ligne (ce qui a l'air d'être le cas), tester sur chaque ligne du BufferedReader, c'est effectivement encore mieux, ça évite de stocker tout le doc.
Déjà, ça faut pas le laisser passer [:totoz]. Un String mediabarre = "http://mediabarre.2xmoinscher.com/DVD/liste.asp?strRech=(...(...))" + URLEncoder.encode(jTextField1.getText()); sera bien plus efficace. En particulier, les String truc = new String("bidule") sont à proscrire, String truc = "bidule" marchera aussi bien.
Ça aussi, il faut le revoir. La concaténation de chaînes était très coûteuse, les concaténations en boucle sont catastrophiques pour les perf :
devrait légèrement accélérer les choses (le compilo remplace String s = a + b par String s = new StringBuffer(a).append(b).toString();, il faut toujours l'avoir en tête), d'autant plus que l'interface n'aura pas à être raffraichie à chaque fois.
Voilà, c'est ce qui me saute aux yeux en première lecture.
[^] # Re: \o/ Pierre Tramo
Posté par cho7 (site web personnel) . Évalué à 0.
Bon parcontre mon programme reste toujours très très monstrueusement lent comparé a visual-regexp... :(
[^] # Re: \o/ Pierre Tramo
Posté par cho7 (site web personnel) . Évalué à 1.
Là c'est nikel, quasi instantanné !!
Merci beaucoup :)
[^] # Re: \o/ Pierre Tramo
Posté par cho7 (site web personnel) . Évalué à 1.
si l'expression a matcher tenait sur plusieurs lignes, je n'aurai pas pu réaliser cette modification cruciale (j'ai réellement gagné 40 secondes de traitement), donc comment aurai-je pu faire ?
[^] # Re: \o/ Pierre Tramo
Posté par Pierre Tramo (site web personnel) . Évalué à 2.
[^] # Re: \o/ Pierre Tramo
Posté par Pierre Tramonson . Évalué à 1.
D'ailleurs la mise en page du code source est ignoble sur ce forum. En utilisant les balises [pre] ca donne quoi ?
[^] # Re: \o/ Pierre Tramo
Posté par Pierre Tramo (site web personnel) . Évalué à 3.
On peut les désactiver, mais ça devient pas pratique pour le texte qui entoure le code...
Faudrait ptet désactiver les retours chariot spécifiquement dans les <pre></pre> (ou utiliser une mise en syntaxe style wiki), mais je tiens trop à ce qu'il me reste de santé mentale pour me plonger dans le code templite et faire un patch :°)
[^] # Re: \o/ Pierre Tramo
Posté par cho7 (site web personnel) . Évalué à 1.
http://fuck.the.world.free.fr/RLFind.java(...)
Voilou
[^] # Re: \o/ Pierre Tramo
Posté par pifou . Évalué à 1.
Autrement à première vu tu codes avec Emacs ou VI, je te conseille d'essayer Eclipse tant que tu es a apprendre Java. Ca permet de vraiment gagner du temps au début (et même après).
[^] # Re: \o/ Pierre Tramo
Posté par cho7 (site web personnel) . Évalué à 1.
Sinon là c'est codé avec NetBeans, que j'utilise pas souvent car jle trouve lourd, mais contrairement a Eclipse il a un editeur de GUI qui me facilite la vie pour coder des trucs "rapidement". En l'occurence, il me fallait une GUI rapidement pour tester mon bouzin :)
Voilou
[^] # Re: \o/ Pierre Tramo
Posté par pifou . Évalué à 1.
Quand je fais un 'wget http://fuck.the.world.free.fr/RLFind.java(...) ' je tombe sur une classe etendant JApplet mais ne contenant aucun code avec des Regexp !!
En gros c'est juste une interface avec rien dans les méthodes de callback :).
Autrement, je suis peut être aveugle ou j'ai de la merde dans les yeux parceque normalement j'aurais du voir les tags "// GEN-" que met NetBeans dans le code généré avant de poster mon précédent commentaire :).
Enfin, j'ai mis un peu de temps à te répondre à cause du déplacement de ton journal dans les forums (ce qui est quand même plus sa place).
[^] # Re: \o/ Pierre Tramo
Posté par cho7 (site web personnel) . Évalué à 1.
(c'etait la version 1, je me contentais d'afficher les pages de chacun des sites, la v2 est donc censée filtrer elle meme les résultats avec les regexp)
[^] # Re: \o/ Pierre Tramo
Posté par Pierre Tramo (site web personnel) . Évalué à 2.
Oui, c'est peut-être plus la place, mais ça serait bien que les modéros n'utilisent pas la fonction de déplacement avant de l'avoir débuggée : y a pas longtemps, le nombre de commentaires affiché ne prenait pas en compte les commentaires postés quand c'était encore un journal, et maintenant le changement efface les informations sur les commentaires lus/non lus, et du coup, la navigation avec la barre ne sert plus à grand chose :/
[^] # Re: \o/ Pierre Tramo
Posté par Cédric Chantepie . Évalué à 0.
[^] # Re: \o/ Pierre Tramo
Posté par Pierre Tramo (site web personnel) . Évalué à 2.
Moi j'évite d'importer des packages entiers. D'une part ça doit ralentir un chouille la résolution de noms au chargement de la classe (j'avais remarqué en désassemblant des .class que le compilo ne remplaçait pas les * par la bonne liste de packages), mais surtout ça rend la maintenance du code plus difficile : si tu reprends ton code quelques mois/années plus tard et que t'as pas un bon IDE sous la main, tu risque de passer du temps à retrouver dans quel package était telle ou telle classe.
Vector, c'était dans l'ancienne API des collections. Dans l'actuelle, il y a la classe ArrayList qui est équivalente et qu'il vaut mieux utiliser : si plusieurs threads ne risquent pas d'accéder simultanément à une liste, les accès seront plus rapides en utilisant un ArrayList qu'un Vector ; et surtout, la nouvelle API des collections est plus complète, plus souple,... donc autant oublier l'ancienne. Et pour faire propre, on peut découpler le type de structure et son implémentation : List i = new ArrayList();
Hmmm pourquoi encoder les caractères accentués ?
Alors je reviens là dessus : on ne fait jamais String s = new String("plop"); (ça crée un fait deux objets String, inutilement), mais String s = "plop" (un seul objet String créé, implicitement).
Pas besoin de créer une chaîne vide pour initialiser le StringBuffer, new StringBuffer() donnera le même résultat.
Vu que le pattern est immuable, il est préférable de le compiler une fois pour toute et non à chaque appel de la méthode jButton1ActionPerformed, en le déclarant comme attribut de classe contstant (static final). Pour les chaînes constantes, c'est aussi plus propre de faire comme ça.
C'est une coquille, non ? Ça ne devrait pas être là ?
Bon, maintenant on va pouvoir commencer à regarder en détail :o
[^] # Re: \o/ Pierre Tramo
Posté par Pierre Tramo (site web personnel) . Évalué à 2.
Et c'est quasi instantanné (et je n'ai pas une machine de guerre), du même ordre que la recherche ligne par ligne (modulo le temps consommé par le réseau et l'interface graphique).
PS : tu as lu http://java.sun.com/j2se/1.4.2/docs/api/java/net/URLEncoder.html#en(...) ?
[^] # Re: \o/ Pierre Tramo
Posté par cho7 (site web personnel) . Évalué à 1.
Moi ma version a 40 seconde etait la 1ere postée initialement dans le topic.
La tienne est propre, mais moi en fait le truc, c'est que j'ai du mal a bien me servir efficacement de java.io, car j'ai l'impression que pour un truc précis, on peut tout prendre, mais chacun s'instancie un peu differement, ont des noms assez semblables, et j'avoue que je suis perdu comparé a mes fonction io en C qui etaient somme toute assez simples :)
StringReader, Reader StreamReader, etc, tout se ressemble, il y aurait pas un tuto expliquant quoi utiliser et comment ?
En tout cas merci beaucoup pour ton aide :)
[^] # Re: \o/ Pierre Tramo
Posté par Pierre Tramo (site web personnel) . Évalué à 2.
L'important à savoir pour commencer, c'est qu'il y a quatre classes (abstraites) de base, selon qu'on manipule des flux d'octets (InputStream, OutputStream) ou des flux de caractères (Reader, Writer). Ensuite on choisit une implémentation selon le support du flux (fichier, String/StringBuffer, tableau d'octets) ou on obtient le flux en retour d'une méthode (sockets), il existe aussi des passerelles de flux d'octets vers flux de caractères. Enfin, il y a les classes (toujours abstraites) Filter* (et quelques autres) qui ajoutent des facilités pour la manipulation d'un flux (quel qu'en soit le support), selon l'implémentation qui correspond au besoin (Buffered*, Print*, ...).
Voilà, c'est un bref résumé. Après il y a aussi java.nio et ses Channels mais c'est une autre histoire ;)
[^] # Re: \o/ Pierre Tramo
Posté par cho7 (site web personnel) . Évalué à 1.
Bref, encore merci pour ton aide crucial pour le developpement de ma ptite appli !
# je peux pas t'aider
Posté par manatlan (site web personnel) . Évalué à -2.
peut être qques regexp simple ... et c tout ...
non ? ;-)
[^] # Re: je peux pas t'aider
Posté par Volnai . Évalué à 1.
[^] # Re: je peux pas t'aider
Posté par cho7 (site web personnel) . Évalué à 1.
Oui mon programme est en java, et je ne connais pas du tout perl ni python, donc je n'envisage pas un seul instant un autre langage :)
[^] # Re: je peux pas t'aider
Posté par manatlan (site web personnel) . Évalué à -1.
tu mettras autant de temps à faire ta regexp, qu'à comprendre le python ...
de plus, tu peux faire du java, des class, utiliser des libs du monde java, en python ... avec jython !
[^] # Re: je peux pas t'aider
Posté par gc (site web personnel) . Évalué à 3.
reste crédible quand même :)
# WebL
Posté par Antonio Da Silva (site web personnel) . Évalué à 3.
Ecrit en java, il peut être appelé depuis ton code, ou appeler ton code existant ( pour pouvoir mettre en base par ex. ).
Pour lui trouver les noeuds SPAN qui ont un attribut CLASS égal à
DVD-TXT, c'est "fingers in the nose".
Je te laisse regarder les exemples pour te faire une idée :
http://research.compaq.com/SRC/WebL/examples.html(...)
[^] # Re: WebL
Posté par cho7 (site web personnel) . Évalué à 1.
Autrement, comment se fait il qu'avec l'api regex de java ce soit aussi long pour parser ? c'est mon algorithmie qui foire totalement, ou c'est l'api qui est lente ?? Car comparé a visual-regexp par exemple, c'est carrément le lièvre et la tortue...
[^] # Re: WebL
Posté par Pierre Tramo (site web personnel) . Évalué à 2.
Et si tu veux faire un bench, fais bien attention de ne pas inclure le coût de la récupération du document par réseau, et aussi celui de l'interface graphique.
# Swing
Posté par Roger Rabbit . Évalué à 1.
C'est du pinaillage mais dans ton code tu executes des opérations "lentes"
a l'intérieur du thread Swing.
Dans Swing, les evenements générés par l'interface
utilisateur sont reçus par la JVM et stockés dans une file d'evenements. A
l'intérieur de la JVM, un thread d'expedition d'evenements ( implémenté par la
class java.awt.EventQueue) surveille cette file et expédie les évenements
arrivants aux listeners intéressés.
Le thread Swing s'occupe d'afficher les composants graphiques. Si tu effectues
des traitements couteux à l'intérieur de ce thread, tu vas empecher la mise
à jour de l'interface.
( Note : la méthode actionPerformed() ici correspond à ta méthode jButton1ActionPerformed() , le listModel est le modele d'une JList et correspond à ton
composant JTextArea - mais c'est plus explicite avec un listModel )
1. Exemple de ce qu'il *ne faut pas* faire :
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Ici le thead Swing est bloqué pendant 1 seconde, si tu resize la fenetre par exemple, elle ne serait pas repainte .
public void actionPerformed(ActionEvent event) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 100; i++) {
val++;
listModel.addElement(new Integer(val));
}
}
2. Exemple de ce qu'il *ne faut pas* faire :
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Ici le travail est bien effectué à l'exterieur du thread Swing, mais le modele de liste n'est pas mis à jour à l'intérieur du thead Swing.
public void actionPerformed(ActionEvent event) {
new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 100; i++) {
val++;
listModel.addElement(new Integer(val));
}
}
}).start();
}
}
3. La bonne manière de procéder
~~~~~~~~~~~~~~~~~~~~~~
- le travail est effectué à l'extérieur du thread Swing
- la mise à jour du composant graphique est effectuée à l'intérieur du thread Swing.
public void actionPerformed(ActionEvent event) {
new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
SwingUtilities.invokeLater(new Runnable() {
public void run() {
for (int i = 0; i < 100; i++) {
val++;
listModel.addElement(new Integer(val));
}
}
});
}
}).start();
}
Références
~~~~~~~
Le threading dans Swing :
http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html(...)
Les exemples de code et plus d'explication :
http://www.clientjava.com/blog/2004/08/20/1093059428000.html(...)
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.