java编程思想笔记(十五)I/O高级
1.内存映射文件读取
当要读取的文件内容太大时,使用内存映射文件来读取,性能就会很好,因为它是通过抽取文件内容的一部分映射到内存中,就可以在java里做逻辑处理了。
import java.nio.*; import java.nio.channels.*; import java.io.*; pulbic class MemoryMappedFile{ //十六进制,128MB大小 static int length = 0x8FFFFFF; public static void main(String[] args)throws Exception{ //通过文件通道将文件映射为内存中的自己缓冲区 MappedByteBuffer out = new RandomAccessFile(“test.dat”, “rw”).getChannel() .map(FileChannel.MapMode.READ_WRITE, 0, length); for(int i = 0; i < length; i++){ out.put((byte)’x’); } System.out.println(“Finished writing”); for(int I = length/2; i < length/2 + 6; i++){ System.out.println((char)out.get(i)); } } }MappedByteBuffer是一个特殊的直接缓冲器,我们要用的话必须指定映射文件的初始位置和映射区域的长度,去拿到这位置的内容。
这里使用RandomAccessFile类实现以下三种模式将文件区域映射到内存中:
(1).只读:视图修改得到的缓冲区将导致抛出ReadOnlyBufferException。
(2).读/写:对得到的缓冲区的更改将最终传播到文件,该更改对映射到同一文件的其他程序不一定是可见的。
(3).专用:对的到的缓冲区更改将不会被传播到文件,并且该更改对映射到同一文件的其他程序也是不可见的,相反,会创建缓冲区已修改部分的专用副本。
注意:映射关系一经创建,就不再依赖于创建它时所用的文件通道,特别是关闭该通道对映射关系的有效性没有任何影响。另外,从性能观点来讲,通常相对较大的文件映射到内存中才是值得的。
2.文件加锁
当前线程如果给文件加锁,其它线程就会无法读取该文件(只限JVM中),这样就能保证资源同步,做到数据安全。
import java.nio.channels.*; import java.util.concurrent.*; import java.io.*; public class FileLocking{ public static void main(String[] args)throws Exception{ FileOutputStream fos = new FileOutputStream(“file.txt”); //试图对文件通道的文件锁定 FileLock fl = fos.getChannel().tryLock(); //文件锁定成功 if(fl != null){ System.out.println(“Locked File”); TimeUnit.MILLISECONDS.sleep(100); //释放文件锁 fl.release(); System.out.println(“Released Lock”); } foc.close(); } }
输出结果:
Locked File
Released Lock
文件通道的tryLock()方法尝试获取对此通道的文件给定区域的锁定,是个非阻塞方法,无论是否已成功获得请求区域的锁定,调用总是立即返回。如果由于另一个程序保持这一个重叠锁而无法锁定,则此方法返回null.
文件锁定方法:
(1).FileLock tryLock():
尝试获取对此通道文件的独占锁定。
(2).FileLock tryLock(long position, long size, Boolean shared):
尝试获取对此通道文件给定区域的锁定。
(3).FileLock lock():
获取此通道的文件的独占锁定。
(4).FileLock lock(long position, long size, Boolean shared):
获取此通道的文件的给定区域锁定。
文件锁定方法是共享锁还是排斥锁取决于底层操作系统,可以使用FileLock.isShared()方法判断使用的是何种类型的文件锁。
锁定文件部分区域并修改 案例:
import java.nio.*; import java.nio.channels.*; import java.io.*; public class LockingMappedFiles { static final int LENGTH = 0x8FFFFFF; // 128 MB static FileChannel fc; public static void main(String[] args) throws Exception { fc = new RandomAccessFile("test.dat", "rw").getChannel(); MappedByteBuffer out = fc .map(FileChannel.MapMode.READ_WRITE, 0, LENGTH); for (int i = 0; i < LENGTH; i++) out.put((byte) 'x'); new LockAndModify(out, 0, 0 + LENGTH / 3); new LockAndModify(out, LENGTH / 2, LENGTH / 2 + LENGTH / 4); } private static class LockAndModify extends Thread { private ByteBuffer buff; private int start, end; LockAndModify(ByteBuffer mbb, int start, int end) { this.start = start; this.end = end; mbb.limit(end); mbb.position(start); buff = mbb.slice(); start(); } public void run() { try { // 锁定文件部分区域 FileLock fl = fc.lock(start, end, false); System.out.println("Locked: " + start + " to " + end); int i = 0; // 修改改区域内容 while (buff.position() < buff.limit() - 1){ buff.put((byte) (buff.get() + 1)); if(i++<5) System.out.print(buff.get(buff.position()-1)+" "); } System.out.println(); fl.release(); System.out.println("Released: " + start + " to " + end); } catch (IOException e) { throw new RuntimeException(e); } } } }
3.压缩/解压缩:
Java I/O类库中提供了一些关于压缩和加压的类,由于压缩和解压缩算法是针对字节数据进行操作的,因此javaI/O中关于压缩和加压素的类是继承自InputStream和OutputStream字节流体系。
Java压缩和解压的相关类在java.util.zip包下,具体的类如下:
需要维护所读取数据校验和的输入流,校验和可用于验证输入数据的完整性。
(2).CheckedOutputStream:
需要维护所写入数据校验和的输出流。
(3).Deflater:
使用流行的”ZLIB”压缩程序库为通用压缩提供支持。
(4).Inflater:
使用流行的”ZLIB”压缩程序库为通用解压缩提供支持。
(5).DeflaterInputStream:
为压缩“deflate“格式压缩数据实现输入流过滤器。
(6).DeflaterOutputStream:
为压缩 “deflate“格式压缩数据实现输出流过滤器,它还用作其他类型的压缩过滤器(如GZIPOutputStream)的基础。
(7).InflaterInputStream:
为解压缩”deflate”压缩格式的数据实现输入流过滤器,它还用作其他解压缩过滤器(如GZIPInputStream)的基础。
(8).InfaterOutputStream:
为解压缩“deflate”压缩格式存储数据实现输出流过滤器。
(9).ZipOutputStream:
为以”zip”文件格式写入文件实现输出流过滤器,包括对已压缩和未压缩条目的支持。
(10).ZipInputStream:
为读取以”zip”文件格式的文件实现输入流过滤器,包括对已压缩和未压缩条目的支持。
(11).GZIPOutputStram:
为使用“GZIP“文件格式写入压缩数据实现输出流过滤器。
(12).GZIPInputStram:
为读取“GZIP“文件格式的压缩数据实现输入流过滤器。
GZIP对单个文件的压缩和提取 例子:
import java.util.zip.*; import java.io.*; public class GZIPCompress{ public static void main(String[] args)throws IOException{ BufferedReader in = new BufferedReader(new FileReader(“test.dat”)); BufferedOutputStream out = new BufferedOutputStream(new GZIPOutputStream( new FileOutputStream(“test.gz”))); int c; //写GZIP格式压缩文件 while((c = in.read()) != -1){ out.write(c); } in.close(); out.close(); BufferedReader in2 = new BufferedReader(new InputStreamReader( new GZIPInputStream(new FileInputStream(“test.gz”)))); String s; //读取GZIP格式压缩文件 while((s = in2.readLine()) != null){ System.out.println(s); } in2.close(); } }
4.使用zip I/O 对多文件压缩:
zip格式的压缩文件是最常用的压缩方式,使用zip多文件压缩时,可以将多个文件压缩在一个压缩包中,同时还可以从一个包含多个文件的压缩包中读取所有的压缩文件。使用zip进行多文件压缩时,一般要使用CheckSum类计算校验和,校验和的计算有两种算法:
(1).Adler32:速度比较快。
(2).CRC32:速度比较慢,但是更精确。
使用zip多文件压缩的例子如下:
import java.util.zip.*; import java.io.*; import java.util.*; public class ZipCompress{ public static void main(String[] args)throws Exception{ FileOutputStream f = new FileOutputStream(“test.zip”); //使用Adler32算法为文件输入流产生输出校验和文件 CheckedOutputStream csum = new CheckedOutputStream(f, new Adler32()); ZipOutputStream zos = new ZipOutputStream(csum); BufferedOutputStream out = new BufferedOutputStream(zos); //设置zip文件注释 zos.setComment(“A test of java zipping”); //向zip压缩文件写入多个文件 for(String arg : args){ System.out.println(“Writing file:” + arg); BufferedReader in = new BufferedReader(new FileReader(arg)); //写入一个zip文件条目,并将流定位到条目数据的开始处 zos.putNextEntry(new ZipEntry(arg)); int c; //写入zip文件内容 while((c = in.read()) != -1){ out.write(c); } in.close(); out.flush(); } out.close(); //文件关闭后获取校验和 System.out.println(“Checksum:” + csum.getChecksum().getValue()); FileInputStream fi = new FileInputStream(“test.zip”); //使用Adler32算法为输入文件流产生输入校验和文件流 CheckedInputStream csumi = new CheckedInputStream(fi, new Adler32()); ZipInputStream in2 = new ZipInputStream(csumi); BufferedInputStream bis = new BufferedInputStream(in2); ZipEntry ze; //读取zip文件条目 While((ze = in2.getNextEntry()) != null){ System.out.println(“Reading file:” + ze); int x; //读取zip文件条目内容 while((x = bis.read()) != -1){ System.out.println(x); } } if(args.length == 1){ System.out.println(“Checksum:” + csumi.getChecksum().getValue()); } bis.close(); //另一种读取zip文件的方法 ZipFile zf = new ZipFile(“test.zip”); //获取zip文件的条目 Enumeration e = zf.entries(); while(e.hasMoreElements()){ ZipEntry ze2 = (ZipEntry)e.nextElement(); System.out.println(“Reading File:” + ze2); } } }