使用RandomAccessFile API

Working with the RandomAccessFile API


Files can be created and/or opened for random access in which a mixture of write and read
operations can occur until the file is closed. Java supports this random access via its concrete java.

io.RandomAccessFile class.

File可以为随机访问而创建和/或 打开,直到文件关闭之前,写和读操作可以混合出现在随机访问中。Java通过它具体的RandomAccessFile 类支持随机访问。

Note RandomAccessFile has its place in Android app development. For example, you can use this class

to read an app’s raw resource file. To learn how, check out “RandomAccessFile in Android raw resource
file” (http://stackoverflow.com/questions/9335379/randomaccessfile-in-android-raw-

resource-file).

例如,你可以使用这个类来读app的原始资源文件。


RandomAccessFile declares the following constructors:

RandomAccessFile 声明了下列构造器:

 RandomAccessFile(File file, String mode) creates and opens a new file if it
doesn’t exist or opens an existing file. The file is identified by file’s abstract

pathname and is created and/or opened according to mode.


 RandomAccessFile(String pathname, String mode) creates and opens a new
file if it doesn’t exist or opens an existing file. The file is identified by pathname

and is created and/or opened according to mode.


Either constructor’s mode argument must be one of "r", "rw", "rws", or "rwd"; otherwise, the

constructor throws IllegalArgumentException. These string literals have the following meanings:


 "r" informs the constructor to open an existing file for reading only. Any attempt

to write to the file results in a thrown instance of the IOException class.


 "rw" informs the constructor to create and open a new file when it doesn’t exist

for reading and writing or open an existing file for reading and writing.


 "rwd" informs the constructor to create and open a new file when it doesn’t
exist for reading and writing or open an existing file for reading and writing.
Furthermore, each update to the file’s content must be written synchronously to
the underlying storage device.


 "rws" informs the constructor to create and open a new file when it doesn’t
exist for reading and writing or open an existing file for reading and writing.
Furthermore, each update to the file’s content or metadata must be written
synchronously to the underlying storage device.

而且,对文件内容或元数据的每个更新必须被同步写到下层的存储设备。


Note A file’s metadata is data about the file and not actual file contents. Examples of metadata include the
file’s length and the time the file was last modified.

注意 文件的元数据是关于文件的数据而不是实际文件内容。元数据的例子包括文件的长度和文件最后修改的时间。


The "rwd" and "rws" modes ensure than any writes to a file located on a local storage device
are written to the device, which guarantees that critical data isn’t lost when the operating system
crashes. No guarantee is made when the file doesn’t reside on a local device.


Note Operations on a random access file opened in "rwd" or "rws" mode are slower than these same
operations on a random access file opened in "rw" mode.


These constructors throw FileNotFoundException when mode is "r" and the file identified by
pathname cannot be opened (it might not exist or it might be a directory) or when mode is "rw" and
pathname is read-only or a directory.


The following example demonstrates the second constructor by attempting to open an existing file
for read access via the "r" mode string:
RandomAccessFile raf = new RandomAccessFile("employee.dat", "r");


A random access file is associated with a file pointer, a cursor that identifies the location of the next
byte to write or read.
When an existing file is opened, the file pointer is set to its first byte at offset 0.
The file pointer is also set to 0 when the file is created.

随机文件与一个文件指针关联-一个标识要写或读的下一个字节位置的游标。
Write or read operations start at the file pointer and advance it past the number of bytes written or
read. Operations that write past the current end of the file cause the file to be extended. These
operations continue until the file is closed.


RandomAccessFile declares a wide variety of methods. I present a representative sample of these
methods in Table 11-6.

Table 11-6. RandomAccessFile Methods

方法
Method Description


void close() Closes the file and releases any associated platform resources. Subsequent
writes or reads result in IOException. Also, the file cannot be reopened with this
RandomAccessFile object. This method throws IOException when an I/O error
occurs.


FileDescriptor getFD() Returns the file’s associated file descriptor object. This method throws
IOException when an I/O error occurs.

文件描述符对象
long getFilePointer() Returns the file pointer’s current zero-based byte offset into the file. This
method throws IOException when an I/O error occurs.


long length() Returns the length (measured in bytes) of the file. This method throws
IOException when an I/O error occurs.


int read() Reads and returns (as an int in the range 0 to 255) the next byte from the file
or returns -1 when the end of the file is reached. This method blocks when no
input is available and throws IOException when an I/O error occurs.


int read(byte[] b) Reads up to b.length bytes of data from the file into byte array b. This method
blocks until at least 1 byte of input is available. It returns the number of bytes
read into the array, or returns -1 when the end of the file is reached. It throws
NullPointerException when b is null and IOException when an I/O error
occurs.


char readChar() Reads and returns a character from the file. This method reads 2 bytes from
the file starting at the current file pointer. If the bytes read, in order, are b1 and
b2, where 0 <= b1, b2 <= 255, the result is equal to (char) ((b1 << 8) | b2).
This method blocks until the 2 bytes are read, the end of the file is detected,
or an exception is thrown. It throws java.io.EOFException (a subclass of
IOException) when the end of the file is reached before reading both bytes, and
IOException when an I/O error occurs.


int readInt() Reads and returns a 32-bit integer from the file. This method reads 4 bytes from
the file starting at the current file pointer. If the bytes read, in order, are b1, b2,
b3, and b4, where 0 <= b1, b2, b3, b4 <= 255, the result is equal to (b1 << 24)
| (b2 << 16) | (b3 << 8) | b4. This method blocks until the 4 bytes are read,
the end of the file is detected, or an exception is thrown. It throws EOFException
when the end of the file is reached before reading the 4 bytes and IOException
when an I/O error occurs.


void seek(long pos) Sets the file pointer’s current offset to pos (which is measured in bytes from the
beginning of the file). If the offset is set beyond the end of the file, the file’s
length doesn’t change. The file length will only change by writing after the offset
has been set beyond the end of the file. This method throws IOException when
the value in pos is negative or when an I/O error occurs.

void setLength(long newLength)
Sets the file’s length. If the present length as returned by length() is greater
than newLength, the file is truncated. In this case, if the file offset as returned
by getFilePointer() is greater than newLength, the offset will be equal to
newLength after setLength() returns. If the present length is smaller than
newLength, the file is extended. In this case, the contents of the extended
portion of the file are not defined. This method throws IOException when an I/O
error occurs.


int skipBytes(int n) Attempts to skip over n bytes. This method skips over a smaller number of
bytes (possibly zero) when the end of file is reached before n bytes have
been skipped. It doesn’t throw EOFException in this situation. If n is negative,
no bytes are skipped. The actual number of bytes skipped is returned. This
method throws IOException when an I/O error occurs.


void write(byte[] b) Writes b.length bytes from byte array b to the file starting at the current file
pointer position. This method throws IOException when an I/O error occurs.
void write(int b) Writes the lower 8 bits of b to the file at the current file pointer position. This
method throws IOException when an I/O error occurs.


void writeChars(String s) Writes string s to the file as a sequence of characters starting at the current file
pointer position. This method throws IOException when an I/O error occurs.
void writeInt(int i) Writes 32-bit integer i to the file starting at the current file pointer position. The
4 bytes are written with the high byte first. This method throws IOException
when an I/O error occurs.

Most of Table 11-6’s methods are fairly self-explanatory. However, the getFD() method requires
further enlightenment.


Note RandomAccessFile’s read-prefixed methods and skipBytes() originate in the java.
io.DataInput interface, which this class implements. Furthermore, RandomAccessFile’s write-prefixed
methods originate in the java.io.DataOutput interface, which this class also implements.


When a file is opened, the underlying platform creates a platform-dependent structure to represent
the file. A handle to this structure is stored in an instance of the java.io.FileDescriptor class,
which getFD() returns.

当一个文件被打开时,底层平台建立一个平台依赖结构来表示这个文件。这个结构的句柄被存储在 java.io.FileDescriptor类的一个实例(由getFD()返回)中。


Note A handle is an identifier that Java passes to the underlying platform to identify, in this case, a specific
open file when it requires that the underlying platform perform a file operation.

注意 句柄是一个标识符-Java传递到底层平台、一个特定的打开文件要求底层平台完成一个文件操作的情况下,来标识件该文件

FileDescriptor is a small class that declares three FileDescriptor constants named in, out, and
err. These constants let System.in, System.out, and System.err (discussed later in this chapter)
provide access to the standard input, standard output, and standard error streams.


FileDescriptor also declares the following pair of methods:
 void sync() tells the underlying platform to flush (empty) the contents of the
open file’s output buffers to their associated local disk device. sync() returns
after all modified data and attributes have been written to the relevant device.
It throws java.io.SyncFailedException when the buffers cannot be flushed
or because the platform cannot guarantee that all the buffers have been
synchronized with physical media.


 boolean valid() determines whether or not this file descriptor object is valid.
It returns true when the file descriptor object represents an open file or other
active I/O connection; otherwise, it returns false.


Data that is written to an open file ends up being stored in the underlying platform’s output buffers.
When the buffers fill to capacity, the platform empties them to the disk. Buffers improve performance
because disk access is slow.


However, when you write data to a random access file that’s been opened via mode "rwd" or "rws",
each write operation’s data is written straight to the disk. As a result, write operations are slower
than when the random access file is opened in "rw" mode.


Suppose you have a situation that combines writing data through the output buffers and writing data
directly to the disk. The following example addresses this hybrid scenario by opening the file in
mode "rw" and selectively calling FileDescriptor’s sync() method.

假设你有一个情形:包含了通过缓冲区写数据和直接写到磁盘。
RandomAccessFile raf = new RandomAccessFile("employee.dat", "rw");
FileDescriptor fd = raf.getFD();
// Perform a critical write operation.
raf.write(...);
// Synchronize with underlying disk by flushing platform's output buffers to disk.
fd.sync();
// Perform non-critical write operation where synchronization isn't necessary.
raf.write(...);
// Do other work.
// Close file, emptying output buffers to disk.
raf.close();


RandomAccessFile is useful for creating a flat file database, a single file organized into records and
fields.
A record stores a single entry (such as a part in a parts database) and a field stores a single
attribute of the entry (such as a part number).

RandomAccessFile 在建立一个平面文件数据库中是有用的,平面文件被组织成记录和字段。
Note The term field is also used to refer to a variable declared within a class. To avoid confusion with this
overloaded terminology, think of a field variable as being analogous to a record’s field attribute.

A flat file database typically organizes its content into a sequence of fixed-length records. Each
record is further organized into one or more fixed-length fields. Figure 11-1 illustrates this concept in
the context of a parts database.


Figure 11-1. A flat file database of automotive parts is divided into records and fields
According to Figure 11-1, each field has a name (partnum, desc, qty, and ucost). Also, each record
is assigned a number starting at 0. This example consists of five records, of which only three are
shown for brevity.
To show you how to implement a flat file database in terms of RandomAccessFile, I’ve created a
simple PartsDB class to model Figure 11-1. Check out Listing 11-9.
Listing 11-9. Implementing the Parts Flat File Database
import java.io.IOException;
import java.io.RandomAccessFile;
public class PartsDB
{
public final static int PNUMLEN = 20;
public final static int DESCLEN = 30;
public final static int QUANLEN = 4;
public final static int COSTLEN = 4;
private final static int RECLEN = 2 * PNUMLEN + 2 * DESCLEN + QUANLEN + COSTLEN;
private RandomAccessFile raf;
public PartsDB(String pathname) throws IOException
{
raf = new RandomAccessFile(pathname, "rw");
}
public void append(String partnum, String partdesc, int qty, int ucost)
throws IOException
{
raf.seek(raf.length());
write(partnum, partdesc, qty, ucost);
}

public void close()
{
try
{
raf.close();
}
catch (IOException ioe)
{
System.err.println(ioe);
}
}


public int numRecs() throws IOException
{
return (int) raf.length() / RECLEN;
}


public Part select(int recno) throws IOException
{
if (recno < 0 || recno >= numRecs())
throw new IllegalArgumentException(recno + " out of range");
raf.seek(recno * RECLEN);
return read();
}
public void update(int recno, String partnum, String partdesc, int qty,
int ucost) throws IOException
{
if (recno < 0 || recno >= numRecs())
throw new IllegalArgumentException(recno + " out of range");
raf.seek(recno * RECLEN);
write(partnum, partdesc, qty, ucost);
}
private Part read() throws IOException
{
StringBuffer sb = new StringBuffer();
for (int i = 0; i < PNUMLEN; i++)
sb.append(raf.readChar());
String partnum = sb.toString().trim();
sb.setLength(0);
for (int i = 0; i < DESCLEN; i++)
sb.append(raf.readChar());
String partdesc = sb.toString().trim();
int qty = raf.readInt();
int ucost = raf.readInt();
return new Part(partnum, partdesc, qty, ucost);
}

private void write(String partnum, String partdesc, int qty, int ucost)
throws IOException
{
StringBuffer sb = new StringBuffer(partnum);
if (sb.length() > PNUMLEN)
sb.setLength(PNUMLEN);
else
if (sb.length() < PNUMLEN)
{
int len = PNUMLEN - sb.length();
for (int i = 0; i < len; i++)
sb.append(" ");
}
raf.writeChars(sb.toString());
sb = new StringBuffer(partdesc);
if (sb.length() > DESCLEN)
sb.setLength(DESCLEN);
else
if (sb.length() < DESCLEN)
{
int len = DESCLEN - sb.length();
for (int i = 0; i < len; i++)
sb.append(" ");
}
raf.writeChars(sb.toString());
raf.writeInt(qty);
raf.writeInt(ucost);
}


public static class Part
{
private String partnum;
private String desc;
private int qty;
private int ucost;
public Part(String partnum, String desc, int qty, int ucost)
{
this.partnum = partnum;
this.desc = desc;
this.qty = qty;
this.ucost = ucost;
}


String getDesc()
{
return desc;
}
String getPartnum()
{
return partnum;
}

int getQty()
{
return qty;
}
int getUnitCost()
{
return ucost;
}
}
}
PartsDB first declares constants that identify the lengths of the string and 32-bit integer fields. It then
declares a constant that calculates the record length in terms of bytes. The calculation takes into
account the fact that a character occupies 2 bytes in the file.
These constants are followed by a field named raf that is of type RandomAccessFile. This field is
assigned an instance of the RandomAccessFile class in the subsequent constructor, which creates/
opens a new file or opens an existing file because of "rw".
PartsDB next declares append(), close(), numRecs(), select(), and update(). These methods
append a record to the file, close the file, return the number of records in the file, select and return a
specific record, and update a specific record.
The    append() method first calls length() and seek(). Doing so ensures that the
file pointer is positioned to the end of the file before calling the private write()
method to write a record containing this method’s arguments.
 RandomAccessFile’s close() method can throw IOException. Because this is a
rare occurrence, I chose to handle this exception in PartDB’s close() method,
which keeps that method’s signature simple. However, I print a message when
IOException occurs.
The    numRecs() method returns the number of records in the file. These records
are numbered starting with 0 and ending with numRecs() - 1. Each of the
select() and update() methods verifies that its recno argument lies within this
range.
The    select() method calls the private read() method to return the record
identified by recno as an instance of the nested Part class. Part’s constructor
initializes a Part object to a record’s field values, and its getter methods return
these values.
The    update() method is equally simple. As with select(), it first positions the
file pointer to the start of the record identified by recno. As with append(), it calls
write() to write out its arguments but replaces a record instead of adding one.
Records are written with the private write() method. Because fields must have exact sizes, write()
pads String-based values that are shorter than a field size with spaces on the right and truncates
these values to the field size when needed.
Records are read via the private read() method. read() removes the padding before saving a
String-based field value in the Part object.

By itself, PartsDB is useless. You need an application that lets you experiment with this class, and
Listing 11-10 fulfills this requirement.
Listing 11-10. Experimenting with the Parts Flat File Database
import java.io.IOException;
public class UsePartsDB
{
public static void main(String[] args)
{
PartsDB pdb = null;
try
{
pdb = new PartsDB("parts.db");
if (pdb.numRecs() == 0)
{
// Populate the database with records.
pdb.append("1-9009-3323-4x", "Wiper Blade Micro Edge", 30, 2468);
pdb.append("1-3233-44923-7j", "Parking Brake Cable", 5, 1439);
pdb.append("2-3399-6693-2m", "Halogen Bulb H4 55/60W", 22, 813);
pdb.append("2-599-2029-6k", "Turbo Oil Line O-Ring ", 26, 155);
pdb.append("3-1299-3299-9u", "Air Pump Electric", 9, 20200);
}
dumpRecords(pdb);
pdb.update(1, "1-3233-44923-7j", "Parking Brake Cable", 5, 1995);
dumpRecords(pdb);
}
catch (IOException ioe)
{
System.err.println(ioe);
}
finally
{
if (pdb != null)
pdb.close();
}
}
static void dumpRecords(PartsDB pdb) throws IOException
{
for (int i = 0; i < pdb.numRecs(); i++)
{
PartsDB.Part part = pdb.select(i);
System.out.print(format(part.getPartnum(), PartsDB.PNUMLEN, true));
System.out.print(" | ");
System.out.print(format(part.getDesc(), PartsDB.DESCLEN, true));
System.out.print(" | ");
System.out.print(format("" + part.getQty(), 10, false));
System.out.print(" | ");
String s = part.getUnitCost() / 100 + "." + part.getUnitCost() % 100;
if (s.charAt(s.length() - 2) == '.') s += "0";

System.out.println(format(s, 10, false));
}
System.out.println("Number of records = " + pdb.numRecs());
System.out.println();
}
static String format(String value, int maxWidth, boolean leftAlign)
{
StringBuffer sb = new StringBuffer();
int len = value.length();
if (len > maxWidth)
{
len = maxWidth;
value = value.substring(0, len);
}
if (leftAlign)
{
sb.append(value);
for (int i = 0; i < maxWidth-len; i++)
sb.append(" ");
}
else
{
for (int i = 0; i < maxWidth-len; i++)
sb.append(" ");
sb.append(value);
}
return sb.toString();
}
}
Listing 11-10’s main() method begins by instantiating PartsDB, with parts.db as the name of the
database file. When this file has no records, numRecs() returns 0 and several records are appended
to the file via the append() method.
main() next dumps the five records stored in parts.db to the standard output stream, updates the
unit cost in the record whose number is 1, once again dumps these records to the standard output
stream to show this change, and closes the database.
Note I store unit cost values as integer-based penny amounts. For example, I specify literal 1995 to
represent 1995 pennies, or $19.95. If I were to use java.math.BigDecimal objects to store currency
values, I would have to refactor PartsDB to take advantage of object serialization, and I’m not prepared to do
that right now. (I discuss object serialization later in this chapter.)
main() relies on a dumpRecords() helper method to dump these records, and dumpRecords() relies
on a format() helper method to format field values so that they can be presented in properly aligned
columns—I could have used java.util.Formatter (see Chapter 13) instead. The following output
reveals this alignment:

1-9009-3323-4x | Wiper Blade Micro Edge | 30 | 24.68
1-3233-44923-7j | Parking Brake Cable | 5 | 14.39
2-3399-6693-2m | Halogen Bulb H4 55/60W | 22 | 8.13
2-599-2029-6k | Turbo Oil Line O-Ring | 26 | 1.55
3-1299-3299-9u | Air Pump Electric | 9 | 202.00
Number of records = 5
1-9009-3323-4x | Wiper Blade Micro Edge | 30 | 24.68
1-3233-44923-7j | Parking Brake Cable | 5 | 19.95
2-3399-6693-2m | Halogen Bulb H4 55/60W | 22 | 8.13
2-599-2029-6k | Turbo Oil Line O-Ring | 26 | 1.55
3-1299-3299-9u | Air Pump Electric | 9 | 202.00
Number of records = 5
And there you have it: a simple flat file database. Despite its lack of support for advanced database
features such as indexes and transaction management, a flat file database might be all that your
Android application requires.
Note To learn more about flat file databases, check out Wikipedia’s “Flat file database” entry
(http://en.wikipedia.org/wiki/Flat_file_database).

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值