TreeSet sortedSet = new TreeSet<String>(); // création de SetPas besoin de TreeSet. Déjà un TreeSet ça va te classer les titres par ordre alphanumérique : aucun intérêt. Conserver les titres dans l'ordre dans lequel on les a trouvé, a un intérêt, parce que c'est l'ordre dans la liste de lignes, donc :
Set sortedSet = new LinkedHashSet<>();Cela dépend de la solution qu'on va implémenter : on pourrait en fait se contenter d'un HashSet.
patternPage.matcher(ligne).matches(); // suppression des numéros des pagesNe sert strictement à rien. On teste si la ligne est un numéro de page, et c'est tout. On ne fait rien si le test est vrai. On ne fait rien si le test est faux.
Il faut exploiter le résultat :
Code:
1
2
3 if ( !patternPage.matcher(ligne).matches() ) { // si ce n'est pas un numéro de page, alors on stocke cette ligne comme titre dans le set (après nettoyage bien sûr) }
int index1=ligne.lastIndexOf('.');//retourne last index de '.'Cela retourne bien la position du dernier point. Or, ce n'est pas ce qu'on veut pour supprimer les ....
Exemple de titre ............................... xxxindex1 est la position du . rouge, or c'est la position du point vert qu'on veut. On pourrait utiliser un indexOf() mais c'est risqué, parce qu'on pourrait avoir . dans le titre.
Ce qu'on va plutôt faire c'est chercher le caractère qui n'est pas un point avant le dernier point.
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 int index1=ligne.lastIndexOf('.'); if ( index1>=0 ) { // vaut mieux tester le cas, on ne sait jamais, et commen ça on est sûr qu'il n'y aura pas de plantage while (index1>=0 && ligne.charAt(index1)=='.' ) { // tant qu'on est encore dans la String et que le caractère est un ., alors... index1--; // ...on recule d'un caractère } // si index1>=0, alors c'est la position avant le point vert if ( index1>= 0 ) { // si index1 est < 0, alors la ligne ne contenait que des . ligne = ligne.substring(0, index1+1); // il faut prendre le dernier caractère avant le point vert (bon évidemment, à priori ça doit être une espace, mais autant ne pas se poser la question, on le prend c'est tout ligne = ligne.trim(); // on supprime les espaces devant et derrière, if ( !ligne.isEmpty() ) { // pas la peine de garder des lignes vides sortedSet.add(ligne); // on stocke le titre } } }
while(ligne.contains("Table of Contents") && ligne.contains("Overview")); // parcours des lignes entre "Table of Contents" et "Overview"Non ! Ce while dit qu'on parcourt les lignes tant qu'elles contiennent à la fois "Table of Contents" et "Overview". Comme aucune ligne ne contient à la fois ces deux expressions, tout ce qu'on fait c'est ajouter la ligne courante, éventuellement nettoyée de ses .... et de ce qui suit, et ça, pour toutes les lignes (le for) : donc le texte entier est dans le set des titres !!!
Il faut parcourir la liste jusqu'à ce qu'on trouve "Table of contents".
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 boolean onATrouveTableOfContents = false; // au début on ne l'a pas trouvé for(String ligne : lignes) { if ( onATrouveTableOfContents ) { // on a déjà trouvé le début de la table, alors on traite if ( ligne.trim().equals("Overview") ) { // si on trouve le titre "Overview" (avec contains on va trouver "Overview.........4" !!!) // on a parcouru toute la table des matière, on peut s'arrête break; } else if ( !patternPage.matcher(ligne).matches() ) { // là tu recopies le code ci-dessus, int index1=ligne.lastIndexOf('.');.... } } } } } else if ( ligne.trim().equals("Table of Contents") ) { // on trouve le début de la table onATrouveTableOfContents = true; // on le marque } }
if ( sortedSet.contains("on met quoi ici pour savoir s'il y a un titre ou non?") && !ligne.contains("(Scored)") && !ligne.contains("(Not Scored)") )On ne peut pas utiliser directement un contains, parce
- le titre n'est pas forcément complet
- si le titre est complet, il contient (Score) ou (Not Scored)
- En plus, il y a des titres sans (Scored) ou (Not Scored)
- Et la table des matières ne contient pas tous les titres de niveau 2 (il assez pourri quand même ce PDF, faut le dire - à un moment, on ne peut pas faire des miracles non plus, Garbage In Garbage Out).
Dans le cas où le titre contient (Scored) ou (Not Scored), on peut l'enlever, faire un trim : titre = ligne.replaceAll("\\(Scored\\)|\\(Not Scored\\)","").trim();, et faire un contains :
sortedSet.containt(titre) sera effectivement vrai si la ligne est un titre.
Mais pour les titres multi lignes, c'est plus compliqué. Si on continue le traitement dans l'ordre, on ne peut pas savoir facilement si la ligne est un bout de titre, et on risque surtout éventuellement de trouver un titre qui n'en n'est pas.
Ce que tu peux faire, c'est faire un for (pas un forEach). C'est chercher les lignes qui contiennent (Scored) ou (Not Scored) et faire :
Code:
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
43
44
45
46
47 for(int i=0; i<lignes.size(); i++) { String ligne = lignes.get(i); if ( ligne.contains("(Scored)") || ligne.contains("(Not Scored)")) { String titre = ligne.replaceAll("\\(Scored\\)|\\(Not Scored\\)","").trim(); if ( sortedSet.contains( titre ) ) { // on est sûr d'avoir trouver un titre complet } else if ( patternTitre.matcher(ligne).matches() ) ) { // on un titre qui n'est pas dans la table des matières mais qui matches la regexp (qui commence donc par des nombres et des points) } else { // on est sûr d'avoir trouver un bout de fin de titre, il faut donc revenir en arrière pour trouver le titre complet for(int j=i-1; j>=0; j--) { // on parcourt les lignes en revenant vers le début ligne = lignes.get(j) + lignes; titre = lignes.remove(j) + titre; // on concatène la ligne avec le titre et on l'enlève de la liste i--; // comme on l'a enlevé de la liste, on décrémente i if ( sortedSet;contains( titre.trim() ) { // on a trouvé le titre complet, on remplace le bout de titre par le titre complet lignes.set(i, ligne); break; // on sort de la boucle for j } else if ( patternTitre.matcher(ligne).matches() ) ) { // on un titre qui n'est pas dans la table des matières mais qui matches la regexp (qui commence donc par des nombres et des points) lignes.set(i, ligne); break; // on sort de la boucle for j } } } } } else if ( sortedSet.contains( ligne ) { // on a un titre sans "Scored" ou "not scored"... } else // }
Une autre solution consisterait à parcourir déjà toutes les titres du sortedSet. C'est pour ça qu'un LinkedHashSet serait utile (pour la solution précédente), un HashSet suffirait.
Pour chaque titre, tu cherches la prochaine ligne qui commence par le titre, ou une ligne qui commencerait le titre, ou une ligne qui qui matcherait la regexp.
(Autre solution, fait un parcourt à deux curseurs, un sur les titres, dans le sortedSet, un sur les lignes, caractères par caractères : simuler donc une recherche de texte. Plus compliqué pour toi, et je n'ai ni l'envie ni le temps de d'expliquer).Code:
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 int i=0; for(String titre : sortedSet) { while( i<lignes.size() ) { String ligne = lignes.get(i); if ( ligne.equals( titre ) ) { // on a trouvé le titre exacte } else if ( ligne.startsWith( titre ) ) { // on a trouvé un titre tel qu'il y a quelque chose après dans la ligne (probablement (Scored) ou (Not Scored) } else if ( titre.startsWith( ligne ) ) { // on a trouvé un début de titre, il suffit de chercher la fin (donc jusqu'à ce qu'on trouve Scored / Not Scored, ou jusqu'à ce qu'on ait le titre complet ( la concaténation des lignes égal à titre) } else if ( la ligne matche le pattern ) { // on a trouvé un titre du type 1.1.1 machin mais qui n'est pas dans la table des matières... on cherche jusqu'à ce qu'on trouve (Scored)/(Not Scored) } // il faut bien incrémenter i, et surtout sortir de la boucle lorsqu'on a trouvé un titre sauf dans le cas pattern } }
Troisième solution, de simplification : tu analyses le PDF pour voir s'il existe des titres de la table des matières qui sont sur plusieurs lignes ou qui ont Scored/Not Scored. Si ce n'est pas le cas, alors tu peux conserver l'algorithme qu'on avait fait jusqu'à présent, et simplement utiliser sortedSet.contains( ligne ) pour les autres. Si tu as des titres qui ont Scored/Not Scored, tu peux utiliser le sortedSet.contains() mais après avoir supprimer (Scored)/(Not Scored). Si tu as des titres sur plusieurs lignes, alors tu ne pourras pas utiliser cette troisième solution.
Il y a également, évidememnt, la solution de revoir l'extraction complètement, pour traiter ça en amont (en se basant sur les tailles de police par exemple).