Transformer la classe SAXFrequence pour afficher le contenu texte des balises <p> et <said>
/*
* Cours Java / POO
* M2 Pro Ingénierie Multilingue
* INALCO
*/
package fr.crim.a2012.saxigraph;
import java.io.IOException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
/**
* Une spécialisation de la classe DefaultHandler qui affiche sur la sortie
* standard le contenu texte des balises <p> et <said>
*
* @author Pierre DITTGEN
*/
public class SAXFrequence extends DefaultHandler {
private StringBuilder text = new StringBuilder();
private boolean dansP;
private boolean dansSaid;
/**
* Méthode appelée sur la lecture de contenu texte
*/
@Override
public void characters(char[] ch, int start, int length) {
if (dansP || dansSaid) {
text.append(ch, start, length);
}
}
/**
* Méthode appelée sur une balise ouvrante
*/
@Override
public void startElement(String uri, String localName, String qName, Attributes attrs) {
if ("p".equals(localName)) {
dansP = true;
} else if ("said".equals(localName)) {
dansSaid = true;
}
if (dansP || dansSaid) {
text.setLength(0);
}
}
/**
* Méthode appelée sur une balise fermante
*/
@Override
public void endElement(String uri, String localName, String qName) {
if ("p".equals(localName)) {
dansP = false;
System.out.println(text);
}
if ("said".equals(localName)) {
dansSaid = false;
System.out.println(text);
}
}
/**
* Méthode principale
* @param args paramètres de la ligne de commande (ignorés)
* @throws SAXException En cas de XML non valide
* @throws ParserConfigurationException Peu probable
* @throws IOException Si le fichier XML n'est pas trouvé par exemple
*/
public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setNamespaceAware(true);
spf.setValidating(false);
spf.setFeature("http://xml.org/sax/features/validation", false);
SAXParser saxParser = spf.newSAXParser();
saxParser.parse("rsc/monfichier.xml", new SAXFrequencePSaid());
}
}
Objectif : afficher les mots les plus fréquents par fréquence décroissante dans les balises <p> et les balises <said>
Créer une classe utilitaire pour la découpe de phrase en mots (pour la langue française).
Remplacer le contenu de la classe FrenchTokenizerTest par celui-ci :
package fr.crim.a2012.util;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.util.Arrays;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
* Une classe de test pour notre découpeur de mots du français
*/
public class FrenchTokenizerTest {
private FrenchTokenizer frTokenizer;
@Before
public void preTests() {
frTokenizer = new FrenchTokenizer();
}
@Test
public void vide() {
String[] mots = frTokenizer.tokenize("");
assertNotNull(mots);
assertEquals(0, mots.length);
}
@Test
public void unMot() {
String[] mots = frTokenizer.tokenize("chêne");
assertEquals(1, mots.length);
assertEquals("chêne", mots[0]);
}
@Test
public void gestionEspace() {
String[] mots = frTokenizer.tokenize(" bouleau ");
assertNotNull(mots);
assertEquals(1, mots.length);
assertEquals("bouleau", mots[0]);
}
@Test
public void plusieursMots() {
String[] mots
= frTokenizer.tokenize("Auprès de mon arbre, je vivais heureux...");
assertEquals(7, mots.length);
assertEquals("Auprès", mots[0]);
assertEquals("de", mots[1]);
assertEquals("mon", mots[2]);
assertEquals("arbre", mots[3]);
assertEquals("je", mots[4]);
assertEquals("vivais", mots[5]);
assertEquals("heureux", mots[6]);
}
@Test
@Ignore
public void motsTordus() {
String[] mots
= frTokenizer.tokenize("Enlève donc l'abat-jour, on n'y voit goutte");
assertEquals(9, mots.length);
assertEquals("Enlève", mots[0]);
assertEquals("donc", mots[1]);
assertEquals("l", mots[2]);
assertEquals("abat-jour", mots[3]);
assertEquals("on", mots[4]);
assertEquals("n", mots[5]);
assertEquals("y", mots[6]);
assertEquals("voit", mots[7]);
assertEquals("goutte", mots[8]);
}
@Test
public void parentheses() {
String[] mots = frTokenizer.tokenize("(un ) { deux )( quatre /");
assertEquals(3, mots.length);
assertEquals("un", mots[0]);
assertEquals("deux", mots[1]);
assertEquals("quatre", mots[2]);
}
@Test
@Ignore
public void autresMotsTordus() {
String[] mots
= frTokenizer.tokenize("Petit papa, c'est aujourd'hui ta fête !");
assertEquals(7, mots.length);
assertEquals("Petit", mots[0]);
assertEquals("papa", mots[1]);
assertEquals("c", mots[2]);
assertEquals("est", mots[3]);
assertEquals("aujourd'hui", mots[4]);
assertEquals("ta", mots[5]);
assertEquals("fête", mots[6]);
}
}
Note : Les tests relatifs aux mots comportant des apostrophes ou des traits d'union ont été mis de côté pour l'instant. Ce point serait à traiter pour disposer d'un découpeur de mots digne de ce nom.
Résultat obtenu en séance :
/*
* Cours Java/POO INALCO 2012/2013
*/
package fr.crim.a2012.util;
/**
* Découpeur de mots du français
* Implémentation réalisée collectivement durant la séance du 30/01/2013
*/
public class FrenchTokenizer {
/**
* Transformation du texte passé en paramètre en liste de mots
* @param text le texte à analyser
* @return un tableau de mots contenu dans le texte analysé
*/
public String[] tokenize(String text) {
// Si la chaîne est vide, renvoyer un tableau vide
if (text.length() == 0) {
return new String[0];
}
// Prétraitement
// Supprimer espaces avant et après
text = text.trim();
// Découpage sur les espaces, tabulation, etc.
String[] mots = text.split("[\\s,\\.;:!\\?\\(\\){}/]+");
// Postraitement : suppression des mots vides
// On parcourt la liste des mots pour compter le nombre de mots
// non vides, i.e. de taille != 0
int nbMotsNonVides = 0;
for (String mot : mots) {
if (mot.length() != 0) {
nbMotsNonVides++;
}
}
// Si ce nombre est différent du nombre de mots initial
// => Il y a des mots vides
if (nbMotsNonVides != mots.length) {
// On crée alors un tableau à la bonne taille pour ne stocker
// que les mots non vides
String[] motsNonVides = new String[nbMotsNonVides];
int cp = 0;
for (String mot : mots) {
if (mot.length() != 0) {
motsNonVides[cp] = mot;
cp++;
}
}
// Et on le renvoie
return motsNonVides;
}
// Sinon, on renvoie le tableau de mots issu du split
return mots;
}
}
On déclare et on initialise 2 attributs supplémentaires (dicoP et dicoSaid) dans la classe SAX Fréquence :
private StringBuilder text = new StringBuilder();
private boolean dansP;
private boolean dansSaid;
private FrenchTokenizer frenchTokenizer = new FrenchTokenizer();
private Map<String,Integer> dicoP = new HashMap<String,Integer>();
private Map<String,Integer> dicoSaid = new HashMap<String,Integer>();
On met à jour la méthode endElement() pour ne plus afficher le texte des balises mais pour le passer à une méthode void traiteText(String txt) qui ajoute les mots issus du texte dans les dictionnaires de fréquence :
/**
* Méthode appelée sur une balise fermante
*/
@Override
public void endElement(String uri, String localName, String qName) {
if ("p".equals(localName)) {
traiteText(text.toString());
dansP = false;
}
if ("said".equals(localName)) {
traiteText(text.toString());
dansSaid = false;
}
}
/**
* Découpe le texte passé en paramètre pour ensuite ajouter les mots dans
* les dictionnaires dicoP et dicoSaid
* @param unTexte le texte à traiter
*/
private void traiteText(String unTexte) {
String[] mots = frenchTokenizer.tokenize(unTexte);
System.out.println(Arrays.toString(mots));
// Note : Il serait bon de trouver une écriture qui évite
// de dupliquer le code d'ajout dans une table de fréquence
// Remplissage de la table de fréquence dicoP
if (dansP) {
for (String mot : mots) {
if (dicoP.containsKey(mot)) {
dicoP.put(mot, dicoP.get(mot) + 1);
} else {
dicoP.put(mot, 1);
}
}
// ou de la table de fréquence dicoSaid
} else {
for (String mot : mots) {
if (dicoSaid.containsKey(mot)) {
dicoSaid.put(mot, dicoSaid.get(mot) + 1);
} else {
dicoSaid.put(mot, 1);
}
}
}
}
Afficher les 10 mots les plus fréquents au sein des balises <p> et les 10 mots les plus fréquents au sein des balises <said>
Cet affichage pourra s'effectuer dans la méthode public void endDocument() à ajouter à la classe fr.crim.a2012.saxigraph.SAXFrequence
Anonymous
Pour la prochaine séance (06/02/2013)
Modifier la classe fr.crim.a2012.saxigraph.SAXFrequence pour qu'elle affiche en fin d'analyse du fichier XML la liste des 10 mots les plus fréquents dans les balises <p> et la liste des 10 mots les plus fréquents dans les balises <said>
On présentera un mot par ligne, chaque mot sera suivi de sa fréquence. Les 10 mots seront présentés par fréquence décroissante, le mot le plus fréquent en premier.