输入与输出-2.3读写二进制数据

2.3 读写二进制数据

2.3.1 DataInput 和DataOutput 接口

DataOutput 接口定义了下面用于以二进制格式写数组、字符、boolean值和字符串的方法:

writerChars
writerByte
writerInt
writerShort
writerLong
writerFloat
writerDouble
writerChar
writerBoolean
writerUTF

例如,writeInt总是将一个整数写出为4字节的二进制数量值,而不管它有多少位,writeDouble总是将一个double值写出为8字节的二进制数量值。这样产生的结果并非人可阅读的,但是对于给定类型的每个值,所需的空间都是相同的,而且将其读回也比解析文本要更快。

writeUTF方法使用修订版的8为Unicode转换格式写出字符串。这种方式与直接使用标准的UTF-8编码方式不同,其中,Unicode码元首先用UTF-16表示,其结果之后使用UTF-8规则进行编码。修订后的编码方式对于编码大于0xFFFF的字符的处理方式有所不同,这是为向后兼容在Unicode还没有超过16位时构建的虚拟机。

因为没有其他方法会使用UTF-8的这种修订,所以你应该只在写出用于java虚拟机的字符串时才使用writerUTF方法,例如,当你需要编写一个生成字节的程序时。对于其他场合,都应该使用writeChars。

为了读回数据,可以适用在DataInput接口中定义的下列方法:

readInt
readShort
readLong
readFloat
readDouble
readChar
readBoolean
readUTF

DataInputStream类实现了DataInput接口,为了从文件中读入二进制数据,可以将DataInputStream与某个字节源相组合,例如FileInputStream:

DataInputStream in= new DataInputStream(new FileInputStream("employee.dat"));

与此类似,想要写出二进制数据,你可以使用实现了DataOutput接口的DataOutputStream类:

DataOutputStream out = new DataOutputStream(new FileOutputStream("employee.dat"));

2.3.2 随机访问文件

RandomAccessFile类可以在文件中的任何位置查找或写入数据。磁盘文件都是随机访问的,但是与网络套接字通信的输入输出流却不是。可以打开一个随机访问文件,只用于读入或者同时用于读写,可以通过使用字符串“r”(用于读入访问)或“rw”(用于读入/写出访问)作为构造器的第二个参数来制定这个选项。

RandomAccessFile in= new RandomAccessFile("employee.dat","r");
RandomAccessFile inout = new RandomAccessFile("employee.dat","rw");

当你将已有文件作为RandomAccessFile打开时,这个文件并不会被删除。

随机访问文件有一个表示下一个将被读入或写出的字节所处位置的文件指针,seek方法可以用来将这个文件指针设置到文件中的任一字节位置,seek的参数是一个long类型的整数,它的值位于0到文件按照字节来度量的长度之间。

getFilePointer方法将返回文件指针的当前位置。

RandomAccessFile类同时实现了DataInput和DataOutput接口。

现在剖析一个将雇员记录存储到随机访问文件中的示例程序,其中每条记录都拥有相同的大小,这样可以很容易地读入任何记录。假设希望将文件指针置于第三条记录处,那么只需要将文件指针置于恰当的字节位置,然后就可以开始读入了。

long n = 3;
in.seek((n-1)*RECORD_SIZE);
Employee e = new Employee();
e.readData(in);

如果你希望修改记录,然后将其存回到相同的位置,那么请切记要将文件指针回到这条记录的开始处:

in.seek((n-1)*RECORD_SIZE);
e.weiteData(out);

要确定文件中的字节总数,可以使用length方法,而记录的总数则是用字节总数除以每条记录的大小。

long nbytes = in.length();
int nrecords = (int)(nbytes/RECORD_SIZE);

整数和浮点数在二进制格式下都具有固定的尺寸,但是在处理字符串时就有些麻烦了,因此提供了两个助手方法来读写固定尺寸的字符串。

writeFixedString写出从字符串开头开始的指定数量的码元(如果码元过少,该方法将用值来补齐字符串)。

public static void writeFixedString(String s, int size, DataOutput out)throw IOException
{
	for(int i=0; i<size;i++)
	{
		char ch = 0;
		if(i<s.length())
			ch = s.charAt(i);
		out.writeChar(ch);
	}
}

readFixedString方法从输入流中读入字符。直至读入SIZE个码元,或者直至遇到具有值得字符值,然后跳过字段中剩余的值。为了提高效率,这个方法使用了StringBuilder类来读入字符串。

public static void readFixedString(int size, DataInput in)throw IOExeception
{
	StringBuilder b = new StringBuilder(size);
	int i =0 ;
	boolean more = true;
	while(more && i<size)
	{
		char ch = in.readChar();
		i++;
		if(ch == 0) more = flase;
		else b.append(ch);
	}
	in.skipBytes(2*(size-i));
	return b.toString();
}

我们将writeFixedString 和 readFixedString方法方法到了DataIO助手类的内部。

为了写出一条固定尺寸的记录,我们直接以二进制方式写出所有的字段:

DataIO.writeFixedString(e.getName(),Employee.NAME_SIZE,out);
out.writeDouble(e.getSalary());
LocalData hireDay = e.getHireDay();
out.writeInt(hireDay.getYear());
out.writeInt(hireDay.getMonthValue());
out.writeInt(hireDay.getDayOfMonth);

读回数据也很简单:

String name = DataIO.readFixedString(Employee.Name_SIZE,in);
double salary = in. readDouble();
int y = in.readInt();
int m = in.readInt();
int d = in.readInt();

计算每条记录大小:将使用40个字符来表示姓名字符串,因此,每条记录包含100个字节:

  • 40字符 = 80字节,用于姓名。
  • 1 double = 8字节, 用于薪水。
  • 3 int = 12字节, 用于日期。

所示程序将三条记录写到了一个数据文件中,然后以逆序将它们从文件中读回。为了高效的执行,这里需要是用随机访问,因此需要首先读入第三条数据。

2.3.3 ZIP文档

ZIP文档(通常)以压缩格式存储一个或多个文件,每个ZIP文档都有一个头,包含诸如每个文件名字和所使用得压缩方法等信息。在Java中,可以使用ZipInputStream来读入ZIP文档。可能需要浏览文档中每个单独的项, getNextEntry方法就可以返回一个描述这些项的ZipEntry对象。向ZipInputStream的getInputStream方法传递该项可以获取用于读取该项的输入流。然后调用closeEntry来读入下一项。下面是典型的通读ZIP文件的代码序列:

ZipInputStream zin = new ZipInputStream(new FileInputStream(zipName));
ZipEntry entry;
while((entry = zin.getNextEntry())!=null)
{
	InputStream in = zin.getInputStream(entry);
	// read the contents of in 
	 zin.closeEntry();
}
zin.close();

要写出到ZIP文件,可以使用ZipOutputStream,而对于希望放到ZIP文件中的每一项,都应该创建一个ZipEntry对象,并将文件名传递给ZipEntry的构造器,她将设置其他诸如文件日期和解压缩方法等参数。如果需要,可以覆盖这些设置。然后需要调用ZipOutputStream的putNextEntry方法来开始写出新文件,并将文件数据发送到ZIP输出流中。当完成时,需要调用closeEntry。然后,对所有希望存储的文件都重复这个工作。

FileOutputStream fout = new FileOutputStream("test.zip");
ZipOutputStream zout = new ZipOutputStream(fout);
for all files
{
	ZipEntry ze = new ZipEntry(fileName);
	zout.putNextEntry(ze);
	//send data to zout
	zout.closeEntry();
}
zout.close();

JAR文件只是带有一个特殊项的ZIP文件,这个项称做清单。可以使用JarInputStream和JarOutputStream类来读写清单项。

猜你喜欢

转载自blog.csdn.net/z036548/article/details/84439091