Java 101: i dettagli dello standard input / output

Negli articoli precedenti di Java 101 , ho fatto riferimento ai concetti di reindirizzamento, dispositivo di input standard e dispositivo di output standard. Per dimostrare l'immissione dei dati, vengono chiamati diversi esempi System.in.read(). Si scopre che System.in.read()immette dati dal dispositivo di input standard. Per dimostrare l'output dei dati, esempi chiamati System.out.print()e System.out.println(). Al contrario System.in.read(), questi metodi - sequenze denominate di codice eseguibile (da esplorare nell'articolo del prossimo mese) - inviano il loro output al dispositivo di output standard. Vuoi saperne di più sui concetti di I / O standard? Continuare a leggere!

L'I / O standard è un meccanismo di input / output standardizzato che ha origine dal sistema operativo Unix. Sebbene questo meccanismo sia utilizzato principalmente con i vecchi sistemi operativi non GUI, l'I / O standard gioca ancora un ruolo nei moderni sistemi operativi GUI (interfaccia utente grafica), dove le persone lo usano per eseguire il debug di programmi malfunzionanti e per insegnare input / output in ingresso- corsi di programmazione di livello.

Come probabilmente avrai intuito, l'I / O standard utilizza dispositivi per l'immissione e l'output dei dati. Questi dispositivi includono input standard, output standard e errore standard.

Input standard

Il dispositivo di input standard è quella parte del sistema operativo che controlla da dove un programma riceve il suo input. Per impostazione predefinita, il dispositivo di input standard legge quell'input da un driver di dispositivo collegato alla tastiera. Tuttavia, puoi reindirizzare, o cambiare, la sorgente di input a un driver di dispositivo collegato a un file in modo che l'input sembri provenire "magicamente" da un file, invece che dalla tastiera.

Un programma immette i propri dati dal dispositivo di input standard chiamando il System.in.read()metodo Java . Guarda nella documentazione dell'SDK e scoprirai una classe chiamata System. Quella classe contiene una variabile chiamata in- un oggetto creato da una sottoclasse di InputStream. Il carattere punto dopo gli Systemstati a cui inappartiene Systeme il carattere punto dopo gli instati a cui read()appartiene in. In altre parole, read()è un metodo che appartiene a un oggetto chiamato in, che a sua volta appartiene a una classe chiamata System. (Discuterò di più su classi, oggetti e "appartenenza a" il mese prossimo.)

System.in.read()non accetta argomenti e restituisce un numero intero, il che ha portato alcuni a credere che System.in.read()restituisca numeri interi inseriti dall'utente. Per chiarire, System.in.read()restituisce il codice ASCII a 7 bit di una chiave (se il dispositivo di input standard è impostato sulla tastiera) o un byte a 8 bit da un file (se il dispositivo di input standard è stato reindirizzato dalla tastiera a un file). In entrambi i casi, System.in.read()converte il codice in un numero intero a 32 bit e restituisce il risultato.

Supponiamo che il dispositivo di input standard sia impostato sulla tastiera. Quanto segue è una descrizione di ciò che accade in Windows: Quando si digita un tasto su una tastiera controllata da Windows, il sistema operativo memorizza il codice ASCII a 7 bit di quel tasto in un buffer di tasti interno. Il buffer della chiave può contenere fino a circa 16 codici ASCII ed è organizzato come una struttura di dati della coda circolare first-in / first-out. System.in.read()recupera il codice ASCII dall'intestazione del buffer delle chiavi e quindi rimuove tale codice dal buffer delle chiavi. Quel codice ASCII a 7 bit viene quindi convertito in un int- System.in.read()anteponendo 25 bit zero al codice - e ritorna al chiamante del metodo. Una seconda System.in.read()chiamata al metodo recupera il codice ASCII successivo, che ora si trova all'inizio del buffer delle chiavi e così via.

Supponiamo che non ci siano codici ASCII nel buffer delle chiavi. Che succede? System.in.read()attende che l'utente digiti i tasti e prema il terminatore. Sotto Windows, quel terminatore è la Enterchiave. La pressione Enterfa sì che Windows memorizzi un codice di ritorno a capo (ASCII 13) seguito da un codice di nuova riga (ASCII 10) nel buffer delle chiavi. Pertanto, il buffer delle chiavi potrebbe contenere diversi codici ASCII seguiti da un ritorno a capo e un carattere di nuova riga. Il primo di quei codici ritorna da System.in.read(). Controlla quell'attività digitando, compilando ed eseguendo l' Echoapplicazione; il suo codice sorgente appare nel listato 1.

Listato 1. Echo.java

// Echo.java class Echo {public static void main (String [] args) throws java.io.IOException {int ch; System.out.print ("Immettere del testo:"); while ((ch = System.in.read ())! = '\ n') System.out.print ((char) ch); }}

Echo completa i seguenti passaggi:

  1. Chiama il System.out.print()metodo, che accetta un Stringargomento, per generare un prompt
  2. Chiama System.in.read()per inserire codici ASCII dal dispositivo di input standard come numeri interi a 32 bit
  3. Converte questi numeri interi a 32 bit in caratteri Unicode a 16 bit tramite il (char)cast
  4. Chiama il System.out.print()metodo, che accetta un charargomento, per riprodurre quei caratteri Unicode nel dispositivo di output standard

Gli ultimi tre passaggi nei quattro passaggi precedenti si svolgono in un ciclo while e continuano finché non viene letto un carattere di nuova riga. Per eseguire Echoin modo che gli ingressi dalla tastiera e uscite allo schermo, emettere il seguente comando: java Echo.

Sebbene System.in.read()non generi mai un'eccezione (vedere l'argomento sul conteggio delle parole in questo articolo per una definizione di quel termine), quando il dispositivo di input standard è impostato sulla tastiera, potrebbe generare un'eccezione quando si reindirizza il dispositivo di input standard dalla tastiera a un file. Ad esempio, si supponga di reindirizzare il dispositivo di input standard a un file e di System.in.read()leggere il contenuto dal file. Supponiamo ora che il file si trovi su un floppy disk e l'utente lo espelle durante l'operazione di lettura. Quando avviene l'espulsione, System.in.read()genera un'eccezione, informando il programma che non può leggere il file. Ciò fornisce il motivo per aggiungere la throws java.io.IOExceptionclausola all'intestazione del main()metodo. (Esplorerai eccezioni, generazione di eccezioni e concetti correlati in un prossimo articolo.)

Come si reindirizza il dispositivo di input standard in modo che l'input provenga da un file? La risposta è introdurre un segno minore di,, <sulla riga di comando e seguire quel simbolo con un nome di file. Per vedere come funziona, immetti la seguente riga di comando: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.