JAVA 复习(Think In Java, 4th) -- File I/O -- part 2

New I/O

在JDK1.4版首次出现的 java.nio.* 包主要是以提高 I/O 速度为目标,之所以能优化 I/O 速度,是因为它的数据结构变得更贴近计算机输入输出的方式:
ChannelByteBuffer,你可以把 Channel 想像成一个煤层,ByteBuffer是煤层和地表间不断运送煤的小推车。在Java 旧的 IO类中,FileInputStream, FileOutputStream, RandomAccessFile 这三类被修改成可以产生 FileChannel类,而 Reader 和 Writer 是不会产生 FileChannel 类的。范例代码如下:

	private static void writeDataWithChannel(String path) {
		FileChannel fo = null;
		FileChannel ra = null;
		FileChannel fi = null;
		try {
			fo = new FileOutputStream(path).getChannel();
			fo.write(ByteBuffer.wrap("lol Test".getBytes()));
			fo.close();

			// /

			ra = new RandomAccessFile(path, "rw").getChannel();
			ra.position(ra.size());
			ra.write(ByteBuffer.wrap(" -some more".getBytes()));
			ra.position(0);
			ByteBuffer bb = ByteBuffer.allocate(1024);
			ra.read(bb);
			bb.flip();
			while (bb.hasRemaining()) {
				System.out.print((char) bb.get());
			}
			System.out.print("\n");
			ra.close();

			// /

			fi = new FileInputStream(path).getChannel();
			ByteBuffer bb2 = ByteBuffer.allocate(2);
			fi.read(bb2);
			bb2.flip();
			while (bb2.hasRemaining()) {
				System.out.print((char) bb2.get());
			}
			System.out.println();
			fi.close();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			try {
				if (fo != null) {
					fo.close();
				}
				if (ra != null) {
					ra.close();
				}
				if (fi != null) {
					fi.close();
				}
			} catch (IOException e) {
				e.printStackTrace();

			}
		}
	}

在读取文件时,我们必须调用 ByteBuffer.allocate()方法,并且用参数指定 ByteBuffer的大小,因为 nio 的设计目标是快速地移动大量的数据,因此 ByteBuffer的大小一般会大一些,1K(1024)可能还是小了些,至於得多大,可能还是得实际去调试,以取得 I/O 速度上的优势。如果我们要再次读取的话,我们需要调用 clear() 将ByteBuffer旧有的数据全部清除。 

	public static void main(String[] args) {
		if (args.length != 2) {
			System.out.println("wrong cmd");
			return;
		}
		copyFile2(args[0], args[1]);
	}

	private static void copyFile2(String originFile, String newFile) {
		FileChannel fi = null;
		FileChannel fo = null;
		try {
			fi = new FileInputStream(originFile).getChannel();
			fo = new FileOutputStream(newFile).getChannel();
			fi.transferTo(0, fi.size(), fo);
			// or fo.transferFrom(in,0,in.size());
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	private static void copyFile(String originFile, String newFile) {
		FileChannel fi = null;
		FileChannel fo = null;
		try {
			fi = new FileInputStream(originFile).getChannel();
			fo = new FileOutputStream(newFile).getChannel();
			ByteBuffer bb = ByteBuffer.allocate(1024);
			while (fi.read(bb) != -1) {
				bb.flip();
				fo.write(bb);
				bb.clear();
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();

		} finally {
			try {
				if (fo != null) {
					fo.close();
				}
				if (fi != null) {
					fi.close();
				}
			} catch (IOException e) {
				e.printStackTrace();

			}
		}

	}
甚至,更方便一点,可以使用 FileChannel 类的 transferTo() transferFrom() 方法就更方便了!看之前的代码,我们必须自 ByteBuffer中逐个读 byte,然後强转为 char ,再打印出来,但实际上,我们有 CharBuffer 类,似乎是能将全部的字符用 toString() 方法打印出来,我们可以看看下面这个例子:

	private static void readFileContent(String filePath2) {
		FileChannel fo = null;
		FileChannel fi = null;
		try {
			fo = new FileOutputStream(filePath2).getChannel();
			fo.write(ByteBuffer.wrap("来玩玩 FileChannel ".getBytes()));

			// /

			fi = new FileInputStream(filePath2).getChannel();
			ByteBuffer bb = ByteBuffer.allocate(1024);
			fi.read(bb);
			bb.flip();
			// doesn't work
			System.out.println("doesn't work: " + bb.asCharBuffer());
			bb.rewind();
			String encoding = System.getProperty("file.encoding");
			System.out.println("System encoding: " + encoding);
			System.out.println("Decoded using: " + encoding + " : "
					+ Charset.forName(encoding).decode(bb));

			fo.write(ByteBuffer.wrap("OMG 试看看".getBytes("UTF-16BE")));

			bb.clear();
			fi.read(bb);
			bb.flip();
			System.out.println(bb.asCharBuffer());
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			try {
				if (fo != null) {
					fo.close();
				}
				if (fi != null) {
					fi.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

	}

在上面的例子中,我们需要注意的是它的编码(encoding)解码(decoding)的一致性,否则会出现乱码的情况,使用 Charset 类可以让我们将字符用许多不同的格式去编码 (encoding) ,此外 ByteBuffer 和网络传输数据一样,是采用 Big- Endian(这边有网上找到有关 little-endian和 big-endian的好文),ByteBuffer可以调用 order() 方法,将其设置为 big-endian 或 little-endian,在这边就不另外说明了~




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值