Java 101: les tenants et aboutissants des entrées / sorties standard

Dans les articles précédents de Java 101 , j'ai fait référence aux concepts de redirection, de périphérique d'entrée standard et de périphérique de sortie standard. Pour illustrer la saisie de données, plusieurs exemples appelés System.in.read(). Il s'avère que les System.in.read()entrées des données du périphérique d'entrée standard. Pour illustrer la sortie de données, des exemples appelés System.out.print()et System.out.println(). Contrairement à System.in.read(), ces méthodes - des séquences nommées de code exécutable (à explorer dans l'article du mois prochain) - envoient leur sortie au périphérique de sortie standard. Vous voulez en savoir plus sur les concepts d'E / S standard? Continuer à lire!

Les E / S standard sont un mécanisme d'entrée / sortie standardisé qui provient du système d'exploitation Unix. Bien que ce mécanisme soit principalement utilisé avec les anciens systèmes d'exploitation non-GUI, les E / S standard jouent toujours un rôle dans les systèmes d'exploitation GUI (interface utilisateur graphique) modernes, où les gens l'utilisent pour déboguer les programmes défectueux et pour enseigner l'entrée / la sortie dans l'entrée- cours de programmation de niveau.

Comme vous l'avez probablement deviné, les E / S standard utilisent des périphériques pour entrer et sortir des données. Ces périphériques incluent une entrée standard, une sortie standard et une erreur standard.

Entrée standard

Le périphérique d'entrée standard est la partie du système d'exploitation qui contrôle d'où un programme reçoit ses entrées. Par défaut, le périphérique d'entrée standard lit cette entrée à partir d'un pilote de périphérique connecté au clavier. Cependant, vous pouvez rediriger ou basculer la source d'entrée vers un pilote de périphérique attaché à un fichier de sorte que l'entrée semble provenir "comme par magie" d'un fichier - au lieu du clavier.

Un programme entre ses données à partir du périphérique d'entrée standard en appelant la System.in.read()méthode Java . Regardez dans la documentation du SDK et vous découvrirez une classe appelée System. Cette classe contient une variable appelée in- un objet créé à partir d'une sous-classe de InputStream. Le caractère de période après les Systemétats qui inappartient à Systemet le caractère de période après les inétats qui read()appartiennent à in. En d'autres termes, read()est une méthode qui appartient à un objet appelé in, qui à son tour appartient à une classe appelée System. (Je discuterai plus en détail des classes, des objets et de "l'appartenance à" le mois prochain.)

System.in.read()ne prend aucun argument et renvoie un entier, ce qui a amené certains à croire qu'il System.in.read()renvoie des nombres entiers entrés par l'utilisateur. Pour clarifier, System.in.read()renvoie le code ASCII 7 bits d'une touche (si le périphérique d'entrée standard est défini sur le clavier) ou un octet de 8 bits d'un fichier (si le périphérique d'entrée standard a été redirigé du clavier vers un fichier). Dans les deux cas, System.in.read()convertit le code en un entier 32 bits et renvoie le résultat.

Supposons que le périphérique d'entrée standard est défini sur le clavier. Voici une description de ce qui se passe sous Windows: Lorsque vous tapez une touche sur un clavier contrôlé par Windows, le système d'exploitation stocke le code ASCII 7 bits de cette touche dans un tampon de touches interne. Ce tampon de clé contient jusqu'à environ 16 codes ASCII et est organisé comme une structure de données de file d'attente circulaire premier entré / premier sorti. System.in.read()récupère le code ASCII de la tête du tampon de clé, puis supprime ce code du tampon de clé. Ce code ASCII 7 bits est ensuite converti en un int- en System.in.read()ajoutant au début 25 bits de zéro au code - et retourne à l'appelant de la méthode. Un deuxième System.in.read()appel de méthode récupère le code ASCII suivant, qui est maintenant en tête du tampon de clé, et ainsi de suite.

Supposons qu'il n'y ait pas de codes ASCII dans le tampon de clé. Ce qui se produit? System.in.read()attend que l'utilisateur saisisse les clés et appuie sur le terminateur. Sous Windows, ce terminateur est la Enterclé. Appuyer sur Enterfait que Windows stocke un code de retour chariot (ASCII 13) suivi d'un code de nouvelle ligne (ASCII 10) dans le tampon de touches. Par conséquent, le tampon de clé peut contenir plusieurs codes ASCII suivis d'un retour chariot et d'un caractère de nouvelle ligne. Le premier de ces codes revient de System.in.read(). Vérifiez cette activité en saisissant, en compilant et en exécutant l' Echoapplication; son code source apparaît dans le Listing 1.

Listing 1. Echo.java

// Classe Echo.java Echo {public static void main (String [] args) lance java.io.IOException {int ch; System.out.print ("Entrez du texte:"); while ((ch = System.in.read ())! = '\ n') System.out.print ((char) ch); }}

Echo effectue les étapes suivantes:

  1. Appelle la System.out.print()méthode, qui prend un Stringargument, pour afficher une invite
  2. Appels System.in.read()pour saisir des codes ASCII à partir du périphérique d'entrée standard sous forme d'entiers 32 bits
  3. Convertit ces entiers 32 bits en caractères Unicode 16 bits au moyen de la (char)conversion
  4. Appelle la System.out.print()méthode, qui prend un charargument, pour faire écho à ces caractères Unicode sur le périphérique de sortie standard

Les trois dernières étapes des quatre étapes précédentes se déroulent dans une boucle while et se poursuivent jusqu'à ce qu'un caractère de nouvelle ligne soit lu. Pour exécuter de Echomanière à entrées du clavier et des sorties à l'écran, exécutez la ligne de commande suivante: java Echo.

Bien que System.in.read()ne lève jamais d'exception (voir la rubrique de comptage de mots dans cet article pour une définition de ce terme), lorsque le périphérique d'entrée standard est défini sur le clavier, il peut lever une exception lorsque vous redirigez le périphérique d'entrée standard du clavier vers un fichier. Par exemple, supposons que vous redirigiez le périphérique d'entrée standard vers un fichier et que vous System.in.read()lisiez le contenu du fichier. Supposons maintenant que le fichier se trouve sur une disquette et que l'utilisateur éjecte ce disque pendant l'opération de lecture. Lorsque l'éjection a lieu, System.in.read()lève une exception, informant le programme qu'il ne peut pas lire le fichier. Cela fournit la raison de l'ajout de la throws java.io.IOExceptionclause à l'en- main()tête de la méthode. (Vous explorerez les exceptions, le lancement d'exceptions et les concepts associés dans un prochain article.)

Comment rediriger le périphérique d'entrée standard pour que l'entrée provienne d'un fichier? La réponse est d'introduire un signe inférieur à <, sur la ligne de commande et de suivre ce symbole avec un nom de fichier. Pour voir comment cela fonctionne, exécutez la ligne de commande suivante:java Echo . The command line redirects the standard input device to a file called Echo.java. When Echo runs, because each line ends in a new-line character, only the first line of text in Echo.java appears on the screen.

Suppose you need a utility program that reads an entire file and either displays the file's contents on the screen, copies those contents to another file, or copies those contents to a printer. Unfortunately, the Echo program only performs that task until it encounters the first new-line character. What do you do? The answer to the problem lies in the Type application. Listing 2 provides the source code:

Listing 2. Type.java

// Type.java class Type { public static void main (String [] args) throws java.io.IOException { int ch; while ((ch = System.in.read ()) != -1) System.out.print ((char) ch); } } 

Type resembles Echo, however, there is no prompt, and the while loop tests against -1 (which indicates end of file) instead of \n (which indicates end of line). To run Type, issue the following command line: java Type . The contents of Type.java -- or whatever file is specified -- will display. As an experiment, try specifying java Type. What do you think will happen? (Hint: this program resembles Echo but doesn't end until you press Ctrl+C.)

Earlier, I mentioned that some programmers mistakenly think that System.in.read() returns a user-entered number. As you've just seen, that isn't the case. But what must you do if you want to use System.in.read() to retrieve a number? Take a look at the Convert application, whose source code is presented in Listing 3.

Listing 3. Convert.java

// Convert.java class Convert { public static void main (String [] args) throws java.io.IOException { System.out.print ("Please enter a number: "); int num = 0; int ch; while ((ch = System.in.read ()) != '\n') if (ch >= '0' && ch <= '9') { num *= 10; num += ch - '0'; } else break; System.out.println ("num = " + num); System.out.println ("num squared = " + num * num); } } 

Listing 3's Convert program prompts the user to enter a number (via System.out.print ("Please enter a number: ");). It reads these digits -- one at a time -- and converts each digit's numeric code to a binary number that is added to a variable called num. Finally, calls to System.out.println() output the value inside num and the square of that value to the standard output device.

Convert demonstrates the time-honored technique of using a while loop to test for a digit, premultiplying a variable by 10 (to make room for the incoming digit), converting a digit to its binary equivalent, and adding that binary equivalent to the variable. However, that technique is not a sound technique to use if you're writing a program for deployment in different countries as some countries use digits other than 0 through 9 -- such as Tamil digits. To make the program operate with other digits, you need to expand the if statement to test for those digits and modify the ch - '0' expression. Fortunately, Java simplifies that task by providing a Character class, which you'll explore in a future article.

Standard output

The standard output device is that part of the operating system that controls where a program sends its output. By default, the standard output device sends the output to a device driver attached to the screen. However, the output destination can be redirected to a device driver attached to a file or printer, which results in the same program displaying its findings on the screen, saving them in a file, or providing a hardcopy listing of the results.

You achieve standard output by calling Java's System.out.print() and System.out.println() methods. Except for the fact that print() methods don't output a new-line character after the data, the two method groups are equivalent. Methods exist to output Boolean, character, character array, double-precision floating-point, floating-point, integer, long integer, string, and object values. To demonstrate these methods, Listing 4 presents source code to the Print application.

Listing 4. Print.java

// Print.java class Print { public static void main (String [] args) { boolean b = true; System.out.println (b); char c = 'A'; System.out.println (c); char [] carray = { 'A', 'B', 'C' }; System.out.println (carray); double d = 3.5; System.out.println (d); float f = -9.3f; System.out.println (f); int i = 'X'; System.out.println (i); long l = 9000000; System.out.println (l); String s = "abc"; System.out.println (s); System.out.println (new Print ()); } } 

Listing 4 has probably triggered some questions for you. First, what is all that System.out. business doing in front of println()? Again, refer to the System class in the SDK documentation. The class contains a variable called out -- an object created from a class called PrintStream. The period character after System indicates that out belongs to System. The period character after out states that println() belongs to out. In other words, println() is a method that belongs to an object called out, which in turn belongs to a class called System.

The second question you might be asking yourself involves println() argument data types: how is it possible for the same println() method to be called with different types of argument data? The answer: because there are several println() methods in the PrintStream class. At runtime, the JVM knows which println() method to call by examining the number of method-call arguments and their data types. (Declaring several methods with the same name but different numbers of arguments and argument data types is known as method overloading. I will discuss that concept next month.)

Finally, you might be wondering about System.out.println (new Print ());. That method call illustrates the println() method, which takes an Object argument. First, the creation operator new creates an object from the Print class and returns a reference to -- also known as the address of -- that object. Finally, that address passes as an argument to the println() method, which takes an Object argument. The method converts the object's contents to a string and outputs that string. By default, the string consists of the name of the object's class, followed by an @ (at) character, followed by a hexadecimal-formatted integer that represents the object's hashcode. (I will present hashcodes and the conversion of objects to strings in an upcoming article.)

Compile Print.java and run the program by issuing the following command line: java Print. You should see nine lines of output. Redirect that output to the out.dat file by issuing the following command line: java Print >out.dat. You can now view the contents of the file.

The greater-than sign, >, indicates standard output redirection. Whenever you want to redirect the standard output device from the screen to a file or printer, specify that symbol followed by the file or printer name on the command line. For example, redirect Print's output to a Windows printer by issuing the following command line: java Print >prn.