Entrées-sorties 2
Transcription
Entrées-sorties 2
Plan de cette partie 2 Entrées-sorties 2 Université de Nice - Sophia Antipolis Version 2.1.4 – 8/7/13 Richard Grin Expressions régulières Mise en forme des sorties Scanner des entrées Imprimer Isoler les entrées-sorties Analyse lexicale Clavier – écran Paquetage java.nio Classe File R. Grin Java : entrées-sorties 2 Généralités Expressions régulières R. Grin Java : entrées-sorties 3 JDK 1.4 a ajouté des classes pour traiter les expressions régulières « à la Perl 5.0 » La classe String a aussi été complétée par des nouvelles méthodes de traitement des expressions régulières On peut ainsi facilement rechercher des chaînes de caractères correspondant à un certain modèle, décomposer une chaîne en sous-chaînes, en extraire des sous-chaînes ou en modifier une partie R. Grin La syntaxe est complexe mais on peut se restreindre à n’utiliser que les formes les plus simples, bien connues si on travaille avec Perl ou Unix Consulter la documentation de la classe java.util.regex.Pattern R. Grin Java : entrées-sorties 5 4 Un caractère Syntaxe des expressions régulières Java : entrées-sorties . : n’importe quel caractère, sauf une fin de ligne si on n’est pas en mode DOTALL (voir méthode compile de la classe Pattern \t, \n, \r, \f (form feed), \e (escape) ont leur signification habituelle \cx est le caractère Ctrl x \uhhhh : caractère unicode dont le code hexadécimal est hhhh R. Grin Java : entrées-sorties 6 1 « Échapper » un caractère Un caractère [abc] : a, b ou c [^abc] : ni a, ni b, ni c [a-l] : de a à l ; [^a-l] : pas de a à l [a-z&&[def]] : d, e ou f (intersection) [a-z&&[^def]] : a à z, sauf d, e ou f [a-zA-Z] : union [a-z[A-Z]] : union R. Grin Java : entrées-sorties 7 \ : enlève la signification spéciale du caractère suivant pour une expression régulière Attention de ne pas oublier de doubler les « \ » dans le code Java : – par exemple, pour rechercher $ on doit donner la chaîne « \\$ » – pour rechercher « \ », on doit entrer la chaîne « \\\\ » ; \ est un caractère spécial ! – mais pour rechercher un guillemet, il faut entrer « \" » R. Grin \d : un chiffre ; \D : pas un chiffre \s : un espace « blanc » (espace, \t, \n, \f, \r) \S : pas un espace blanc \w : une lettre ou un chiffre \W : ni une lettre, ni un chiffre R. Grin Java : entrées-sorties 9 ^ : début (et début de ligne si mode MULTILINE, option de méthode compile étudiée plus loin) $ : fin (et fin de ligne si mode MULTILINE) \b : début ou fin de mot \B : pas un début ou fin de mot \A : le début du texte \z : la fin du texte \G : la fin de la précédente chaîne correspondant à l’expression régulière R. Grin Quantifieurs Java : entrées-sorties Java : entrées-sorties 10 Glouton ou pas ? Soit X un caractère ou un groupe X? : 0 ou 1 fois X (X est une expression régulière quelconque) X* : 0 ou plusieurs fois X X+ : 1 ou plusieurs fois X X{n} : n fois X ; X{n,} : au moins n fois X X{n,m} : n à m fois X R. Grin 8 « Bords » Types de caractères prédéfinis Java : entrées-sorties 11 Par défaut, la recherche d’une chaîne de caractères qui correspond à une expression régulière englobe le plus de caractères possible, tant qu’une chaîne correspond à l’expression régulière Si on veut que le moins de caractères possible soient englobés, il faut ajouter ? derrière un quantifieur R. Grin Java : entrées-sorties 12 2 Exemple Super-glouton Si on cherche dans la chaîne Abcdec, Les expressions règulières "A.*c" et "A.+c" correspondent à Abcdec Les expressions règulières "A.*?c" et "A.+?c" correspondent à Abc R. Grin Java : entrées-sorties 13 R. Grin Si X et Y sont des expressions régulières, XY : X suivi de Y X|Y : X ou Y (X) : X, groupe « capturé » \n : le nième groupe capturé R. Grin Java : entrées-sorties 15 Attention, si l’expression régulière est de la forme X | Y, on ne peut travailler avec les groupes de Y R. Grin Java : entrées-sorties Java : entrées-sorties 16 Autres possibilités (2) Si vous ne trouvez pas votre bonheur dans la syntaxe qui vient d’être exposée, allez voir d’autres possibilités dans la documentation de la classe Pattern Quelques exemples : [a-z&&[^bc]] : de a à z, sauf b et c (&& indique une intersection) \p{Alpha} une lettre \p{Blank} un espace ou une tabulation R. Grin 14 Les groupes sont numérotés selon l’occurrence de la parenthèse ouvrante, en commençant par 1 pour la parenthèse la plus à gauche Le groupe spécial de numéro 0 correspond à toute la chaîne qui correspond à l’expression régulière Autres possibilités (1) Java : entrées-sorties Groupes Opérateurs Un 3ème mode (+ ajouté derrière le quantifieur) mange le plus possible de caractères, même si ça fait ignorer une chaîne qui pourrait correspondre à l’expression régulière Exemple : avec la chaîne Abdec les expressions règulières "A.*+c" et "A.++c" ne correspondent à rien car le caractère c final est « mangé » par .*+ ou .++ 17 D’autres exemples : \d+(?!€) : repère des chiffres qui ne sont pas suivis par le caractère « € » (negative lookahead) (?<!\d+)€ : repère le caractère « € » qui n’est pas précédé par des chiffres (negative lookbehind) (?:X) : pour regrouper, par exemple pour changer la priorité des opérateurs, sans créer un groupe R. Grin Java : entrées-sorties 18 3 Méthodes de String (1) Exemple boolean matches(String expreg) String phrase = "Bonjour bonhomme veux-tu un bon bonbon ?"; String phraseModifiee = phrase.replaceAll("\\bbon(\\w+)", "mal$1"); System.out.println(phraseModifiee); renvoie vrai si la chaîne (en entier) correspond à l’expression régulière String replaceAll(String expreg, String remplacement) renvoie une chaîne dans laquelle les souschaînes qui correspondent à expreg sont remplacées par la chaîne remplacement ; remplacement peut comporter des $n pour désigner des portions parenthésées de expreg Variante : replaceFirst R. Grin Java : entrées-sorties 19 affichera Bonjour malhomme veux-tu un bon malbon ? Si on veut ne pas tenir compte des majusculesminuscules, il faut utiliser les classes dédiées Pattern et Matcher R. Grin Méthode split de String éclate la chaîne en un tableau de chaînes séparées par un délimiteur qui correspond à l’expression régulière 2 délimiteurs accolés délimitent une chaîne vide (pas la valeur null) Les valeurs vides finales sont ignorées Java : entrées-sorties 21 Un 2ème paramètre n de type int peut aussi servir à limiter à n - 1 le nombre de recherches des délimiteurs (le dernier élément contient la fin de la chaîne), si n > 0 Il peut faire prendre en compte les valeurs vides finales, si n est ≠ 0 n = 0 correspond au cas où il n’y a pas de 2ème paramètre Cette méthode rend obsolète l'utilisation de la classe StringTokenizer R. Grin Java : entrées-sorties Exemples (1) Décomposer en « mots » séparés par un seul caractère blanc (4 mots « c'est », « » (chaîne vide), « un », « test. ») : 2 espaces Décomposer en « mots » séparés par un ou plusieurs caractères blancs (3 mots « c'est », « un », « test. ») : String[] resultat = "c'est un test.".split("\\s+"); . . . R. Grin Java : entrées-sorties 22 Exemples (2) String[] resultat = "c'est un test.".split("\\s"); for (int i = 0; i < resultat.length; x++) System.out.println(resultat[i]); 20 Compléments sur split String[] split(String expreg) R. Grin Java : entrées-sorties Décomposer en « mots » séparés par un ou plusieurs caractères non lettre ou chiffre (4 mots « c », « est », « un », « test ») : String[] resultat = "c'est un test.".split("\\W+"); . . . Décomposer en « mots » séparés par un ou plusieurs caractères non lettre, non chiffre, et pas « ' » (3 mots « c’est », « un », « test ») : String[] resultat = "c'est un test.".split("[^a-zA-Z0-9']+"); . . . 23 R. Grin Java : entrées-sorties 24 4 Performances de String.split Exemples (3) Attention, avec un seul paramètre, split ignore une ou plusieurs valeurs vides finales : "toto,,machin,".split(","); renvoie un tableau de 3 valeurs (une chaîne vide entre toto et machin, mais pas à la fin) "toto,,machin,".split(",", -1); renvoie un tableau de 4 valeurs (une chaîne vide entre toto et machin, et à la fin) R. Grin Java : entrées-sorties 25 Si la méthode doit être utilisée de très nombreuses fois avec la même expression régulière et que les performances sont importantes, il est préférable d’utiliser la méthode split de la classe Pattern (l’expression régulière ne sera alors compilée qu’une seule fois) Même remarque pour les autres méthodes de String qui utilisent des expressions régulières R. Grin Classes dédiées Java : entrées-sorties 27 Si on veut effectuer plusieurs recherches ou remplacement avec une même expression régulière, il est plus performant d’utiliser directement ces classes Elles permettent aussi d’effectuer des traitements complexes, autres que des simples remplacements, à chaque occurrence d’une chaîne correspondant à une expression régulière R. Grin Correspond à une expression régulière « compilée », c’est-à-dire préparée pour que les futures recherches soient accélérées Si une expression régulière est utilisée plusieurs fois, on a intérêt à la compiler (donc à utiliser Pattern et pas directement les méthodes de String) La javadoc de cette classe décrit la syntaxe des expressions régulières R. Grin Java : entrées-sorties Java : entrées-sorties 28 Méthode compile Classe Pattern 26 Classes dédiées Ces méthodes de String sont des raccourcis qui utilisent les classes Pattern et Matcher (du paquetage java.util.regex) dédiées aux expressions régulières R. Grin Java : entrées-sorties 29 static Pattern compile(String exprReg) compile une expression régulière compile est surchargée ; un 2ème paramètre de type int joue sur les recherches futures de l’expression régulière (voir transparent suivant) R. Grin Java : entrées-sorties 30 5 Options de la méthode compile Options de la méthode compile Ce 2ème paramètre est un drapeau qui peut correspondre à plusieurs options (utiliser « | » entre 2 options pour « ajouter » les bits) Exemple : si on veut ne pas tenir compte des majuscules et considérer que « . » peut correspondre à une fin de ligne, on écrit : Pattern patternTR = Pattern.compile("truc(.*?)machin", Pattern.CASE_INSENSITIVE | Pattern.DOTALL); R. Grin Java : entrées-sorties CASE_INSENSITIVE : ne pas tenir compte des majuscules/minuscules (par défaut, seules les lettres du code ASCII sont prises en compte, donc pas les lettres accentuées) UNICODE_CASE : toutes les lettres du code unicode sont prises en compte pour l’option précédente DOTALL : « . » inclut les caractères de fin de ligne (utile quand une expression régulière peut correspondre à une chaîne de caractères sur plusieurs lignes) MULTILINE : ^ et $ correspondent à un début ou fin de ligne (ou un début ou fin d’entrée) 31 R. Grin Interface CharSequence Java : entrées-sorties 32 Méthodes de Pattern Les chaînes de caractères dans lesquelles on recherche une expression régulière sont du type CharSequence String[] split(CharSequence texte) éclate le texte en prenant le pattern comme délimiteur ; un 2ème paramètre permet de limiter le nombre de morceaux (voir javadoc) static boolean matches(String exprReg, CharSequence texte) permet de faire directement une recherche sans passer explicitement par la classe Matcher (idem Pattern.compile(exprReg) .matcher(texte).matches() ) R. Grin Java : entrées-sorties 33 R. Grin Matcher matcher(CharSequence texte) renvoie une instance de Matcher pour rechercher l’expression régulière dans texte (et éventuellement effectuer des remplacements) R. Grin Java : entrées-sorties 34 Classe Matcher Méthode matcher Java : entrées-sorties 35 Permet de rechercher des chaînes qui correspondent à une expression régulière On obtient une instance avec la méthode matcher de la classe Pattern en donnant le texte dans lequel se fera la recherche Les méthodes reset(CharSequence) et reset() réinitialisent un matcher pour effectuer une nouvelle recherche R. Grin Java : entrées-sorties 36 6 Méthodes de la classe Matcher Méthodes de la classe Matcher replaceAll , replaceFirst, appendReplacement et appendTail remplacent l’expression régulière par une chaîne de caractères lookingAt, find et matches renvoient true si une expression régulière a été trouvée Si l’expression a été trouvée, on peut en savoir plus avec les méthodes start, end et group R. Grin Java : entrées-sorties 37 boolean matches() renvoie vrai si le texte en boolean lookingAt() renvoie vrai si le début entier correspond à l’expression régulière du texte correspond à l’expression régulière boolean find() recherche la prochaine occurrence de l’expression régulière (utilisé le plus souvent dans une boucle) ; renvoie false s’il ne reste plus d’occurrence ; find(int debut) ne recherche qu’à partir du caractère numéro debut Java : entrées-sorties 39 R. Grin Java : entrées-sorties Java : entrées-sorties 40 Méthodes pour les groupes Une expression régulière peut contenir des portions entre parenthèses Par exemple, cette expression correspond aux sous-chaînes formées d’un mot qui commence par « bon » ; la portion entre parenthèses désigne le reste du mot : \bbon(\w*) Toutes les portions entre parenthèses sont appelées des groupes et sont numérotées par ordre d’apparition de leur parenthèse ouvrante dans l’expression régulière (le groupe 0 désigne toute la sous-chaîne) R. Grin 38 Quand une sous-chaîne qui correspond à l’expression régulière vient d’être trouvée (par lookingAt, find ou autre), on peut avoir des informations sur cette sous-chaîne String group() renvoie la sous-chaîne int start() donne le numéro du 1er caractère int end() donne le numéro du dernier caractère + 1 Groupes Java : entrées-sorties Informations sur la sous-chaîne R. Grin chaîne dans laquelle toutes les sous-chaînes qui correspondent au pattern sont remplacées par remplacement ; remplacement peut contenir des références à des sous-groupes du pattern ($n) La variante replaceFirst ne remplace que la 1ère occurrence du pattern R. Grin Méthodes de la classe Matcher String replaceAll(String remplacement) renvoie une nouvelle 41 String group(int n) renvoie la souschaîne qui correspond au nième groupe de l’occurrence qui vient d’être trouvée (group(0) est équivalent à group()) On obtient le numéro du caractère qui débute et termine (+ 1) chaque groupe avec les méthodes int start(int n) et int end(int n) R. Grin Java : entrées-sorties 42 7 Fin du code Exemple de code import java.util.regex.*; . . . String phrase = "Bonjour bonhomme veux-tu un bon bonbon ?"; String expReg = "\\bbon(\\w+)"; Pattern p = Pattern.compile( expReg, Pattern.CASE_INSENSITIVE); Matcher m = p.matcher(phrase); System.out.println(m.replaceAll("mal$1")); System.out.println(m.matches()); System.out.println(m.lookingAt()); System.out.println(m.find()); R. Grin Java : entrées-sorties // Affiche détails de la recherche while (m.find()) { System.out.print("Trouvé " + m.group() + " en position " + m.start()); System.out.println(" suffixe : " + m.group(1) + " en position " + m.start(1)); } 43 R. Grin maljour malhomme veux-tu un bon malbon ? false true true Trouvé Bonjour en position 0 ; suffixe : jour Trouvé bonhomme en position 8 ; suffixe : homme Trouvé bonbon en position 32 ; suffixe : bon Java : entrées-sorties 45 La méthode appendReplacement offre plus de souplesse que replaceAll : elle ajoute dans un StringBuffer, en effectuant les remplacements un à un On peut choisir ce que l’on fait entre chaque remplacement R. Grin Remplacement Matcher appendReplacement(StringBuffer nouvTexte, String remplace) : ajoute les StringBuffer appendTail(StringBuffer : ajoute les caractères qui restent après la dernière correspondance nouvTexte) R. Grin Java : entrées-sorties Java : entrées-sorties 46 Exemple de remplacement caractères jusqu’à la fin de la précédente correspondance (par find ou autre), son remplacement compris 44 Remplacement Affichage de l’exemple R. Grin Java : entrées-sorties 47 // Même phrase que l’exemple précédent, // avec la même expression régulière StringBuffer sb = new StringBuffer(); while (m.find()) { m.appendReplacement(sb, "mal$1"); System.out.println(sb); } // Ajoute ce qui suit la dernière occurrence m.appendTail(sb); System.out.println(sb); R. Grin Java : entrées-sorties 48 8 Affichage de l’exemple Réinitialisation Phrase du départ : Bonjour bonhomme veux-tu un bon bonbon ? Pattern : \bbon(\w+) maljour maljour malhomme maljour malhomme veux-tu un bon malbon maljour malhomme veux-tu un bon malbon ? R. Grin Java : entrées-sorties 49 Ces 2 méthodes réinitialisent le matcher en mettant la position courante au début (cf. find et appendReplacement) Matcher reset() : remet la position courante au début du texte actuel Matcher reset(CharSequence texte) : change de texte et met la position courante au début R. Grin Interface MatchResult Paquetage java.util.regex Représente une des occurrences trouvées lors d’une recherche effectuée par un matcher ; elle a été introduite par le JDK 5 La méthode toMatchResult() de la classe Matcher renvoie un MatchResult que l’on peut ensuite interroger par les méthodes end, group, start Cet objet renvoyé conserve les informations, même après le déplacement du matcher à la prochaine occurrence R. Grin Java : entrées-sorties 51 Si le fichier n’est pas trop grand, on peut lire le fichier dans une String ou StringBuffer dans laquelle on effectue alors les recherches Si on peut travailler ligne par ligne, on peut aussi lire le fichier ligne par ligne et ensuite de rechercher dans chaque ligne Sinon, on peut d’abord transformer le fichier en un CharBuffer (du paquetage java.nio, qui implémente CharSequence) pour le passer à un pattern R. Grin Java : entrées-sorties 50 Exemple List<MatchResult> resultats = new ArrayList<MatchResult>(); Matcher m = pattern.matcher(texte); while(m.find()) resultats.add(m.toMatchResult()); // On peut ensuite traiter les occurrences // ou les passer à une autre méthode . . . R. Grin Java : entrées-sorties 52 Exemple de recherche dans un fichier Recherche dans un fichier Java : entrées-sorties 53 FileInputStream fis = . . .; FileChannel fc = fis.getChannel(); ByteBuffer bbuf = fc.map(FileChannel.MapMode.READ_ONLY, 0, (int)fc.size()); CharBuffer cbuf = Charset.forName("8859_1").newDecoder(). decode(bbuf); Pattern pattern = Pattern.compile("pat"); Matcher matcher = pattern.matcher(cbuf); while (matcher.find()) String match = matcher.group(); R. Grin Java : entrées-sorties 54 9 Utilisation de type envoi de mailing Avec les expressions régulières on peut créer un document qui contient des modèles particuliers, par exemple <#nom#> qui seront remplacés par des valeurs correspondants au modèle (par exemple, la valeur de la variable « nom ») StringTemplate est un projet open source qui a le même type de fonctionnalité (publipostage) R. Grin Java : entrées-sorties 55 Java : entrées-sorties 57 R. Grin Java : entrées-sorties 56 En Java, pas de méthode printf (comme dans le langage C) avant le JDK 5 Sans entrer dans les détails, en particulier en ce qui concerne l’internationalisation, voici quelques exemples qui présentent des classes et méthodes utiles pour mettre en forme des sorties R. Grin Java : entrées-sorties 58 Mise en forme des nombres affichera (locale courante French) 1 234 567,256 1 234 567,2 1 234 567 1,234,567.2 SimpleDateFormat Java : entrées-sorties Projet issu du projet open source ANTLR, qui permet de générer des textes à partir d’un modèle qui contient des « trous » à combler avec des données Les trous à combler peuvent comporter des « if », ce qui permet, par exemple, d’insérer dans le document « Monsieur » ou « Madame » suivant le sexe d’une personne import java.text.NumberFormat; import java.util.Locale; Locale en cours . . . NumberFormat nf = NumberFormat.getInstance(); System.out.println(nf.format(1234567.256)); System.out.println(nf.format(1234567.2)); System.out.println(nf.format(1234567)); nf = NumberFormat.getInstance(Locale.US); System.out.println(nf.format(1234567.2)); Pour mettre en forme les nombres et dates La classe NumberFormat permet de mettre en forme des nombres, des valeurs monétaires et des pourcentages Pour les dates, DateFormat Ces 2 classes suffisent pour les formats les plus courants ; sinon, on peut utiliser leur classe fille DecimalFormat ou R. Grin Paquetage java.text Mise en forme des sorties R. Grin StringTemplate 59 R. Grin Java : entrées-sorties 60 10 Autres méthodes de NumberFormat Donner le nombre minimum ou maximum de la portion du nombre située avant le séparateur décimal : setMinimumIntegerDigits(int n) setMaximumIntegerDigits(int n) affichera (locale courante French) Idem pour la partie décimale : 01 234 567,26 01 234 567,20 1 234 567,2 1 234 567 setMinimumFractionDigits(int n) setMaximumFractionDigits(int n) R. Grin Java : entrées-sorties Mise en forme spéciale de nombres import java.text.*; . . . NumberFormat nf = new DecimalFormat("00,000,000.00"); System.out.println(nf.format(1234567.256)); System.out.println(nf.format(1234567.2)); nf = new DecimalFormat("##,000,000.##"); System.out.println(nf.format(1234567.2)); System.out.println(nf.format(1234567)); 61 R. Grin Mise en forme des dates Les séparateurs de milliers et décimales sont indépendants de la locale en cours Java : entrées-sorties 62 Mise en forme spéciale de dates Date maintenant = new Date(); DateFormat df = DateFormat.getDateInstance(); System.out.println(df.format(maintenant)); df = DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.US); System.out.println(df.format(maintenant)); affichera 16 oct. 01 Oct 16, 2001 SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd G 'à' " + "hh:mm:ss a zzz"); Date maintenant = new Date(); System.out.println(sdf.format(maintenant)); sdf = new SimpleDateFormat("dd/MM/yyyy 'à' " + "HH:mm:ss"); System.out.println(sdf.format(maintenant)); Attention aux majuscules/ minuscules affichera 2000.08.07 ap. J.-C. à 02:15:20 PM CEST 07/08/2000 à 14:15:20 R. Grin Java : entrées-sorties 63 R. Grin NumberFormat permet aussi de mettre en forme des valeurs monétaires et des pourcentages avec les méthodes getCurrencyInstance et Java : entrées-sorties Les classes suivantes (du paquetage java.text) permettent d’aller plus loin pour formater des messages : – ChoiceFormat pour formater différemment un message selon la valeur d’un nombre – MessageFormat pour définir des messages avec du texte fixe et des parties variables getPercentInstance R. Grin 64 Encore plus pour formater… Autres types de mise en forme Java : entrées-sorties 65 Le plus souvent ces classes sont utilisées avec des fichiers de ressources pour internationaliser les applications R. Grin Java : entrées-sorties 66 11 Exemple avec MessageFormat Exemple avec Choice double[] limites = {0,1,2,10}; String[] chaines = {"Pas de fichier", "Un fichier", "des fichiers", "beaucoup de fichiers"}; ChoiceFormat choice = new ChoiceFormat(limites, chaines); for (int i = -1; i < 14; i++) { System.out.println(i + " : " + choice.format(i)); } R. Grin Java : entrées-sorties 67 R. Grin Java : entrées-sorties 68 Méthodes format Nouveautés du JDK 5 double[] limites = {0,2}; String[] chaines = {"fichier", "fichiers"}; ChoiceFormat choice = new ChoiceFormat(limites, chaines); MessageFormat mf = new MessageFormat( "On a trouvé {0,number,integer} {1}"); for (int i = 0; i < 5; i++) { String choix = choice.format(i); Object[] arguments = new Object[] {new Integer(i), choix}; System.out.println(mf.format(arguments)); } Des nouvelles méthodes format ont été ajoutées aux classes d’écriture dans un flot et à la classe String pour faciliter la mise en forme « à la printf de C » Les classes PrintStream et PrintWriter contiennent des méthodes format qui permettent de mettre en forme ce que l’on envoie dans le flot de sortie Types des paramètres : Locale l, String format, Object... args Une variante prend la locale par défaut : String format, Object... args R. Grin Java : entrées-sorties 69 Les méthodes renvoient l’instance de la classe (this ; on peut ainsi enchaîner) R. Grin La classe String contient elle aussi 2 méthodes static format qui renvoient une String (elles ont les mêmes types de paramètres que les méthodes de PrintStream et PrintWriter) Elles permettent de récupérer une chaîne de caractères mise en forme R. Grin Java : entrées-sorties 70 Méthodes printf String.format Java : entrées-sorties 71 Pour ne pas décontenancer les programmeurs C, les classes PrintStream et PrintWriter ont 2 méthodes printf qui font exactement la même chose que les méthodes format R. Grin Java : entrées-sorties 72 12 Syntaxe des formats Exemples simples %[n$][option][larg][.précision]type n : numéro ordre ; 3$ se réfère au 3ème paramètre option : par exemple, « - » pour justifier à gauche, « , » le nombre comportera des séparateurs de milliers, « 0 » pour imposer 0 au début (larg doit être donnée) larg : largeur minimale de la zone réservée précision : nombre de chiffres après la virgule pour les nombres décimaux, largeur maximale pour les autres types type : le type du paramètre ; quelques types : s pour String, d pour un entier décimal, f pour un réel System.err.printf( "Unable to open file '%1$s': %2$s", fileName, exception.getMessage()); System.err.printf( "Unable to open file '%s': %s", fileName, exception.getMessage()); R. Grin Java : entrées-sorties 73 R. Grin 74 Mise en forme de dates Passage à la ligne Java : entrées-sorties %n (la lettre n) fait passer à la ligne Tient compte de la plateforme sur laquelle on travaille On peut aussi mettre en forme des dates et des heures ou des BigDecimal Les dates et les heures sont adaptées à la locale choisie (ou à la locale par défaut) L’exemple suivant montre aussi l’utilité du numéro d’ordre ($1) Calendar c = ...; String s = String.format( "Anniversaire : %1$te %1$tB %1$tY", c); R. Grin Java : entrées-sorties 75 Seule une petite partie des possibilités a été donnée dans ce cours On peut trouver la syntaxe complète dans la javadoc de la classe java.util.Formatter R. Grin Java : entrées-sorties Java : entrées-sorties 76 Classe java.util.Formatter Syntaxe complète des formats R. Grin 77 En fait les méthodes format que l’on a vues utilisent la classe Formatter On passe au constructeur un paramètre de type Appendable ou String (pour un nom de fichier) qui représente la destination de ce qui sera mis en forme La méthode format envoie une représentation formatée des paramètres vers la destination donnée dans le constructeur R. Grin Java : entrées-sorties 78 13 Classe java.util.Scanner Scanner des entrées R. Grin Java : entrées-sorties 79 Depuis le JDK 5 la classe Scanner permet de récupérer des données au milieu d’un flot d’entrée, à l’image de la fonction scanf du langage C Elle implémente iterator<String> R. Grin On crée une instance de Scanner avec un de ses nombreux constructeurs On lui passe en paramètre le flot d’entrée dans lequel on va extraire les données ; par exemple, pour lire l’entrée standard : Scanner sc = new Scanner(System.in); 81 (source de données de type octet) ou InputStream, ou File, ou String Pour InputStream et File, on peut indiquer le nom d’un codage pour les caractères (voir classe java.nio.Charset ; en France c’est par défaut ISO-8859-1) int i = sc.nextInt(); Java : entrées-sorties Ils peuvent prendre en paramètre une instance de java.lang.Readable (source de données de type caractère), java.nio.channels.ReadableByteChannel On extrait ensuite les données une à une avec une des méthodes nextXXX : R. Grin R. Grin Délimiteurs Java : entrées-sorties 82 Localisation Par défaut les données sont séparées par des espaces, tabulations, passages à la ligne,… (ce qui renvoie true avec la méthode Character.isWhitespace) 80 Constructeurs Utilisation Java : entrées-sorties On peut en indiquer d’autres avec la méthode useDelimiter qui prend en paramètre une expression régulière : Une instance de Scanner utilise la locale par défaut On peut modifier la locale avec la méthode useLocale sc.useDelimiter("\\s*truc\\s*"); R. Grin Java : entrées-sorties 83 R. Grin Java : entrées-sorties 84 14 Méthodes nextXXX Méthodes nextXXX On trouve une méthode next par type primitif : nextInt, nextDouble,… et aussi nextBigDecimal, nextBigInteger Pour les entiers, on peut passer en paramètre la base pour les interpréter (10 par défaut) Ces méthodes renvoient une exception java.util.InputMismatchException (non contrôlée) si le type de l’élément suivant ne correspond pas au type attendu R. Grin Java : entrées-sorties 85 next() retourne une String qui contient la prochaine donnée placée entre 2 délimiteurs nextLine() avance jusqu’à la prochaine ligne et retourne ce qui a été sauté R. Grin Une méthode hasNextXXX par méthode nextXXX : hasNextInt, hasNextDouble (sauf pour nextLine) Retourne true si la prochaine donnée correspond au type que l’on cherche Ces méthodes nextXXX et hasNextXXX peuvent bloquer en attente du flot d’entrée R. Grin Java : entrées-sorties 86 Autres méthodes Méthodes hasNextXXX Java : entrées-sorties 87 findInline prend une expression régulière en paramètre et renvoie la prochaine occurrence dans la ligne de cette expression régulière (ne tient pas compte des délimiteurs) findWithinHorizon fait de même mais dans les n prochains caractères (n passé en paramètre) ; 0 correspond à un horizon à l’infini R. Grin Java : entrées-sorties 88 IOException Exemple Scanner s = new Scanner(ligne); s.useDelimiter("\\s*;\\s*"); nom = s.next(); age = s.nextInt(); if (s.hasNextInt()) { option = s.nextInt(); } Il n’est pas obligé de gérer les IOException car les méthodes de Scanner ne lèvent pas cette exception Si une IOException survient dans le flot sous-jacent, le scanner suppose qu’il a atteint la fin du flot On peut retrouver l’exception avec la méthode ioException() de la classe Scanner R. Grin Java : entrées-sorties 89 R. Grin Java : entrées-sorties 90 15 Interfaces Imprimer Le paquetage java.awt.print contient 2 interfaces pour représenter ce que l’on veut imprimer : – Printable correspond à un objet qui peut s’imprimer d’une façon simple – Pageable qui correspond à des impressions plus complexes, avec plusieurs formats d’impression (qui correspondent à plusieurs Printable) R. Grin Java : entrées-sorties 91 R. Grin Tâche d’impression On commence par obtenir une tâche d’impression de la classe PrinterJob (du paquetage java.awt.print) avec la méthode – soit une instance de Printable (avec setPrintable) – soit une instance de Pageable (avec setPageable) Java : entrées-sorties Il comporte une seule méthode public int print( Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException On passe ensuite à cette tâche un objet qui représente ce que l’on veut imprimer : R. Grin 93 Et les 2 constantes (de type int) PAGE_EXISTS et NO_SUCH_PAGE qui peuvent être renvoyées par la méthode print R. Grin Paramètres : On remarque que, s’il y a plusieurs pages, on doit pouvoir les écrire dans n’importe quel ordre en donnant leur numéro, ce qui n’est pas toujours facile R. Grin Java : entrées-sorties 94 class HelloImprimable implements Printable { public int print(Graphics g, PageFormat pf, int pageIndex) throws PrinterException { if (pageIndex != 0) return NO_SUCH_PAGE; Graphics2D g2 = (Graphics2 D)g; g2.setFont(new Font("serif", Font.PLAIN, 12)); g2.drawString("Hello World", 100, 100); return PAGE_EXISTS; } } – contexte graphique pour rendre la page à imprimer (peut être casté en Graphics2D) – format de la page d’impression qui décrit la taille et l’orientation de la page – numéro de la page à imprimer (0 pour la 1ère page) Java : entrées-sorties Exemple d’objet imprimable Méthode print 92 Interface Printable public static PrinterJob getPrinterJob() Java : entrées-sorties 95 R. Grin Java : entrées-sorties 96 16 Exemple de code pour imprimer Zone imprimable PrinterJob tache = PrinterJob.getPrinterJob(); tache.setPrintable(objetAImprimer); try { doit implémenter tache.print(); l’interface } Printable catch(PrinterException e) { . . . } Quand on écrit la méthode print, il faut souvent translater le Graphics pour tenir compte de la zone imprimable de la page : Graphics2D g2d = (Graphics2D)g; g2d.translate(pf.getImageableX(), pf.getImageableY()); R. Grin Java : entrées-sorties 97 R. Grin La tâche d’impression imprime sur l’imprimante par défaut On peut faire afficher une fenêtre de dialogue qui permet à l’utilisateur d’indiquer une autre imprimante et d’autres informations comme les pages à imprimer renvoie et le nombre de copies : if (tache.printDialog()) tache.print(); R. Grin 98 Choix du format de page Choix de l’imprimante Java : entrées-sorties false si l’utilisateur a annulé Java : entrées-sorties La tâche d’impression peut donner le format de page par défaut : PageFormat pf = tache.defaultPage(); On peut modifier le format de page par défaut – par programmation : pf.setOrientation(PageFormat.LANDSCAPE); – en affichant une fenêtre de dialogue : pf = tache.pageDialog(pf); Pour utiliser un format de page, il suffit de le passer à la méthode setPrintable : tache.setPrintable(ObjetAImprimer, pf); 99 R. Grin Exemple de code Java : entrées-sorties 100 Interface Pageable PrinterJob tache = PrinterJob.getPrinterJob(); tache.setPrintable(objetAImprimer); PageFormat pf = tache.pageDialog(printJob.defaultPage()); if (tache.printDialog()) { Seulement si on try { veut changer les tache.print(); dimensions de la page } Elle possède 3 méthodes : – int getNumberOfPages() – Printable getPrintable(int numeroPage) – PageFormat getPageFormat(int numeroPage) catch(PrinterException e) { . . . } } R. Grin Java : entrées-sorties 101 R. Grin Java : entrées-sorties 102 17 Exemple schématique avec Book Classe Book Elle implémente l’interface Pageable Outre les méthodes de Pageable, elle contient 3 méthodes pour ajouter ou remplacer des pages : – void append(Printable p, PageFormat pf) : ajoute une page (à la fin) – void append(Printable p , PageFormat pf, int n) : ajoute n pages (à la fin) – void setPage(int n, Printable p, PageFormat pf) : remplace la page numéro n R. Grin Java : entrées-sorties 103 Book livre = new Book(); // Ajoute 1 page en paysage et 10 en portrait PageFormat paysage = tache.defaultPage(); paysage.setOrientation(PageFormat.LANDSCAPE); livre.append(new Page1(), paysage); PageFormat portrait = tache.defaultPage(); paysage.setOrientation(PageFormat.PORTRAIT); livre.append(new Page2(), portrait, 10); // tache.setPageable(livre); R. Grin Conclusion Java : entrées-sorties 105 Java : entrées-sorties Java : entrées-sorties 106 APIs open source et gratuites JasperReports permet de générer des rapports sophistiqués de formats divers (PDF, HTML, XML, RTF,…) en prenant les données dans divers sources (base de données relationnelle, XML,…) ; iReport est un outil wysiwyg pour générer des fichiers de description de rapports utilisés ensuite par JasperReports R. Grin Des APIs non standard sont disponibles sur le Web pour faciliter l’impression de rapports complexes, directement ou en passant par l’exportation en formats divers (le plus souvent en PDF) Ces APIs sont fournies par des projets open source ou par des produits commerciaux Quelques exemples sont donnés dans les transparents suivants R. Grin APIs open source et gratuites 104 APIs non standard L’API standard n’offre que des fonctionnalités de bas niveau Le programmeur doit donc ajouter beaucoup de code s’il veut imprimer un document de plusieurs pages, avec des formats de pages non simplistes R. Grin Java : entrées-sorties 107 iText est une API qui permet de générer des documents PDF ou RTF (Word) Apache POI permet de manipuler en lecture et écriture les fichiers associés à la suite Office de Microsoft (fichiers Word ou Excel en particulier) R. Grin Java : entrées-sorties 108 18 Autres APIs Il est aussi possible d’utiliser XSL-FO (du monde XML) Crystal Reports est un produit commercial payant (et cher !) JPedal est un produit commercial qui offre une version gratuite pour les organisations à but non lucratif Différentes librairies open source à l’adresse Isoler les entrées-sorties http://java-source.net/open-source/pdf-libraries R. Grin Java : entrées-sorties 109 R. Grin Les entrées-sorties ont souvent des API complexes Elles compliquent les tests De plus, il n’est pas rare de changer de façon d’accéder à des ressources ou données externes (fichiers, BD relationnelle, BD objet, XML,…) Il vaut donc mieux isoler les entrées-sorties du reste de l’application R. Grin Java : entrées-sorties 110 Implémentation Pourquoi ? Java : entrées-sorties 111 Il est bon d’encapsuler les accès aux données dans des classes à part de telle sorte que – le reste de l’application puisse accéder aux ressources plus facilement : interfaces simples et adaptées à l’application – les interfaces ne dépendent pas du type de persistance choisi – on puisse simuler des entrées-sorties pendant les tests (sans nécessairement disposer du support de persistance) R. Grin Java : entrées-sorties 112 Introduction Analyse lexicale R. Grin Java : entrées-sorties 113 Cette section étudie des classes pour décomposer du texte en lexèmes : – classe StringTokenizer – classe StreamTokenizer Autres possibilités pour effectuer la même tâche (étudiées plus loin dans cette partie du cours) : – méthode split de la classe String – classe Scanner, introduite par le JDK 5 R. Grin Java : entrées-sorties 114 19 Avertissement Séparateurs des données Rappel : les flots de caractères utilisent des séparateurs entre les données Les classes étudiées dans cette section facilitent l’extraction de données séparées par des séparateurs Scanner R. Grin Java : entrées-sorties 115 Analyse lexicale d’une chaîne Java : entrées-sorties Cependant la connaissance de cette classe peut être utile pour comprendre du code déjà écrit R. Grin Java : entrées-sorties 116 Analyse lexicale d’une chaîne import java.util.StringTokenizer; Pas java.io public class Decomposition { public static void main(String[] args) { String donnees = Que sera-t-il "Chiffres : 12,,689\n155"; affiché ? StringTokenizer st = new StringTokenizer(donnees, " ,\n"); while (st.hasMoreTokens()) { String token = st.nextToken(); Chiffres : System.out.println(token); 12 } 689 2 séparateurs accolés sont considérés } 155 comme 1 seul séparateur } R. Grin La classe StringTokenizer ne supporte pas l’utilisation d’expressions régulières et elle peut donc souvent être avantageusement remplacée par l’utilisation de méthode split de la classe String ou par la classe 117 Que sera-t-il . . . affiché ? String donnees = Chiffres "Chiffres : 12,,689\n155"; : StringTokenizer st = new StringTokenizer(donnees, " ,\n", 12 Les séparateurs sont true); , des lexèmes (tokens) while (st.hasMoreTokens()) { , 689 String token = st.nextToken(); System.out.println(token); } 155 R. Grin Java : entrées-sorties 118 Analyse lexicale d’un flot La classe java.io.StreamTokenizer possède un constructeur Entrées et sorties sur clavier - écran public StreamTokenizer(Reader r) qui permet de faire une analyse syntaxique du flot de caractères associé à r Cette classe offre plus de souplesse que StringTokenizer (surtout pour analyser des programmes Java ou C) mais est plus complexe ; elle ne sera pas étudiée dans ce cours R. Grin Java : entrées-sorties 119 R. Grin Java : entrées-sorties 120 20 Lecture caractère par caractère Entrées-sorties sur clavier-écran int n; char car; String s = ""; try { while (true) { int n = System.in.read(); if (n == -1) break; car = (char)n; s += car; Cast pour avoir } un char } catch(IOException e) { System.err.println("Erreur I/O" + e); } R. Grin Java : entrées-sorties BufferedReader br = new BufferedReader( new InputStreamReader(System.in)); System.out.print("Entrez votre nom : "); String nom = br.readLine(); System.out.print("Entrez votre âge : "); String age = br.readLine(); System.out.println(nom + " a " + Integer.parseInt(age) + " ans."); 121 R. Grin Que signifie « System.out.println() » ? System est une classe du paquetage java.lang Elle est final et non instanciable. Elle comprend (entre autres) : – 3 variables de classe : in, out, err pour les voies Déclaration de la variable out : public static PrintStream out; println() est une méthode de la classe PrintStream Depuis le JDK 5, le plus simple est d’utiliser la classe Scanner (étudiée avant dans ce support) car on n’a pas besoin de gérer les IOException R. Grin Java : entrées-sorties 123 standard d’entrée, sortie et de messages d’erreurs R. Grin On peut rediriger les voies standard des entrées, sorties et erreurs par les méthodes setIn(InputStream), setOut(PrintStream) et setErr(PrintStream) de la classe System R. Grin Java : entrées-sorties 125 Java : entrées-sorties 124 Console A propos de System.in, out et err 122 Petite colle ! Entrées-sorties sur clavier-écran Scanner sc = new Scanner(System.in); System.out.printf("%-20s", "Votre nom : "); String nom = sc.nextLine(); System.out.printf("%-20s", "Et votre âge : "); int age = sc.nextInt(); System.out.printf("%s a %d ans", nom, age); Java : entrées-sorties Depuis le JDK 6 la classe java.io.Console permet de lire et d’écrire des caractères sur la console (clavier – écran) En particulier elle permet de saisir un mot de passe tapé sur le clavier sans qu’il ne s’affiche sur l’écran R. Grin Java : entrées-sorties 126 21 Exemple Console console = System.console(); if (console = = null) return; console.printf("%s", "Mot de passe ? "); char[] mdp = console.readPassword(); String mdps = new String(mdp); console.printf("%s, %s", nom, mdps); R. Grin Java : entrées-sorties Nouveau paquetage java.nio 127 R. Grin Ce paquetage reprend toute l’architecture des classes pour les entrées/sorties Il utilise la notion de canal (channel) et de buffer Il utilise les possibilités avancées du système d’exploitation hôte pour optimiser les entrées-sorties et offrir plus de fonctionnalités R. Grin Java : entrées-sorties 128 Utilisation Présentation Java : entrées-sorties 129 Il a été écrit pour – permettre des entrées-sorties non bloquantes – améliorer les performances, en particulier pour les serveurs fortement chargés – bloquer des portions de fichiers Il est un peu plus complexe à utiliser et pas nécessaire si ces fonctionnalités ne sont pas recherchées Il n’est pas étudié dans ce cours R. Grin Java : entrées-sorties 130 Avertissement Classe File R. Grin Java : entrées-sorties 131 Il vaut mieux utiliser la nouvelle API NIO.2 du JDK 7 (étudiée au début de ce support) que la classe File La méthode toPath() de la classe File renvoie une instance de Path ; elle peut être utile pour faire le pont avec NIO.2 Cette section vous aidera à comprendre les nombreuses lignes de code écrites avec les versions précédentes du JDK R. Grin Java : entrées-sorties 132 22 Classe File Cette classe représente un chemin de fichier, indépendamment du système d’exploitation Un fichier est repéré par un chemin abstrait composé d’un préfixe optionnel (nom d’un disque par exemple) et de noms (noms des Constructeurs Les chemins passés en premier paramètre peuvent être des noms relatifs ou absolus File(String chemin) File(String cheminParent, String chemin) File(File parent, String chemin) répertoires parents et du fichier lui-même) Attention, File fichier = new File("/bidule/truc"); ne lève aucune exception si /bidule/truc n’existe pas dans le système de fichier R. Grin Java : entrées-sorties 133 R. Grin Portabilité pour les noms de fichiers La classe File offre des facilités pour la portabilité des noms de fichiers Le caractère séparateur pour le nom d’un fichier est donné par les constantes File.separator (de type String) ou Le caractère pour séparer des chemins (par exemple pour classpath ou path) est donné par File.pathSeparator et 134 Noms relatifs File.separatorChar (de type char) (« / » pour Unix, « \ » pour Windows) Java : entrées-sorties Les noms relatifs sont relatifs au répertoire courant (propriété système user.dir) Le répertoire courant est généralement le répertoire dans lequel la JVM a été lancée (commande java), mais est difficile à maîtriser dans les environnements complexes d’exécution (Java EE par exemple) Un problème donc ! File.pathSeparatorChar R. Grin Java : entrées-sorties 135 est à éviter ! Malgré les facilités pour la portabilité offertes par la classe File, l’utilisation de noms de fichiers « en dur » rend souvent un programme plus difficile à réutiliser En effet, donner des noms absolus, et même relatifs, nuit à la souplesse comme on le verra dans la section suivante « Noms de fichiers » de ce supports de cours R. Grin Java : entrées-sorties Java : entrées-sorties 136 Fonctionnalités de la classe File new File(String) R. Grin 137 Elle permet d’effectuer des manipulations sur les fichiers et répertoires considérés comme un tout (mais pas de lire ou d’écrire le contenu) : – lister un répertoire – supprimer, renommer un fichier – créer un répertoire – créer un fichier temporaire – connaître et positionner les droits que l’on a sur un fichier (lecture, écriture) (depuis JDK 6) – etc. R. Grin Java : entrées-sorties 138 23 Méthodes informatives Méthodes pour des actions boolean exists() boolean isDirectory() boolean isFile() boolean canRead() boolean canWrite() boolean canExecute() long lastModified() long length() réussie) Java : entrées-sorties boolean renameTo(File nouveau) (true si suppression réussie) boolean mkdir() boolean mkdirs() (peut créer des répertoires intermédiaires) R. Grin boolean delete() (true si suppression 139 boolean setLastModified(long temps) boolean setReadOnly() R. Grin Méthodes pour des actions String getName() (nom terminal) String getPath() (chemin absolu ou relatif) répertoire pour les fichiers temporaires, avec un nom unique void deleteOnExit() le fichier qui reçoit ce message sera supprimé à la fin de l’exécution Java : entrées-sorties File getAbsoluteFile() String getAbsolutePath() String getParent() File getParentFile() URL toURL() (de la forme file:url) 141 R. Grin Fichiers d’un répertoire 140 Méthodes pour les noms static File createTempFile(String prefixe, String suffixe) crée un fichier dans le R. Grin Java : entrées-sorties Java : entrées-sorties 142 Modifier les permissions Le résultat est null si this n’est pas un répertoire, et un tableau de taille 0 si le listing est vide String[] list() (noms terminaux) String[] list(FileNameFilter filtre) (seulement certains fichiers) Depuis le JDK 6, il est possible de positionner les permissions des fichiers avec les méthodes setWritable, setReadable, setExecutable File[] listFiles() (File avec des noms relatifs ou absolus, suivant this) ; variantes avec FileNameFilter et FileFilter R. Grin Java : entrées-sorties 143 R. Grin Java : entrées-sorties 144 24 Exemple d’utilisation de File public boolean isLisible(String nomFichier) { File fichier = new File(nomFichier); if (!fichier.isFile()) return false; return fichier.canRead(); Aucune exception } n’est levée si le fichier n’existe pas R. Grin Java : entrées-sorties 145 25