Compter les occurences de lignes et non de mots provenant d'un fichier texte
Bonjour à tous,
Je commence à apprendre le langage Java et je ne trouve pas de solution à mon problème. Je dois compter le nombre d'occurrences de lignes identiques issues d'un fichier texte. J'ai trouvé un début de solution avec le code ci-dessous mais cela me compte le nombre d'occurrences de mots alors que je souhaite identifier et incrémenter les lignes identiques. J'ai tenté une autre approche avec Collector.GroupBy mais alors là, j'ai des erreurs de partout. Est-ce que quelqu'un pourrait m'indiquer une piste à suivre svp ? Merci d'avance pour votre aide.
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
|
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class AnalyticsCounterV2 {
private static final String FILENAME = "C:\\\xxx.txt";
public static void main(String[] args) throws FileNotFoundException, IOException {
Map list = new HashMap();
try (BufferedReader br = new BufferedReader(new FileReader(FILENAME))) {
StringBuffer sb = new StringBuffer();
String line = br.readLine();
while (line != null) {
String[] words = line.split(" ");
for (int i = 0; i < words.length; i++) {
if (list.get(words[i]) == null) {
list.put(words[i], 1);
} else {
int newValue = Integer.valueOf(String.valueOf(list.get(words[i])));
newValue++;
symptomslist.put(words[i], newValue);
}
}
sb.append(System.lineSeparator());
line = br.readLine();
}
}
HashMap<String, String> sorted = new HashMap<String, String>(list);
for (Object key : sorted.keySet()) {
System.out.println(key + " : " + list.get(key));
}
}
} |
Question sur la POO sur ce sujet
Bonjour,
Je me permets de déterrer ce topic car il est toujours d'actualité :
grand débutant en programmation java et faisant visiblement le même parcours de formation Java que Lanasandra, je me prenais la tête pour comprendre comment résoudre le problème de ce projet. Tout d'abord Joël, grand merci pour tous ces éléments de réponses ! C'est merveilleux ! En revanche je ne peux pas dire que j'ai le niveau pour le moment pour écrire ce type de solution :calim2:.
je précise que l'énoncé du projet est exactement le même, à savoir lire un fichier de symptômes avec des doublons à trier et créer un fichier dans lequel les occurrences sont comptabilisées.
J'ai écris le code comme suit en m'inspirant évidemment de vos échanges, sans passer par la HashMap mais directement avec la TreeMap. J'ai gardé pas mal de choses en commentaires pour m'aider à bien comprendre tout le processus et de quoi faire ma javadoc. Est-ce bien ou est-ce que je saute des étapes ?
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 48 49 50 51 52 53 54 55 56 57 58
|
public class AnalyticsCounterV2 {
public static void main(String[] args) throws IOException {
try {
/*
* Cette ligne charge toutes les lignes en mémoire, dans une liste.
*/
List<String> lines = Files.readAllLines(Paths.get("Project02Eclipse/symptoms.txt"));
/*
* Ici, on compte les doublons grâce à la Map. en paramètres, Clé (ligne) et
* valeur (nb d'occurrences). Avoir instancié "TreeMap" permet de sortir les
* symptômes dans l'ordre alphabétique en affichage de console.
* Utilisation du comparateur avec la classe Collator. Cela prendra en compte les accents
* (si l'on veut une version du fichier result.out en français par exemple).
*
*/
TreeMap<String, Long> comptTri = new TreeMap<>(Collator.getInstance());
for (String symptoms : lines) {
if (comptTri.containsKey(symptoms)) {
// si la ligne contient déjà la ligne, on incrémente le compteur qui est associé :
comptTri.put(symptoms, comptTri.get(symptoms) + 1);
} else {
// sinon on ajoute l'association en initialisant le compteur à 1 (1L étant pour le type long) :
comptTri.put(symptoms, 1L);
}
}
// Sortir la liste des symptômes dans la console :
Files.lines(Paths.get("Project02Eclipse/symptoms.txt")).forEach(System.out::println);
// Sortir la TreeMap en brut dans la console :
System.out.println(comptTri);
/*
* Cette ligne permet d'écrire dans le fichier resultV2.out les résultats.
*/
Files.write(Paths.get("resultV2.out"), lines);
/*
* Cette partie permet d'écrire dans le fichier resultV3.out les résultats triés, à la ligne et avec le nb d'occurrences.
*/
List<String> finallines = comptTri.entrySet()
.stream()
.map(entry-> entry.getKey()+": "+entry.getValue())
.collect(Collectors.toList());
Files.write(Paths.get("resultV3.out"), finallines);
} catch(IOException e) {
e.printStackTrace();
}
}
} |
Mes questions :
- Il nous est demandé de faire en sorte que le code corresponde aux principes de la programmation orientée objet. J'imagine donc que cela demande de créer d'autres classes avec leurs méthodes. Ce code étant déjà très concis, est-il possible de faire autrement (créations et utilisation d'interfaces, créations de classes à part dont les méthodes seront invoquées dans le main) ? plus décomposé d'une certaine manière ?
- En plus du main (AnalyticsCounter.java) à corriger, nous avions une classe ReadSymptomDataFromFile.java et une interface avec la méthode GetSymptoms (elle même implémentée dans le ReadSymptomDataFromFile.java). Mais là j'ai eu beau me creuser, je ne sais pas comment appeler/faire fonctionner dans le main ce code. La solution que j'ai trouvé ici me convient parfaitement mais je veux vraiment comprendre comment ça fonctionne et répondre au mieux à ce qui m'est demandé. A savoir que ces deux fichiers sont des pistes pour la correction :
Le Main d'origine à corriger que j'avais déjà bien débroussaillé pour pas dire avoir enlevé une très grosse partie:
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
|
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class AnalyticsCounter {
public static void main(String args[]) throws Exception {
try {
/* Lecture du fichier symptoms.txt via le BufferedReader */
BufferedReader reader = new BufferedReader(new FileReader("Project02Eclipse/symptoms.txt"));
String line = reader.readLine();
//HashMap<String, Integer> occurrences = new HashMap<String, Integer>();
int i = 0;
while (line != null) {
i++;
System.out.println("symptom : " + line);
line = reader.readLine(); // get another symptom
}
// next generate output
FileWriter writer = new FileWriter("result.out");
writer.write("\n");
writer.close();
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} |
Le début de correction fourni avec l'énoncé :
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
|
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* Simple brute force implementation
*
*/
public class ReadSymptomDataFromFile implements ISymptomReader {
private String filepath;
/**
*
* @param filepath a full or partial path to file with symptom strings in it, one per line
*/
public ReadSymptomDataFromFile (String filepath) { // constructeur
this.filepath = filepath;
}
@Override
public List<String> GetSymptoms() {
ArrayList<String> result = new ArrayList<String>();
if (filepath != null) {
try {
BufferedReader reader = new BufferedReader (new FileReader(filepath));
String line = reader.readLine();
while (line != null) {
result.add(line);
line = reader.readLine();
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
} |
Et enfin, l'interface comme piste de correction également :
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
import java.util.List;
/**
* Anything that will read symptom data from a source
* The important part is, the return value from the operation, which is a list of strings,
* that may contain many duplications
*
* The implementation does not need to order the list
*
*/
public interface ISymptomReader {
/**
* If no data is available, return an empty List
*
* @return a raw listing of all Symptoms obtained from a data source, duplicates are possible/probable
*/
List<String> GetSymptoms ();
} |
Du coup, quelles seraient les bonnes pratiques ? et surtout comment utiliser la classe ReadSymptomDataFromFile ?
A savoir que dans toutes mes recherches j'étais tombé sur la notion de TreeMap (bien meilleure qu'une HashMap dans le cadre de ce projet), c'est ce qui m'a permis par extension d'arriver sur ce super topic.
Merci pour votre aide ^^.
Soucis avec résultat : @hexadecimal au lieu du résultat attendu
Hello !
Je reviens avec mon sujet et mon programme qui a bien avancé, pour ne pas dire quasiment bouclé.
J'ai donc tout écrit et posé une grande partie dans le main(String[] args). Tout fonctionne à merveille, écriture des résultats triés dans mon fichier avec le nb d'occurrences, dans l'ordre alphabétique, affichage des résultats sur la console nickel. Côté procédural on est bon :D.
Sur les conseils d'une étudiante avancée, l'idée est de laisser moins de choses dans le main et de décomposer le code pour être plus POO. J'ai donc décomposé/rangé comme bon me semblait, ce qui me paraissait logique au départ et c'est là que je me perds dans mes appels de classes et de méthodes. j'ai en effet un résultat avec un hexadecimal dans mon fichier au lieu de ma liste triée, ordonnée.
L'idée de l'exercice étant de bien comprendre l'utilisation des méthodes, des classes, des appels aux méthodes. Et puis au final c'est vraiment ce qui m'est demande ;)
J'ai donc fait comme suit :
Mon main
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
| package com.hemebiotech.analytics;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Map;
import java.util.TreeMap;
public class AnalyticsCounterV5 {
public static void main(String[] args) throws IOException {
FileWriterData finalResult = new FileWriterData();
finalResult.writeSymptomsData();
/* Appel de la TreeMap et affichage dans la console pour vérifier que l'output est bon.
*
* */
SortSymptomsData consoleOutput = new SortSymptomsData();
TreeMap<String, Integer> symptoms = consoleOutput.sortingSymptomsData();
for (Map.Entry<String, Integer> entry : symptoms.entrySet())
System.out.println("Symptoms : " + entry.getKey() + ": " +entry.getValue());
}
} |
La Classe ReadSymptomDataFromFile écrite de base dans le sujet de l'exercice et implémente l'interface ISymptomReader (une seule methode, getSymptoms())
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 48 49 50 51 52 53 54 55 56 57
| package com.hemebiotech.analytics;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* Simple brute force implementation
*
*/
public class ReadSymptomDataFromFile implements ISymptomReader {
private String filepath;
/**
*
* @param filepath a full or partial path to file with symptom strings in it, one per line
*/
public ReadSymptomDataFromFile (String filepath) { // constructeur
this.filepath = filepath;
}
@Override
// Ecriture camelCase pour getSymptoms au lieu de GetSymptoms.
public List<String> getSymptoms() {
ArrayList<String> result = new ArrayList<String>();
if (filepath != null) {
try {
BufferedReader reader = new BufferedReader (new FileReader(filepath));
String line = reader.readLine();
while (line != null) {
result.add(line);
line = reader.readLine();
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
@Override
public int sum(int a, int b) {
int c = a + b;
return c;
}
} |
Ma classe SortSymptomsData qui appelle la classe ReadSymptomDataFromFile et ses méthodes . "J'ai rangé" ici la lecture effective de mon fichier et la fonction de tri avec la TreeMap.
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
| package com.hemebiotech.analytics;
import java.util.List;
import java.util.TreeMap;
public class SortSymptomsData {
public TreeMap<String, Integer> sortingSymptomsData() {
ReadSymptomDataFromFile file = new ReadSymptomDataFromFile("Project02Eclipse/symptoms.txt");
file.getSymptoms();
List<String> symptomsFile = file.getSymptoms();
TreeMap<String, Integer> symptomsTri = new TreeMap<>();
for (String listSymptoms : symptomsFile) {
if (symptomsTri.containsKey(listSymptoms)) {
symptomsTri.put(listSymptoms, symptomsTri.get(listSymptoms) + 1);
} else {
symptomsTri.put(listSymptoms, 1);
}
}
return symptomsTri;
}
} |
Ma classe FileWriterData qui va écrire les résultats du tri de la TreeMap dans un fichier "en dur" nommé "results.out"
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package com.hemebiotech.analytics;
import java.io.FileWriter;
import java.io.IOException;
public class FileWriterData {
public void writeSymptomsData() throws IOException {
try {
SortSymptomsData symptomsSorting = new SortSymptomsData();
FileWriter writer = new FileWriter("resultV5.out");
writer.write("Symptoms List " + symptomsSorting);
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} |
Je précise que mon prof/mentor m'a aidé à rédiger la partie TreeMap dans le main pour afficher le résultat en console.
En effet, si j'écrivais :
Code:
1 2
| consoleOutput.sortingSymptomsData();
System.out.println(consoleOutput); |
J'avais comme résultat en sortie de console : com.hemebiotech.analytics.SortSymptomsData@e2d56bf.
Avec la correction de mon prof, et de l'appel de la TreeMap en tant qu'objet dans le main et de menus changements dans la classe SortSymptomsData, pas de soucis, affichage en console parfait.
En revanche pour la partie écriture de fichier, ci j'écris ceci :
Code:
1 2
| FileWriterData finalResult = new FileWriterData();
finalResult.writeSymptomsData(); |
je me retrouve toujours avec quelque chose du genre : Symptoms List com.hemebiotech.analytics.SortSymptomsData@71dac704. Je n'ai toujours pas ma liste de symptômes triés avec leur nombre d'occurrence comme en affichage console. Et j'ai l'impression que c'est autrement plus complexe que l'écriture en console. Je pensais que faire appel aux classes qui contiennent mes méthodes suffirait.
j'ai eu beau essayer dans tous les sens, et de faire différents appels aux classes je me perds dans mon raisonnement (et je me dis que ma façon de décomposer mon code de base est peut être un peu bord*** finalement :aie:):
- Est ce que je dois faire appel à ma classe contenant ma TreeMap dans ma classe FileWriterData puis faire appel à cela dans mon main ?
- Est ce que je dois tout simplement reproduire ce qui a été écrit dans le main ?
Ex : TreeMap<String, Integer> symptomFile = finalResult.writeSymptomsData();.
Mais dans ce cas là dans mon FileWriterData je vais devoir écrire public TreeMap<String, Integer> writeSymptomsData() et il va me demander d'écrire return null puisqu'il n'y a pas de valeur à retourner.. Bref je sais que ce n'est pas ça.
Il doit y avoir encore des notions dans la POO que je ne sais pas encore..
Merci pour le coup de main ^^.