pdf1
Transcription
pdf1
Entrées-sorties 2 Université de Nice - Sophia Antipolis Version 2.1.4 – 8/7/13 Richard Grin Plan de cette partie 2 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 Expressions régulières R. Grin Java : entrées-sorties 3 Généralités 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 Java : entrées-sorties 4 Syntaxe des expressions régulières 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 Un caractère . : 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 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 « Échapper » un caractère \ : 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 Java : entrées-sorties 8 Types de caractères prédéfinis \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 « Bords » ^ : 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 Java : entrées-sorties 10 Quantifieurs 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 Java : entrées-sorties 11 Glouton ou pas ? 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 Exemple 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 Super-glouton 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 .++ R. Grin Java : entrées-sorties 14 Opérateurs Si X et Y sont des expressions régulières, XY : X suivi de Y X|Y : X ou Y R. Grin Java : entrées-sorties 15 Groupes (X) : X, groupe « capturé » \n : le nième groupe capturé 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 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 16 Autres possibilités (1) 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 Java : entrées-sorties 17 Autres possibilités (2) 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 Méthodes de String (1) boolean matches(String expreg) 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 Exemple String phrase = "Bonjour bonhomme veux-tu un bon bonbon ?"; String phraseModifiee = phrase.replaceAll("\\bbon(\\w+)", "mal$1"); System.out.println(phraseModifiee); 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 Java : entrées-sorties 20 Méthode split de String String[] split(String expreg) é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 R. Grin Java : entrées-sorties 21 Compléments sur split 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 22 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 String[] resultat = "c'est un test.".split("\\s"); for (int i = 0; i < resultat.length; x++) System.out.println(resultat[i]); 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 23 Exemples (2) 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']+"); . . . R. Grin Java : entrées-sorties 24 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 Performances de String.split 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 Java : entrées-sorties 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 27 Classes dédiées 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 Java : entrées-sorties 28 Classe Pattern 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 29 Méthode compile 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 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 31 Options de la méthode compile 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) R. Grin Java : entrées-sorties 32 Interface CharSequence Les chaînes de caractères dans lesquelles on recherche une expression régulière sont du type CharSequence R. Grin Java : entrées-sorties 33 Méthodes de Pattern 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 34 Méthode matcher 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 35 Classe Matcher 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 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 Méthodes de la classe Matcher String replaceAll(String remplacement) renvoie une nouvelle 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 Java : entrées-sorties 38 Méthodes de la classe Matcher boolean matches() renvoie vrai si le texte en entier correspond à l’expression régulière boolean lookingAt() renvoie vrai si le début 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 R. Grin Java : entrées-sorties 39 Informations sur la sous-chaîne 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 R. Grin Java : entrées-sorties 40 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 Java : entrées-sorties 41 Méthodes pour les groupes 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 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 43 Fin du code // 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)); } R. Grin Java : entrées-sorties 44 Affichage de l’exemple 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 R. Grin Java : entrées-sorties 45 Remplacement 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 Java : entrées-sorties 46 Remplacement Matcher appendReplacement(StringBuffer nouvTexte, String remplace) : ajoute les caractères jusqu’à la fin de la précédente correspondance (par find ou autre), son remplacement compris StringBuffer appendTail(StringBuffer nouvTexte) : ajoute les caractères qui restent après la dernière correspondance R. Grin Java : entrées-sorties 47 Exemple de remplacement // 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 Affichage de l’exemple 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 Réinitialisation 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 Java : entrées-sorties 50 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 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 Recherche dans un fichier 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 53 Exemple de recherche dans un fichier 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 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 StringTemplate 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 R. Grin Java : entrées-sorties 56 Mise en forme des sorties R. Grin Java : entrées-sorties 57 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 Paquetage java.text 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 SimpleDateFormat R. Grin Java : entrées-sorties 59 Mise en forme des nombres import java.text.NumberFormat; import java.util.Locale; Locale en . . . 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)); cours affichera (locale courante French) 1 234 567,256 1 234 567,2 1 234 567 1,234,567.2 R. Grin Java : entrées-sorties 60 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) Idem pour la partie décimale : setMinimumFractionDigits(int n) setMaximumFractionDigits(int n) R. Grin Java : entrées-sorties 61 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)); affichera (locale courante French) 01 234 567,26 01 234 567,20 1 234 567,2 1 234 567 R. Grin Les séparateurs de milliers et décimales sont indépendants de la locale en cours Java : entrées-sorties 62 Mise en forme des 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 R. Grin Java : entrées-sorties 63 Mise en forme spéciale de dates 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 64 Autres types de mise en forme NumberFormat permet aussi de mettre en forme des valeurs monétaires et des pourcentages avec les méthodes getCurrencyInstance et getPercentInstance R. Grin Java : entrées-sorties 65 Encore plus pour formater… 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 Le plus souvent ces classes sont utilisées avec des fichiers de ressources pour internationaliser les applications R. Grin Java : entrées-sorties 66 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 Exemple avec MessageFormat 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)); } R. Grin Java : entrées-sorties 68 Nouveautés du JDK 5 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 » R. Grin Java : entrées-sorties 69 Méthodes format 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 Les méthodes renvoient l’instance de la classe (this ; on peut ainsi enchaîner) R. Grin Java : entrées-sorties 70 String.format 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 71 Méthodes printf 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 Exemples simples 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 Syntaxe des formats %[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 R. Grin Java : entrées-sorties 74 Passage à la ligne %n (la lettre n) fait passer à la ligne Tient compte de la plateforme sur laquelle on travaille R. Grin Java : entrées-sorties 75 Mise en forme de dates 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 76 Syntaxe complète des formats 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 77 Classe java.util.Formatter 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 Scanner des entrées R. Grin Java : entrées-sorties 79 Classe java.util.Scanner 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 Java : entrées-sorties 80 Utilisation 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); On extrait ensuite les données une à une avec une des méthodes nextXXX : int i = sc.nextInt(); R. Grin Java : entrées-sorties 81 Constructeurs Ils peuvent prendre en paramètre une instance de java.lang.Readable (source de données de type caractère), java.nio.channels.ReadableByteChannel (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) R. Grin Java : entrées-sorties 82 Délimiteurs 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) On peut en indiquer d’autres avec la méthode useDelimiter qui prend en paramètre une expression régulière : sc.useDelimiter("\\s*truc\\s*"); R. Grin Java : entrées-sorties 83 Localisation Une instance de Scanner utilise la locale par défaut On peut modifier la locale avec la méthode useLocale R. Grin Java : entrées-sorties 84 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 Méthodes nextXXX 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 Java : entrées-sorties 86 Méthodes hasNextXXX 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 87 Autres méthodes 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 Exemple Scanner s = new Scanner(ligne); s.useDelimiter("\\s*;\\s*"); nom = s.next(); age = s.nextInt(); if (s.hasNextInt()) { option = s.nextInt(); } R. Grin Java : entrées-sorties 89 IOException 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 90 Imprimer R. Grin Java : entrées-sorties 91 Interfaces 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 92 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 public static PrinterJob getPrinterJob() On passe ensuite à cette tâche un objet qui représente ce que l’on veut imprimer : – soit une instance de Printable (avec setPrintable) – soit une instance de Pageable (avec setPageable) R. Grin Java : entrées-sorties 93 Interface Printable Il comporte une seule méthode public int print( Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException 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 Java : entrées-sorties 94 Méthode print Paramètres : – 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) 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 95 Exemple d’objet imprimable 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; } } R. Grin Java : entrées-sorties 96 Zone imprimable 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 Exemple de code pour imprimer PrinterJob tache = PrinterJob.getPrinterJob(); tache.setPrintable(objetAImprimer); try { doit implémenter tache.print(); l’interface } Printable catch(PrinterException e) { . . . } R. Grin Java : entrées-sorties 98 Choix de l’imprimante 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 Java : entrées-sorties false si l’utilisateur a annulé 99 Choix du format de page 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); R. Grin Java : entrées-sorties 100 Exemple de code 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 } catch(PrinterException e) { . . . } } R. Grin Java : entrées-sorties 101 Interface Pageable Elle possède 3 méthodes : – int getNumberOfPages() – Printable getPrintable(int numeroPage) – PageFormat getPageFormat(int numeroPage) R. Grin Java : entrées-sorties 102 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 Exemple schématique avec Book 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 Java : entrées-sorties 104 Conclusion 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 105 APIs non standard 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 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 Java : entrées-sorties 107 APIs open source et gratuites 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 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 http://java-source.net/open-source/pdf-libraries R. Grin Java : entrées-sorties 109 Isoler les entrées-sorties R. Grin Java : entrées-sorties 110 Pourquoi ? 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 111 Implémentation 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 Analyse lexicale R. Grin Java : entrées-sorties 113 Introduction 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 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 R. Grin Java : entrées-sorties 115 Avertissement 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 Scanner 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 Java : entrées-sorties 117 Analyse lexicale d’une chaîne 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 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 Entrées et sorties sur clavier - écran R. Grin Java : entrées-sorties 120 Lecture caractère par caractère 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 121 Entrées-sorties sur clavier-écran 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."); R. Grin Java : entrées-sorties 122 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); 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 Petite colle ! 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 standard d’entrée, sortie et de messages d’erreurs Déclaration de la variable out : public static PrintStream out; println() est une méthode de la classe PrintStream R. Grin Java : entrées-sorties 124 A propos de System.in, out et err 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 Console 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 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 127 Nouveau paquetage java.nio R. Grin Java : entrées-sorties 128 Présentation 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 129 Utilisation 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 Classe File R. Grin Java : entrées-sorties 131 Avertissement 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 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 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 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. Grin Java : entrées-sorties 134 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 File.separatorChar (de type char) (« / » pour Unix, « \ » pour Windows) Le caractère pour séparer des chemins (par exemple pour classpath ou path) est donné par File.pathSeparator et File.pathSeparatorChar R. Grin Java : entrées-sorties 135 Noms relatifs 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 ! R. Grin Java : entrées-sorties 136 new File(String) 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 137 Fonctionnalités de la classe File 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 Méthodes informatives boolean exists() boolean isDirectory() boolean isFile() boolean canRead() boolean canWrite() boolean canExecute() long lastModified() long length() R. Grin Java : entrées-sorties 139 Méthodes pour des actions boolean delete() (true si suppression réussie) boolean renameTo(File nouveau) (true si suppression réussie) boolean mkdir() boolean mkdirs() (peut créer des répertoires intermédiaires) boolean setLastModified(long temps) boolean setReadOnly() R. Grin Java : entrées-sorties 140 Méthodes pour des actions static File createTempFile(String prefixe, String suffixe) crée un fichier dans le 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 R. Grin Java : entrées-sorties 141 Méthodes pour les noms String getName() (nom terminal) String getPath() (chemin absolu ou relatif) File getAbsoluteFile() String getAbsolutePath() String getParent() File getParentFile() URL toURL() (de la forme file:url) R. Grin Java : entrées-sorties 142 Fichiers d’un répertoire 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) File[] listFiles() (File avec des noms relatifs ou absolus, suivant this) ; variantes avec FileNameFilter et FileFilter R. Grin Java : entrées-sorties 143 Modifier les permissions Depuis le JDK 6, il est possible de positionner les permissions des fichiers avec les méthodes setWritable, setReadable, setExecutable R. Grin Java : entrées-sorties 144 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