| Home > Core Java FAQ
> Input - Output FAQ |
| Input-OutPut |
| Input Output (38) |
| |
|
Q .
How do I do keyboard (interactive) I/O in Java?
|
Ans :
Interactive I/O in Java is very poorly supported. Programmers must
piece together several library classes in non-obvious ways to get the
required functionality.
|
|
Q .
Is there a way to read a char from the keyboard without having to type carriage-return?
|
Ans
:
You can do this in a GUI (e.g. in a text component). There is no
pure Java way to do character-by-character I/O without using a GUI.
|
|
Q .
How do I read a line of input at a time?
|
Ans
:
In the JDK
1.0.2, use the readLine
method in the DataInputStream class;
in the JDK 1.1, use readLine in
either BufferedReader or LineNumberReader.
The
DataInputStream class is a hidden workhorse in the
Java input/output system. The class exists chiefly to read various
data types in binary format from an input stream. It also provides
a readLine method for reading one line of input at a
time.
The readLine method
reads from the current input position up to a line terminator. For
cross-platform compatibility, readLine recognizes as
line terminators the bytes for newline (\n), carriage
return (\r), and the byte sequence newline-return (\n\r).
Reaching the end of a file also counts as terminating an input
line. Invoking readLine at the end of the input
stream returns null.
The following sample code defines a
method that calculates the average line length of text in the
input stream:
/* using JDK 1.0.2: */
public static float getAverageLineLength(InputStream in)
throws IOException {
DataInputStream dataIn = new DataInputStream(in);
String currentLine;
int lineCount = 0;
int charCount = 0;
while ((currentLine = dataIn.readLine()) != null) {
++lineCount;
charCount += currentLine.length();
}
return (charCount / (float) lineCount);
}
DataInputStream's
readLine method reads bytes, but it outputs a Java
string containing 2-byte Unicode characters. Each input byte gets
translated to a Unicode character by using the input byte as the
output character's low byte and setting the output character's
high byte to zero. Thus, DataInputStream's readLine
method assumes that each input byte is a 8-bit character, which
renders the method unfit for reading lines of Unicode text in
general.
The JDK 1.1 fills in the Unicode
input deficiency with a suite of ...Reader classes,
which truly read Unicode input characters (Java's char type)
rather than just byte streams. For line-oriented input, in
particular, you can use the readLine method in either
the BufferedReader or LineNumberReader
classes. The sample method, rewritten to use LineNumberReader,
follows:
/* using JDK 1.1: */
public static float getAverageLineLength(InputStream in)
throws IOException {
LineNumberReader lineReader = new LineNumberReader(
new InputStreamReader(in));
String currentLine;
int charCount = 0;
while ((currentLine = lineReader.readLine()) != null) {
charCount += currentLine.length();
}
return (charCount / (float) lineReader.getLineNumber());
}
This example also
illustrates using the InputStreamReader class to
convert from a byte-oriented input stream to a char-oriented
Reader. The LineNumberReader instance is
then constructed from that Reader object.
|
|
Q .
How do I read input from the user (or send output) analogous to using standard input and standard output in C or C++?
|
Ans
:
Use the
standard system streams: System.in,
System.out, and System.err.
C
and C++ define standard input/output libraries (collections of
compiled functions) that rely heavily on three input/output
streams, called standard input, standard output, and standard
error. The analogue in Java to a library in C or C++ is a Java
language package. Completing the analogy, the java.io
package defines essentially the same three streams, which you
access as fields of the System class:
System.in:
standard input; the customary input stream for keyboard input
when a program lacks a graphical user interface (GUI).
System.out:
standard output; the customary output stream for programs that
lack a GUI.
System.err:
standard error; the customary output stream for error output
in programs that lack a GUI.
System.in
is an instance of InputStream and System.out
and System.err are instances of class PrintStream.
If you want just string output (to
either System.out or System.err), the PrintStream
methods print and println, together with
the string concatenation operator +, provide straightforward tools
that get the job done (in the JDK 1.0.2). For example, the
following code prints the value of one int variable and one float
variable to System.out:
/* using JDK 1.0.2: */
int intValue = 3;
float floatValue = 3.1416F;
System.out.println("My int value = " + intValue + ".");
System.out.println("My float value = " + floatValue + ".");
For
proper handling of Unicode text, the JDK 1.1 supersedes many of
the byte-stream input/output classes with Unicode text-oriented
classes: Reader, Writer, and their many
subclasses. The PrintStream class, for example, is
superseded by the PrintWriter class, with its own
version of print and println. Thus, to use println to write to the
standard output, you build a PrintWriter instance
from the underlying System.out byte stream:
/* using JDK 1.1: */
int intValue = 3;
float floatValue = 3.1416F;
PrintWriter writer;
// Create a PrintWriter with autoflush set to true,
// so that each newline will automatically flush the output.
writer = new PrintWriter(System.out, true);
writer.println("My int value = " + intValue + ".");
writer.println("My float value = " + floatValue + ".");
Warning:
When migrating code from 1.0.2 to 1.1, you may encounter some
nasty surprises unless you carefully match the automatic flushing
behavior of System.out. Use PrintWriter's
two-parameter constructor, with the second argument set to true;
this ensures that the output will be flushed once for each newline
you send.
Reading from System.in
is more complicated, depending on what format you're expecting to
read the data in. System.in gives you a plain input
byte stream, which has no idea of input formats and can read only
one byte at a time. For more control over your input, you need to
build a fuller-featured input stream around System.in,
such as a DataInputStream instance. The following
code fragment shows a DataInputStream instance
reading one line of input at a time:
/* using JDK 1.0.2: */
String currentLine;
DataInputStream inStream = new DataInputStream(System.in);
try {
while ((currentLine = inStream.readLine()) != null) {
// ... process current line
}
} catch (IOException e) { /* ... */ }
// ... handle exception
}
Updating this code for
JDK 1.1 primarily involves wrapping a BufferedReader
instance around System.in :
/* using JDK 1.1: */
String currentLine;
BufferedReader reader;
reader = new BufferedReader(new InputStreamReader(System.in));
try {
while ((currentLine = reader.readLine()) != null) {
// ... process current line
}
} catch (IOException e) { /* ... */ }
|
|
Q .
Is there a standard way to read in int, long, float, and double values from a string representation?
|
Ans
:
There is no
one standard way, but there is a small group of methods and
constructors that will do the job for you.
The java.lang
package in the JDK 1.0.2 provides four classes for treating
numbers as objects: Integer, Long, Float,
and Double. Each of these classes provides
constructors and methods that translate from a string to an
instance of the class. You then need to invoke an additional <type>Value
method to retrieve the numeric value of type <type>
held in the numeric object. Alternatively, the Integer and Long
classes provide additional parse<type>
methods that make the conversion in one step. These techniques are
arrayed in Table.
Table
: Converting from Strings to Numerical Values
| Target
type |
Code
to convert from a String instance (JDK
1.0.2) |
int |
new
Integer(String).integerValue() |
Integer.valueOf(String).integerValue() |
Integer.parseInt(String) |
long |
new
Long(String).longValue() |
Long.valueOf(String).longValue() |
Long.parseLong(String) |
float |
new
Float(String).floatValue() |
Float.valueOf(String).floatValue() |
double |
new
Double(String).doubleValue() |
Double.valueOf(String).doubleValue() |
Note:
the valueOf methods in Integer and Long
can also take a second argument specifying the radix for
interpreting the string (e.g., base 8 for octal numbers).
This picture is expanded
considerably in the JDK 1.1. As shown in Table , new classes Short
and Byte parallel Integer and Long,
and there is an additional decode method in Integer, Short,
and Byte (but not Long, curiously):
Table
: Additional Conversions from String to Number—JDK 1.1
int |
Integer.decode(String).intValue() |
short |
Short.parseShort(String) |
new
Short(String).shortValue() |
Short.valueOf(String).shortValue() |
Short.decode(String).shortValue() |
byte |
Byte.parseByte(String) |
new
Byte(String).byteValue() |
Byte.valueOf(String).byteValue() |
Byte.decode(String).byteValue() |
The decode
methods add the convenience of understanding conventional prefixes
such as 0x and # for hexadecimal and 0
for octal.
All of the above number-parsing
expressions throw a NumberFormatException if the String
argument does not parse correctly for the desired numerical type.
Correct parsing requires that the string is well formed as a
number representation and that the value it represents lies within
the range covered by the numerical type. The string
"128", for instance, fails to parse as a byte. Your code
must handle the possible NumberFormatException
explicitly, either by catching the exception or declaring it. For
example:
String someString = ...;
double value;
try {
value = Double.valueOf(someString).doubleValue();
} catch (NumberFormatException e) {
// ... handle exception
}
// ... now use value
Finally,
note that the valueOf and parse<type>
methods each have a counterpart taking a second parameter—the
radix to use for interpreting the string numerically. For example,
the following code parses a long value from a string
in hexadecimal format:
String hexString = "cafebabe"; // cannot have leading 0x
long value = 0; // int isn't big enough to hold "cafebabe"
try {
value = Long.parseLong(hexString, 16);
} catch (NumberFormatException e) { /* ... */ }
|
|
Q . How do I read a String/int/boolean/etc from the keyboard?
|
Ans
:
The easiest way is to pick up the source for the 100% pure Java
class EasyIn from http://www.afu.com/ (same place as this FAQ). Compile
it with your code and use it like this:
EasyIn easy = new EasyIn();
int i = easy.readInt(); // gets an int from System.in
boolean b = easy.readBoolean(); // gets a boolean from System.in
double d = easy.readDouble(); // gets a double from System.in
... etc.
EasyIn is free, comes with source, and you can do what you like with it, including improve it, and send me back the results.
If, instead, you want to "roll your own" code (why?!), in JDK 1.0.2
java.io.DataInputStream in = new java.io.DataInputStream(System.in);
String s = in.readLine();
One way in JDK 1.1:
java.io.BufferedReader in =
new java.io.BufferedReader( new InputStreamReader(System.in));
String s = in.readLine();
Once you have the token in a String, it is easy to parse it into one of the other types, as shown earlier in the FAQ. Yes, it is bone-headed,
as it makes the simplest case of keyboard I/O unnecessarily
complicated. A bug was filed with Javasoft to record this problem, but don't count on this being fixed any time soon.
|
|
Q . I try to use "int i = System.in.read();" to read in an int from the standard input stream. It doesn't work. Why?
|
Ans
:
The method read() reads in a single Byte and returns it in int form. System.in is of the class
InputStream (the base class of all input streams). InputStream does not provide methods to read int directly. You may want to do this:
DataInputStream dis = new DataInputStream(System.in);
StringTokenizer st;
...
st = new StringTokenizer(dis.readLine());
int i = Integer.parseInt(st.nextToken());
The method readLine() throws IOException. So, you need to either include the above code in an appropriate try block, or declare the method containing the above code to throw IOException.
|
|
Q . I use the following to read an int. It does not work. Why?
|
Ans
:
DataInputStream dis = new DataInputStream(System.in);
int i = dis.readInt();
DataInputStream dis = new DataInputStream(System.in);
int i = dis.readInt();
The method readInt() reads in a 4 Byte int. It does not parse the text to convert to an integer. If you use readInt(), it will read in the next four chars and treat them as an int. To read an int, use the technique described in the answer to the previous question.
|
|
Q . I'm trying to read in a character from a text file using the DataInputStream's readChar() method. However, when I print it out, I get ?'s.
|
Ans
:
Remember that Java characters are 16-bit Unicode characters, while
many hosts systems store characters as 8-bit ASCII characters. Therefore, to read individual chacters from a text file, you need to
ensure the proper conversion. The proper way to do this is to use an
InputStreamReader, which converts from 8 to 16 bit streams:
FileInputStream fis = new FileInputStream("myfile.txt");
InputStreamReader isr = new InputStreamReader(fis);
char c3 = (char) isr.read();
The less-favored way (because it is not so portable, as the encodings translation is not done) is just to read a byte and cast it into a
character:
FileInputStream fis = new FileInputStream("myfile.txt");
DataInputStream dis = new DataInputStream(fis);
char c1 = (char) dis.readByte();
|
|
Q . Why do I get garbage results when I use DataInputStream's readInt or readFloat methods to read in a number from an input string?
|
Ans
:
DataInputStream
methods such as readInt and readFloat
are not for reading string input—they expect to read a binary
representation of the specified numeric type in network byte order
(high-end byte first).
The
DataInputStream and DataOutputStream
classes are designed primarily for platform-neutral transport of
binary data. Invoking DataOutputStream's writeInt
method, for example, writes a four-byte binary representation of
the int value to the output stream, high-order byte first.
Correspondingly, DataInputStream's readInt
method takes four bytes from the input stream and combines them
into a single int value:
/* in DataInputStream.java (JDK 1.0.2 and 1.1): */
public final int readInt() throws IOException {
InputStream in = this.in;
int ch1 = in.read();
int ch2 = in.read();
int ch3 = in.read();
int ch4 = in.read();
if ((ch1 | ch2 | ch3 | ch4) < 0)
throw new EOFException();
return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
If
you mistakenly try to read a string representation of an integer
value using readInt, the first four bytes from that
string representation will be converted according to the formula
just shown. The result will be systematic, predictable, and almost
certainly wrong.
A simple rule for remembering the
purpose of the read... methods in DataInputStream
is that they exist to read the output of the corresponding DataOutputStream
write... methods. A notable exception to this naming pattern is readLine
—its counterpart is writeBytes.
Note: DataInputStream's
readLine method has been deprecated starting with JDK
1.1. Its replacement is the readLine method in class BufferedReader.
|
|
Q .
How do I read data from a file?
|
Ans
:
There are a number of ways to read data from a file. If you're reading a file as raw binary data, you open a file using a FileInputStream(String) constructor and use one of the various read() methods to read the data into an array of bytes. For example the following program reads raw data from a file specified on the command line. It then writes the same data to the standard output.
import java.io.*;
class ReadRawData {
public static void main (String args[]) {
boolean done = false;
byte b[] = new byte[1024];
int num_bytes = 0;
FileInputStream fin = null;
try {
fin = new FileInputStream(args[0]);
}
catch(ArrayIndexOutOfBoundsException e) {
System.out.println("You have to give me the name of a file to open.");
System.exit(0);
}
catch (FileNotFoundException e) {
System.out.println("Could not open input file " + args[0]);
System.exit(0);
}
catch(IOException e) {
System.out.println("Error while opening input file" + args[0]);
System.exit(0);
}
catch (Exception e) {
System.out.println("Unexpected exception: " + e);
System.exit(0);
}
try {
num_bytes = fin.read(b);
}
catch(IOException e) {
System.out.println("Finished Reading: " + e);
done = true;
}
catch (Exception e) {
System.out.println("Unexpected exception: " + e);
System.exit(0);
}
while(!done) {
System.out.write(b, 0,
num_bytes);
try {
num_bytes = fin.read(b);
}
catch(IOException e) {
System.out.println("Finished Reading: " + e);
done = true;
}
catch (Exception e) {
System.out.println("Unexpected exception: " + e);
System.exit(0);
}
if (num_bytes == -1) done = true;
} // end while
} // end main
} // end ReadRawData
On the other hand if you're reading a text file in Java 1.0 you'll probably want to use a DataInputStream which gives you a readLine() method that returns successive lines of the file as Java Strings. You can then process each String as you see fit.
// Implement the Unix cat utility in java
import java.io.*;
class cat {
public static void main (String args[]) {
String thisLine;
//Loop across the arguments
for (int i=0; i < args.length; i++) {
//Open the file for reading
try {
FileInputStream fin = new FileInputStream(args[i]);
try {
DataInputStream myInput = new DataInputStream(fin);
try {
while ((thisLine = myInput.readLine()) != null) { // while loop begins here
System.out.println(thisLine);
} // while loop ends here
}
catch (Exception e) {
System.out.println("Error: " + e);
}
} // end try
catch (Exception e) {
System.out.println("Error: " + e);
}
} // end try
catch (Exception e) {
System.out.println("failed to open file " + args[i]);
System.out.println("Error: " + e);
}
} // for ends here
} // main ends here
}
This code emulates the Unix "cat" command. Given a series of filenames on the command line it concatenates the files onto the standard output.
In Java 1.1 DataInputStream.readLine() is deprecated. You should use a BufferedReader instead as in this class:
// Implement the Unix cat utility in java
import java.io.*;
class cat {
public static void main (String args[]) {
String thisLine;
//Loop across the arguments
for (int i=0; i < args.length; i++) {
//Open the file for reading
try {
FileReader fr = new FileReader(args[i]);
BufferedReader myInput = new BufferedReader(fr);
while ((thisLine = myInput.readLine()) != null) { // while loop begins here
System.out.println(thisLine);
} // while loop ends here
} // end try
catch (IOException e) {
System.out.println("Error: " + e);
}
} // for ends here
} // main ends here
}
|
|
Q . How do I write data to a file?
|
Ans
:
You should only assume you'll be able to write to a file from an application. Although it may be possible to write data into a file from an applet if the browser viewing the applet is HotJava, this ability will generally be disabled. From within Netscape there is no way for an applet to write to a file on the local hard drive.
Within an application, however, file access is straight-forward. There are several ways but here is a simple example using formatted output streams:
import java.io.*;
class PrintToAFile {
public static void main (String args[]) {
//First open the file you want to write into
try {
FileOutputStream fout = new FileOutputStream("test.out");
// now convert the FileOutputStream into a PrintStream
PrintStream myOutput = new PrintStream(fout);
// Now you're able to use println statements just as if you were using System.out.println
// to write to the terminal
myOutput.println("Hello There!");
myOutput.println(1 + " + " + 1 + " = " + (1+1));
}
catch (IOException e) {
System.out.println("Error opening file: " + e);
System.exit(1);
}
} // main ends here
}
There are a number of other things to note about writing data to a file. This program creates or opens a file called "test.out" in the same directory as the running program. However you could pass it a full pathname to a file in a different directory instead.
You should also learn about the DataOutputStream class and the write() method when you get a chance. DataOutputStreams and DataInputStreams are used for moving data between Java programs in a portable way. The various incarnations of the write() method are used for writing and reading arbitrary byte streams. What I've demonstrated here is more suitable for human consumption.
In Java 1.1 you should probably use a PrintWriter instead of a
PrintStream.
|
|
Q .
How do I append data to a file?
|
Ans
: In Java 1.1 you can just pass true as the second argument to this FileOutputStream constructor to indicate that you want to append data to the file:
public FileOutputStream(String name, boolean append)
throws IOException
In Java 1.0, however, you must use the java.io.RandomAccessFile class that lets you read and write bytes from arbitrary locations in a file. This class implements DataInput and DataOutput so you have all the methods of DataInputStream and DataOutputStream available to you.
To create a new random access file pass the name of the file and the mode to the constructor. The mode is either "r" (read-only) or "rw" (read and write). The length() method returns a long that tells you how many bytes there are in a file and the seek(long p) method lets you position the file pointer at a particular point in the file. Thus to start writing at the end of a RandomAccessFile raf, you first raf.seek(raf.length()). The following example demonstrates by appending the string "Kilroy was here!" to every file specified on the command line.
import java.io.*;
class AppendToAFile {
public static void main (String args[]) {
for (int i = 0; i < args.length; i++) {
//First open the file you want to append to
try {
RandomAccessFile raf = new RandomAccessFile(args[i], "rw");
// Position yourself at the end of the file
raf.seek(raf.length());
// Write the String into the file. Note that you must
// explicitly handle line breaks.
raf.writeBytes("\nKilroy was here!\n");
}
catch (IOException e) {
System.out.println("Error opening file: " + e);
}
}
}
}
|
|
Q . When do I need to flush an output stream?
|
Ans
:
Invoke flush
on a stream whenever you want your output to be sent to the other
end of the output connection (file, remote socket, etc.) as soon
as possible.
Some
methods of the input/output system buffer their output and input.
In other words, they read or write data in relatively large
chunks—usually larger than required by individual method
calls—and they keep any excess material in a buffer (temporary
storage area) until required to move the data. The flush
method in the OutputStream class instructs the system
to send any pending material without further delay.
Besides invoking flush
explicitly, you should be aware of flushing that can occur
automatically. In the JDK 1.0.2, instances of the PrintStream
class can be created with automatic flushing (autoflush)
turned on or off. When autoflush is on, the output is
flushed whenever a newline occurs in the output stream. Using PrintStream's
one-parameter constructor, PrintStream(OutputStream),
gives the default: autoflush off. To create a PrintStream
instance with autoflush on, use the two-parameter
constructor, with a second argument of true:
OutputStream outStream = ...;
PrintStream out = new PrintStream(outStream, true);
In the JDK 1.1, PrintStream
is superseded by the PrintWriter class, which
provides the same autoflushing controls and defaults. However, a PrintWriter
instance does not inherit the autoflushing properties of the
underlying output stream it is build on top of. In particular, System.out
is a PrintStream instance autoflush set
to true, but you must still explicitly set autoflush
to true for a PrintWriter instance built on top of System.out:
PrintWriter writer;
writer = new PrintWriter(System.out, true);
|
|
Q . Why do I see no output when I run a simple process, such as r.exec("/usr/bin/ls")?
|
Ans
:
You need to
get an input stream from the process, so that output from the
process becomes input for your program.
The
Runtime class connects a Java application to the
native environment in which the application is running. Among
other things, it allows you to start independent processes on the
host system, via one of the exec methods. The
following code fragment would work on a UNIX system to start a
local process that lists the files in the current directory:
Runtime r = Runtime.getRuntime();
Process p = r.exec("/usr/bin/ls");
To
communicate with a process you've started, you need to hook up the
process's standard input and output streams to output and input
streams in your Java program. The Process class
provides three methods for making such connections, shown in Table
10.3.
Table
10.3: Methods for Communicating with a Process
Instance
getInputStream() |
returns
an InputStream object for reading from the
process's standard output |
getOuputStream() |
returns
an OutputStream object for writing to the
process's standard input |
getErrorStream() |
returns
an InputStream object for reading from the
process's standard error output |
Remember
that what you call input versus output depends on which side of
the connection you view it from: the process's output is your
program's input, and your program's output can be the process's
input. Also, be aware that if you use the exec
method, your program forfeits the cross-platform advantage central
to Java technology.
The following code fragment presents
a simple example:
/* using JDK 1.0.2, on Solaris: */
Process p;
Runtime r = Runtime.getRuntime();
try {
p = r.exec("/usr/bin/wc"); // UNIX word count program
/* Send lines of text to the subprocess. */
out = new PrintStream(p.getOutputStream());
out.println("This is line 1.");
out.println("This is line 2.");
out.println("And this is a somewhat long line 3.");
out.close(); // important
/* Read in the output from the subprocess. */
in = new DataInputStream(p.getInputStream());
while ((currentLine = in.readLine()) != null)
System.out.println(currentLine);
}
} catch(IOException e) {
// ... handle exception
}
The
corresponding code for the JDK 1.1 uses the same Runtime
and Process methods, but wraps appropriate Reader
or Writer subclass instances around the underlying
byte streams:
/* using JDK 1.1, in Solaris: */
Process p;
Runtime r = Runtime.getRuntime();
try {
p = r.exec("/usr/bin/wc"); // UNIX word count program
PrintWriter outWriter = new PrintWriter(System.out, true);
/* Send lines of text to the subprocess. */
writer = new PrintWriter(p.getOutputStream(), true);
writer.println("This is line 1.");
writer.println("This is line 2.");
writer.println("And this is a somewhat long line 3.");
writer.close(); // important
/* Read in the output from the subprocess. */
reader = new BufferedReader(new InputStreamReader(
p.getInputStream()));
while ((currentLine = reader.readLine()) != null) {
outWriter.println(currentLine);
}
} catch(IOException e) {
// ... handle exception
}
|
|
Q . Can I write objects to and read objects from a file or other stream?
|
Ans
:
In theory yes, but once again all the coding is up to you. There is no general method for doing this in Java 1.0. The problem is made harder by Java's security features that don't let you forge arbitrary byte streams into objects.
In Java 1.1 a serialization interface has been added to the language. However only objects that explicitly implement the java.io.Serializable interface can be serialized.
|
|
Q .
How do I format numbers like C's printf()?
|
Ans
:
Java does not have any built in equivalent to C's printf/sprintf/fprintf family of functions that specify the width and precision of numbers converted into strings. Since Java does not support variable length argument lists, it's not possible to write exact equivalents for these functions. Instead the approach that must be taken is to convert one number at a time into a string according to a format specification, then write the resulting string onto the appropriate output stream. This is a more flexible solution, but it's far from obvious.
In Java 1.1, the java.text package contains classes that format numbers according to particular needs. In particular it's worth getting to know the java.text.NumberFormat and java.text.DecimalFormat classes, though these can't handle exponential notation. I've begun work on my own formatting class that does handle exponential and other notations available through printf(). It can be found at http://metalab.unc.edu/javafaq/formatter/. Gary Cornell and Cay Horstmann's popular book Core Java also includes such a class. You can probably find more at Gamelan.
|
|
Q . How do I do file I/O in an applet?
|
Ans
:
By default, an applet can read files on the server, but not write
them, and has no access to the client. This is for reasons of security.
It would be very unsafe to let any old applet that you downloaded from
an unknown origin on the Internet read/write your files. It would be as unwise as allowing this kind of access to an ActiveX control (which is
one reason ActiveX is dead on the Internet).
|
|
Q . How do I do I/O to the serial port on my computer?
|
Ans
:
Java 1.0 and 1.1 do not have a serial port API. There are several
commercially-available libraries that supply the needed functionality.
JDK 1.2 introduces access to the serial and parallel ports as an
extension (optional extra) library.
|
|
Q . How do I do formatted I/O like printf and scanf in C/C++?
|
Ans
:
The java.text package introduced with Java 1.1 supports formatted
I/O.
|
|
Q .
How do I read a file containing ASCII numbers?
|
Ans
:
There are several ways to do this. Here is one way. Let's assume
your file is called "C:\work\mydata.txt" and it contains lines like:
135 7512 3659814 328 1
54829
68522 19982810 38
i.e. lines contain several ASCII strings that are numbers separated by spaces.
The code fragment is as follows:
// Open the file with
RandomAccessFile f = new RandomAccessFile("c:\\work\\datafile.txt", "r");
// read an entire line from it
String s= f.readLine();
// get some methods to break up a line into tokens
StringTokenizer st = new StringTokenizer(s);
// extract the next int from the line
i = Integer.parseInt(st.nextToken());
We use a RandomAccessFile because that supports the readLine() method directly. An alternative would be to instantiate a FileReader, and wrap
a BufferedReader around it. Putting it all together, including the
exception handling in the event the file is missing, the code lookslike:
import java.io.*;
import java.util.*;
public class c {
public static void main(String args[]) {
try {
RandomAccessFile f = new RandomAccessFile("datafile.txt", "r");
String s;
while ( (s=f.readLine()) != null ) {
System.out.println("read: "+s);
StringTokenizer st = new StringTokenizer(s);
int i=0;
while (st.hasMoreTokens()) {
i = Integer.parseInt(st.nextToken());
// i now holds the next int on the line
// could also use Double.parseDouble(), etc.
System.out.print(" "+ i);
}
System.out.println();
}
} catch (Exception e) {System.out.println("Excpn: "+e); }
// file I/O, from book "Just Java" by Peter van der Linden
}
}
|
|
Q . Why do I have trouble with System.out.println()?
|
Ans
:
"a & b" takes two boolean operands, or two integer operands. It
always evaluates both operands. For booleans, it ANDs both operands
together producing a boolean result. For integer types, it bitwise ANDs
both operands together, producing a result that is the promoted type of
the operands (i.e. long, or int). "|" is the corresponding bitwise OR
operation. "^" is the corresponding bitwise XOR operation.
"a && b" is a "conditional AND" which only takes boolean operands. It
always avoids evaluating its second operand if possible. If a is evaluated to false, the AND result must be "false" and the b operand is
not evaluated. This is sometimes called "short-circuited" evaluation.
"||" is the corresponding short-circuited OR operation.
|
|
Q . How do I write to the serial port on my PC using Java?
|
Ans
:
There is a platform-independent serial port API introduced in JDK
1.2. You can download the documentation by registering with the Java
Developer Connection (it's free, http://java.sun.com) and browsing
http://java.sun.com/jdc/earlyAccess/communications.html.
For systems prior to JDK 1.2, read on. At least two companies have written a library to drive the port. See
o http://www.sc-systems.com has a library for Windows 95, WindowsNT, OS/2, Macintosh PPC, Solaris Sparc, Linux x86, FreeBSD x86,
HP/UX PA-RISC, and possibly others too.
o http://www.cd.com/portio
o In addition, there is a Unix serial port utility available with source at http://jarvi.ezlink.com/rxtx/ It's free under the
GPL, and works on Linux, Irix, Solaris, Windows 95, and NT.
While not helpful to typical home users, there is an alternative portable COM port solution for Java 1.1 and even 1.0. Buy your COM
ports in the form of "terminal servers". Using a COM port is now as
easy as connecting to it with a Socket. Port parameters can be changed programatically using SNMP for most terminal servers (but this is never
necessary when a modern modem or other fixed-rate equipment is attached). Any networked box can serve as a terminal server - even
Win95 - with a simple native server application for that box, but buying an actual firmware based hardware box is much easier.
Furthermore, your Win95 native applications can now share the COM ports (and any attached modems) via a Win95 product called "Dial-out IP" at
http://www.tactical-sw.com/.
If the port exists as a pathname in the filesystem, you can open it as a file and read/write. You can also print text this way by writing to
"prn" or "lpt1" on a pc, and "/dev/something" on Unix. Writing a
formfeed at the end of the file is essential on Windows 95. Here is some sample code:
// class that opens the printer as a file
// and writes "Hello World" to it
import java.io.*;
public class lpt {
public static void main (String[] argv) {
try {
FileOutputStream os = new FileOutputStream("LPT1");
//wrap stream in "friendly" PrintStream
PrintStream ps = new PrintStream(os);
//print text here
ps.println("Hello world!");
//form feed -- this is important
//Without the form feed, the text will simply sit
// in print buffer until something else gets printed.
ps.print("\f");
//flush buffer and close
ps.close();
} catch (Exception e) {
System.out.println("Exception occurred: " + e);
}
}
}
If you wish to change the characteristics of the port (e.g. baud rate, parity, etc.), not just read/write data, Java currently offers no
portable way to do this. You will need to use one of the packages mentioned above or some native code or a system command.
|
|
Q . Is it possible to lock a file using Java ?
|
Ans
:
JDK 1.2 introduces the ability to lock a file (indirectly) using
the File class. Use createTempFile() with delete on exit. Prior releases of Java do not feature an API to lock a file or regions within
a file. Code that needs to do this must take one of four approaches:
1. Implement an advisory locking scheme using features that Java does
have (synchronized methods). This allows you to lock files against
use by other Java code running in the same JVM.
2. Use the atomic operation File.createNewFile(), and mark it as
deleteOnExit(). Have all processes (Java and non-Java) follow the same protocol: if the create operation succeeded, you have the
lock. To give up the lock, you either delete the file or exit the
JVM.
Note that this may fail if the file is remotely mounted using NFS version 2. (There's a window of opportunity bewteen the LOOKUP to
see if it's already there, and the CREATE if it's not). However,
NFS version 3 does guarantee exclusive create of remotely mounted files, and is not subject to this race condition failure.
3. Make calls to native code to issue the locking ioctls. This approach is not portable, but gives you a shot at having your
locks respected by legacy code in non-Java programs using standard
locking ioctls.
4. Push the work to a central server. Since socket connection requests arrive in a single queue on the server, this can be used
to serialize lock requests. There might be some merit in copying the NFS lockd protocol for a general approach. Rolling your own
simple version for a specific application is pretty easy. A database would be better off locking records or fields, not byte
offsets. In theory, the server socket approach would make it easier to perform automatic cleanup of a lock on abrupt VM process
failure, e.g. by asking "are you still alive?" to the lock holder occasionally.
|
|
Q . How do I make the keyboard beep in Java?
|
Ans
:
In JDK 1.1, java.awt.Toolkit has the method beep(). The pre-1.1
alternative of
System.out.print("\07");
System.out.flush();
(the ASCII BEL character) doesn't work on Macs, but does on some other platforms. Java doesn't support the C abstraction of '\a' for an alert
character.
|
|
Q . How do I make I/O faster? My file copy program is slow.
|
Ans
:
This is the purpose of BufferedInputStream. It is a flaw in Java
that buffered I/O is not the default, with a flag or different constructor to turn it off. I/O is the second worst designed package in
Java, after the Date class.
|
|
Q . How do I do formatted I/O of floating point numbers?
|
Ans
:
Use the class java.text.NumberFormat.
Or use http://www.newbie.net/sharky/lava/. Or use Cay Horstmann's http://www.horstmann.com/corejava/Format.java
Although many utilities claim to handle all varieties of C's
printf, as far as has been found, this is the only one to correctly handle the
equivalent of %e in printf.
See also the standard packages java.text.DecimalFormat and java.text.NumberFormat
|
|
Q .
How do I read numbers in exponential format in Java?
|
Ans
:
The program below (written by Steve Chapel) uses StreamTokenizer to
read data from the standard input and recognizes doubles in exponential
format (e.g. -1.23e-45).
import java.io.*;
public class ReadExponential {
public static void main(String argv[]) {
DataInputStream in = new DataInputStream(System.in);
StreamTokenizer st = new StreamTokenizer(in);
try {
while (st.nextToken() != StreamTokenizer.TT_EOF) {
switch (st.ttype) {
case StreamTokenizer.TT_NUMBER:
double num = st.nval;
int exp = 0;
st.ordinaryChars('\0', ' ');
st.nextToken();
st.whitespaceChars('\0', ' ');
if (st.ttype == StreamTokenizer.TT_WORD &&
Character.toUpperCase(st.sval.charAt(0)) == 'E') {
try {
exp = Integer.parseInt(st.sval.substring(1));
} catch (NumberFormatException e) {
st.pushBack();
}
} else if (st.ttype < 0 || st.ttype > ' ')
st.pushBack();
System.out.println("Num " + num * Math.pow(10, exp));
break;
case StreamTokenizer.TT_WORD:
System.out.println("Word " + st.sval);
break;
default:
System.out.println("Char '" + (char) st.ttype + "'");
break;
} // end switch
} // end while
} catch (IOException e) {
System.out.println("IOException: " + e);
}
} // end main
}
|
|
Q . How do I delete a directory in Java?
|
Ans
:
JDK 1.0 did not support directory removal. JDK 1.1 supports
directory removal with the method:
public boolean delete() in class java.io.File
Make sure you don't have any open streams in the directory you're trying to remove. Do a close() on all streams, even if the underlying
file is gone.
|
|
Q . How do I tell how much disk space is free in Java?
|
Ans
:
There currently aren't any good Java APIs for system introspection.
There is no Java way to control processes, or look at system resources.
You can use Runtime.getRuntime().exec() to do "df" on unix or
"dir" on Windows right now.
Alternatively, check out JConfig:
http://www.tolstoy.com/samizdat/jconfig.html
JConfig is a cross-platform library that fills in many of the gaps in the core Java API, and makes it possible to work with files, processes,
file types, video monitors, etc. in a much more Windows- and
Mac-friendly manner.
|
|
Q . How do I get a directory listing of the root directory C:\ on a PC?
|
Ans
:
The obvious approach of calling File.list("C:\"); does not work.
There are two reasons why this fails. First, slash is an escape character in Java, so if you want a literal slash, you have to repeat
it. Second, you need to give the name of the directory, i.e. dot. Putting this together, either of the following calls will work
File.list("C:\\.");
or
File.list("C:/.");
Note: a file separator of "/" works just as well as "\" in most Windows programs and library calls. It is an artifact of DOS's origin's as a
ripped-off port of CP/M. When Microsoft bought the rights to DOS from
Seattle Computer Products (what, you didn't know Microsoft didn't develop DOS? They didn't even own the rights to DOS at the time the
contracted with IBM to supply DOS for the PC) they were buying software
which was an unauthorized port of the CP/M operating system.
CP/M didn't have directories, so it didn't use pathname separators. The forward slash "/" was already used for giving options to CP/M commands,
so "\" was pressed into service as the DOS pathname separator, but the
DOS shell was taught to understood "/" for compatibility with other
OS's.
|
|
Q . I did a read from a Buffered stream, and I got fewer bytes than I specified.
|
Ans
:
This is the way that BufferedInputStream works up to and including
the current release. The behavior is so unintuitive that it really
represents a bug. Javasoft has "resolved" the bug by writing comments
in the program so that the broken behavior is in the range of legal outcomes. Ugh.
When you instantiate a buffered input stream, you can specify the size of buffer it should use. Let's call this the internal buffer. When you
call read() you can say how many bytes to read. Let's call this the
request. If the request is smaller than the internal buffer and not a multiple of the internal buffer, then the last read returns only the
odd bytes left in the internal buffer! The more reasonable and intuitive behavior would be for the internal buffer to be refilled, so
that the whole request can be granted.
For example, if you create a BufferedInputStream with an internal buffer of 1000 bytes, and try to read 512 byte chunks, your first read
will return 512 bytes, but your second read will only return (1000-512), or 488, bytes. (Assuming that the file has at least that
many bytes in it). The following code illustrates the problem.
// troubleshooting by Tov Are Jacobsen
import java.io.*;
class filebug {
public static void main(String args[])
throws FileNotFoundException, IOException {
BufferedInputStream bis =
new BufferedInputStream(new FileInputStream("test.txt"), 1000 );
byte[] buf = new byte[2000];
int numread;
System.out.println( "Available: "+bis.available() );
while (true) {
numread = bis.read(buf,0,512);
if (numread<0) break;
System.out.println( "got "+numread+", avail:"+ bis.available());
}
}
}
Of course, a valid reason for getting less than you asked for is that you asked for more data than is actually available in the Stream, e.g.
you requested 512 bytes from a file that only contains 40 bytes. In
general, there are no guarantees about how much data is returned for a given buffered input stream read request. To avoid this problem, push a
DataInputStream on top of your buffered stream. Then you can call
readFully(), which will do what you want.
A similar "got less than I asked for" occurs when reading a socket. Network protocols frequently packetize data and send it across in
bursts. Nothing is lost of course, and you are always told how many
bytes you actually got. You will get the remaining bytes on a subsequent read. This happens regardless of the language used. Be sure
to check the "amount of data returned" when using the read(byte[],
int, int) method of BufferedInputStream, or when reading from a socket.
Another problem with java.io.InputStream.read(byte[], int, int) is that it catches and ignores IOExceptions. Instead, these exceptions should
be passed on to the caller. Ace programmer Jef Poskanzer, jef@acme.com,
has a version to do this at http://www.acme.com/java/software/Acme.Utils.html. See Jef's read() and
readFully() routines.
|
|
Q .
How do I redirect the System.err stream to a file?
|
Ans
:
You cannot assign a new FileOutputStream to System.err, as it is
final. Instead use the System.setErr() library call, like this:
FileOutputStream err = new FileOutputStream("stderr.log");
PrintStream errPrintStream = new PrintStream(err);
System.setErr(errPrintStream);
This was introduced with JDK 1.1. There is also a corresponding setIn() for redirecting standard in, and a setOut() for standard out.
Note that you will get a compiler warning about a deprecated construct when you do this. PrintStreams are deprecated in favor of
PrintWriters, which handle Unicode properly. The PrintStream is marked as deprecated
by marking all its constructors as deprecated. There is no way to create the PrintStream needed to redirect System.err or System.out
without triggering the deprecated warning.
|
|
Q .
What are the values for the Unicode encoding schemes?
|
Ans
:
The f (or F) suffix directs the compiler to create a float value from a sequence of characters representing a floating
point number (a float literal)-otherwise the compiler would by default create either a double value or an int value.
A literal is a string of characters in a source file that the compiler translates directly to a value of a specific type. The Java
language recognizes literals for integral numbers, floating point numbers, strings, Unicode characters, boolean values, and null.
Table 2.4 shows examples of literals in the Java language, with the types as indicated.
|
|
Q . How do I print from a Java program?
|
Ans
:
Use the Toolkit.getPrintJob() method
Component c = this.getParent();
while (c!=null && !(c instanceof Frame))
c=c.getParent();
// With a JComponent use c=getTopLevelAncestor();
PrintJob pj = getToolkit().getPrintJob((Frame) c, "test", null);
Graphics pg = pj.getGraphics();
printAll(pg);
pg.dispose();
pj.end();
This feature was introduced with JDK 1.1. A common place to put this is in the code that handles a button press. Printing from an untrusted
applet is subject to a check from the SecurityManager.
The JDK 1.1 printing API is more a screen hardcopy facility than a full blown publishing and illustration hardcopy API. JDK 1.2 offers a more
full-featured printing API.
If you simply want to print text, then write it to a file and print the file. Or open a filename that corresponds to the printer. On Windows,
that is "LPT1" and the code looks like:
try {
FileOutputStream fos = new FileOutputStream("LPT1");
PrintStream ps = new PrintStream(fos);
ps.print("Your string goes here");
ps.print("\f");
ps.close();
} catch (Exception e) {
System.out.println("Exception occurred: " + e);
}
The final formfeed is needed by windows to start the printjob.
|
|
Q .
What are the properties that can be used in a PrintJob?
|
Ans
:
The properties are
o awt.print.destination - can be "printer" or "file"
o awt.print.printer - printer name
o awt.print.fileName - name of the file to print
o awt.print.numCopies - obvious
o awt.print.options - options to pass to the print command
o awt.print.orientation - can be "portrait" or "landscape"
o awt.print.paperSize - can be "letter","legal","executive" or "a4"
The defaults are destination=printer, orientation=portrait,
paperSize=letter, and numCopies=1.
You can search for info like this by joining the Java Developer Connection (it's free) at http://java.sun.com/jdc.
and doing a search for "PrintJob".
|
|
Q . How do I get Java talking to a Microsoft Access database?
|
Ans
:
Use the JDBC-ODBC bridge. It is not especially challenging to set
up, but it does require painstaking attention to detail. There is a
step-by-step example in the van der Linden text "Just Java" mentioned
in the sponsorship section of this document.
Note that the Microsoft version of the Java kit does not support JDBC-ODBC access because it uses a non-standard native code interface.
The JDBC FAQ can be found at http://java.sun.com/products/jdbc/jdbc-frequent.html
|
|
Q . How do I do I/O redirection in Java using exec()?
|
Ans
:
This solution works on Unix platforms using either JDK 1.0.2, or
JDK 1.1. The trick is to use an array of Strings for the command line:
String[] command = {"/bin/sh", "-c", "/bin/ls > out.dat"};
If you don't do this, and simply use a single string, the shell will see the -c and /bin/ls and ignore everything else after that. It only
expects a single argument after the -c.
import java.io.*;
import java.util.*;
class IoRedirect {
public static void main(String Argv[]) {
try {
String[] command = {"/bin/sh", "-c", "/bin/ls > out.dat"};
Process p = Runtime.getRuntime().exec(command);
p.waitFor();
System.out.println("return code: "+ p.exitValue());
} catch (IOException e) {
System.err.println("IO error: " + e);
} catch (InterruptedException e1) {
System.err.println("Exception: " + e1.getMessage());
}
}
}
|
|
|