NIO入门系列之第3章:从理论到实践:NIO 中的读和写

3.1  概述

读和写是 I/O 的基本过程。从一个通道中读取很简单:只需创建一个缓冲区,然后让通道将数据读到这个缓冲区中。写入也相当简单:创建一个缓冲区,用数据填充它,然后让通道用这些数据来执行写入操作。

在本节中,我们将学习有关在Java 程序中读取和写入数据的一些知识。我们将回顾 NIO 的主要组件(缓冲区、通道和一些相关的方法),看看它们是如何交互以进行读写的。在接下来的几节中,我们将更详细地分析这其中的每个组件以及其交互。

3.2  从文件中读取

在我们第一个练习中,我们将从一个文件中读取一些数据。如果使用原来的 I/O,那么我们只需创建一个FileInputStream 并从它那里读取。而在 NIO中,情况稍有不同:我们首先从FileInputStream 获取一个FileInputStream 对象,然后使用这个通道来读取数据。

在 NIO 系统中,任何时候执行一个读操作,您都是从通道中读取,但是您不是直接从通道读取。因为所有数据最终都驻留在缓冲区中,所以您是从通道读到缓冲区中。

因此读取文件涉及三个步骤:(1)从FileInputStream 获取 Channel,(2) 创建 Buffer,(3) 将数据从 Channel 读到 Buffer 中。

现在,让我们看一下这个过程。

3.3  三个容易的步骤

第一步是获取通道。我们从 FileInputStream 获取通道:

FileInputStream fin = new FileInputStream( "readandshow.txt" );
FileChannel fc = fin.getChannel();

下一步是创建缓冲区:

ByteBuffer buffer = ByteBuffer.allocate( 1024 );

最后,需要将数据从通道读到缓冲区中,如下所示:

fc.read( buffer );

您会注意到,我们不需要告诉通道要读多少数据到缓冲区中。每一个缓冲区都有复杂的内部统计机制,它会跟踪已经读了多少数据以及还有多少空间可以容纳更多的数据。我们将在缓冲区内部细节中介绍更多关于缓冲区统计机制的内容。

// ReadAndShow
package com.mzsx.nio;
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
public class ReadAndShow {
    static public void main(String args[]) throws Exception {
        //创建FileInputStream实例
        FileInputStream fin = new FileInputStream("C:\\Users\\Qiang\\Desktop\\hydra.txt");
        //获取通道
        FileChannel fc = fin.getChannel();
        //创建缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        //将数据从通道读到缓冲区中
        fc.read(buffer);
        //反转
        buffer.flip();
        int i = 0;
        while (buffer.remaining() > 0) {
            byte b = buffer.get();
            System.out.println("Character " + i + ": " + ((char) b)+" , buffer.remaining():"+buffer.remaining());
            i++;
        }
        fin.close();
    }
}

3.4  写入文件

在 NIO 中写入文件类似于从文件中读取。首先从 FileOutputStream 获取一个通道:

FileOutputStream fout = new FileOutputStream( "writesomebytes.txt" );
FileChannel fc = fout.getChannel();

下一步是创建一个缓冲区并在其中放入一些数据- 在这里,数据将从一个名为message 的数组中取出,这个数组包含字符串"Some bytes" 的 ASCII 字节(本教程后面将会解释buffer.flip() 和 buffer.put() 调用)。

ByteBuffer buffer = ByteBuffer.allocate( 1024 );
for (int i=0; i<message.length; ++i) {
     buffer.put( message[i] );
}
buffer.flip();

最后一步是写入缓冲区中:

fc.write( buffer );

注意在这里同样不需要告诉通道要写入多数据。缓冲区的内部统计机制会跟踪它包含多少数据以及还有多少数据要写入。

// WriteSomeBytes
package com.mzsx.nio;
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
public class WriteSomeBytes {
    static private final byte message[] = { 83, 111, 109, 101, 32, 98, 121,
            116, 101, 115, 46 };
    static public void main(String args[]) throws Exception {
        //实例化FileOutputStream
        FileOutputStream fout = new FileOutputStream("C:\\Users\\Qiang\\Desktop\\writesomebytes.txt");
        //获取通道
        FileChannel fc = fout.getChannel();
        //获取缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        //向缓冲区添加数据
        for (int i = 0; i < message.length; ++i) {
            buffer.put(message[i]);
        }
        buffer.flip();
        //写入缓冲区
        fc.write(buffer);
        fout.close();
    }
}

3.5  读写结合

下面我们将看一下在结合读和写时会有什么情况。我们以一个名为 CopyFile.java 的简单程序作为这个练习的基础,它将一个文件的所有内容拷贝到另一个文件中。CopyFile.java 执行三个基本操作:首先创建一个 Buffer,然后从源文件中将数据读到这个缓冲区中,然后将缓冲区写入目标文件。这个程序不断重复—读、写、读、写—直到源文件结束。

CopyFile 程序让您看到我们如何检查操作的状态,以及如何使用 clear() 和 flip() 方法重设缓冲区,并准备缓冲区以便将新读取的数据写到另一个通道中。

3.6  运行 CopyFile 例子

实例读写结合:

// CopyFile
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
public class CopyFile {
    static public void main(String args[]) throws Exception {
        //需要输入源文件夹和目标文件的全路径
        if (args.length < 2) {
            System.err.println("Usage: java CopyFile infile outfile");
            System.exit(1);
        }
        String infile = args[0];
        String outfile = args[1];
        FileInputStream fin = new FileInputStream(infile);
        FileOutputStream fout = new FileOutputStream(outfile);
        //获得通道
        FileChannel fcin = fin.getChannel();
        FileChannel fcout = fout.getChannel();
        //创建缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        while (true) {
            //清空缓冲区
            buffer.clear();
            //将数据从通道读到缓冲区中
            int r = fcin.read(buffer);
            //检查状态
            if (r == -1) {
                break;
            }
            //反转
            buffer.flip();
            //写入
            fcout.write(buffer);
        }
        fcin.close();
        fcout.close();
        fin.close();
        fout.close();
    }
}

因为缓冲区会跟踪它自己的数据,所以CopyFile 程序的内部循环 (inner loop) 非常简单,如下所示:

fcin.read( buffer );
fcout.write( buffer );

第一行将数据从输入通道fcin 中读入缓冲区,第二行将这些数据写到输出通道 fcout 。

3.7  检查状态

下一步是检查拷贝何时完成。当没有更多的数据时,拷贝就算完成,并且可以在 read() 方法返回 -1 是判断这一点,如下所示:

int r = fcin.read( buffer );
if (r==-1) {
     break;
}

3.8  重设缓冲区

最后,在从输入通道读入缓冲区之前,我们调用 clear() 方法。同样,在将缓冲区写入输出通道之前,我们调用 flip() 方法,如下所示:

buffer.clear();
int r = fcin.read( buffer );
if (r==-1) {
     break;
}
buffer.flip();
fcout.write( buffer );

clear() 方法重设缓冲区,使它可以接受读入的数据。 flip() 方法让缓冲区可以将新读入的数据写入另一个通道。

NIO入门系列之第3章:从理论到实践:NIO 中的读和写

时间: 2024-08-05 21:56:05

NIO入门系列之第3章:从理论到实践:NIO 中的读和写的相关文章

NIO入门系列之第5章:关于缓冲区的更多内容

第5章 关于缓冲区的更多内容 5.1  概述 到目前为止,您已经学习了使用缓冲区进行日常工作所需要掌握的大部分内容.我们的例子没怎么超出标准的读/写过程种类,在原来的 I/O中可以像在 NIO 中一样容易地实现这样的标准读写过程. 本节将讨论使用缓冲区的一些更复杂的方面,比如缓冲区分配.包装和分片.我们还会讨论 NIO 带给 Java 平台的一些新功能.您将学到如何创建不同类型的缓冲区以达到不同的目的,如可保护数据不被修改的只读缓冲区,和直接映射到底层操作系统缓冲区的直接缓冲区.我们将在本节的最

NIO入门系列之第6章:分散和聚集

第6章 分散和聚集 6.1  概述 分散/聚集 I/O 是使用多个而不是单个缓冲区来保存数据的读写方法. 一个分散的读取就像一个常规通道读取,只不过它是将数据读到一个缓冲区数组中而不是读到单个缓冲区中.同样地,一个聚集写入是向缓冲区数组而不是向单个缓冲区写入数据. 分散/聚集 I/O 对于将数据流划分为单独的部分很有用,这有助于实现复杂的数据格式. 6.2  分散/聚集 I/O 通道可以有选择地实现两个新的接口: ScatteringByteChannel 和 GatheringByteChan

NIO入门系列之第7章:文件锁定

第7章 文件锁定 7.1  概述 文件锁定初看起来可能让人迷惑.它似乎指的是防止程序或者用户访问特定文件.事实上,文件锁就像常规的 Java 对象锁-它们是劝告式的(advisory)锁.它们不阻止任何形式的数据访问,相反,它们通过锁的共享和获取赖允许系统的不同部分相互协调. 您可以锁定整个文件或者文件的一部分.如果您获取一个排它锁,那么其他人就不能获得同一个文件或者文件的一部分上的锁.如果您获得一个共享锁,那么其他人可以获得同一个文件或者文件一部分上的共享锁,但是不能获得排它锁.文件锁定并不总

NIO入门系列之第4章:缓冲区内部细节

4.1  概述 本节将介绍 NIO 中两个重要的缓冲区组件:状态变量和访问方法 (accessor). 状态变量是前一节中提到的"内部统计机制"的关键.每一个读/写操作都会改变缓冲区的状态.通过记录和跟踪这些变化,缓冲区就可能够内部地管理自己的资源. 在从通道读取数据时,数据被放入到缓冲区.在有些情况下,可以将这个缓冲区直接写入另一个通道,但是在一般情况下,您还需要查看数据.这时使用访问方法 get() 来完成的.同样,如果要将原始数据放入缓冲区中,就要使用访问方法 put(). 在本

NIO入门系列之第8章:连网和异步 I/O

8.1  概述 连网是学习异步 I/O 的很好基础,而异步 I/O 对于在 Java 语言中执行任何输入/输出过程的人来说,无疑都是必须具备的知识.NIO 中的连网与 NIO 中的其他任何操作没有什么不同--它依赖通道和缓冲区,而您通常使用InputStream 和 OutputStream来获得通道. 本节首先介绍异步 I/O 的基础-它是什么以及它不是什么,然后转向更实用的.程序性的例子. 8.2  异步 I/O 异步 I/O 是一种没有阻塞地读写数据的方法.通常,在代码进行 read()

NIO入门系列之第9章:字符集

9.1  概述 根据 Sun 的文档,一个 Charset 是"十六位 Unicode 字符序列与字节序列之间的一个命名的映射".实际上,一个 Charset 允许您以尽可能最具可移植性的方式读写字符序列. Java 语言被定义为基于 Unicode.然而在实际上,许多人编写代码时都假设一个字符在磁盘上或者在网络流中用一个字节表示.这种假设在许多情况下成立,但是并不是在所有情况下都成立,而且随着计算机变得对 Unicode 越来越友好,这个假设就日益变得不能成立了. 在本节中,我们将看

NIO入门系列之第一章:输入/输出:概念性描述

第1章 输入/输出:概念性描述 1.1  I/O 简介 I/O 或者输入/输出指的是计算机与外部世界或者一个程序与计算机的其余部分的之间的接口.它对于任何计算机系统都非常关键,因而所有 I/O 的主体实际上是内置在操作系统中的.单独的程序一般是让系统为它们完成大部分的工作. 在 Java 编程中,直到最近一直使用流的方式完成 I/O.所有 I/O 都被视为单个的字节的移动,通过一个称为 Stream 的对象一次移动一个字节.流 I/O 用于与外部世界接触.它也在内部使用,用于将对象转换为字节,然

NIO入门系列之第二章:通道和缓冲区

第2章 通道和缓冲区 2.1  概述 通道和缓冲区是 NIO 中的核心对象,几乎在每一个I/O 操作中都要使用它们. 通道是对原 I/O 包中的流的模拟.到任何目的地(或来自任何地方)的所有数据都必须通过一个 Channel 对象.一个 Buffer 实质上是一个容器对象.发送给一个通道的所有对象都必须首先放到缓冲区中:同样地,从通道中读取的任何数据都要读到缓冲区中. 2.2  什么是缓冲区? Buffer 是一个对象,它包含一些要写入或者刚读出的数据.在 NIO 中加入 Buffer 对象,体

Jenkins入门系列之——02第二章 Jenkins安装与配置

2014-12-08:已不再担任SCM和CI的职位,Jenkins的文章如无必要不会再维护. 写的我想吐血,累死了. 网页看着不爽的,自己去下载PDF.有问题请留言! Jenkins入门系列之--03PDF文档下载 第二章 Jenkins安装与配置 2 Jenkins安装 在最简单的情况下,Jenkins 只需要两个步骤: 1.下载最新的版本(一个 WAR 文件).Jenkins官方网址: http://Jenkins-ci.org/ 2.运行 java -jar jenkins.war 注意: