NIO在文件处理方面的应用

旧i/o库的FileInputStream,FileOutputStream,RandomAccessFile被修改,可以返回FileChannel;
3种管道 可读,可写,可读可写

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class GetChannel {
    private static final int BSIZE = 1024;
    public static void main(String...args)throws Exception{
        //向文件写入
        //可写
        FileChannel fc=
                new FileOutputStream("data.txt").getChannel();
        fc.write(ByteBuffer.wrap("some text ".getBytes()));
        fc.close();
        //可读可写
        fc = new RandomAccessFile("data.txt","rw").getChannel();
        fc.position(fc.size());
        fc.write(ByteBuffer.wrap("Some more".getBytes()));
        fc.close();
        //从文件读入
        //可读
        //只读访问 必须使用allocate进行分配
        fc = new FileInputStream("data.txt").getChannel();
        ByteBuffer buff = ByteBuffer.allocate(BSIZE);
        fc.read(buff);
        buff.flip();//prepare to write
        //buff.get() 返回byte型
        //hasRemaining()
        //Tells whether there are any elements between the current position and the limit.
        while(buff.hasRemaining())
            System.out.print((char)buff.get());
    }
}

调用wrap产生"数组支持的ByteBuffer"(出自Java编程思想)
将已存在的Byte数组包装成ByteBuff,作为ByteBuff的存储器。
对于只读访问,必须显示调用allocate分配ByteBuffer。
一旦调用read,就必须调用flip()为后面的write做准备:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class ChannelCopy {
    private static final int BSIZE = 1024;
    public static void main(String...args) throws Exception{
        FileChannel in,out;
        ByteBuffer buff;
        in = new FileInputStream("data1.txt").getChannel();
        out = new FileOutputStream("data2.txt").getChannel();
        buff = ByteBuffer.allocate(BSIZE);
        //返回-1表示文件结尾
        while(in.read(buff)!=-1){
            buff.flip();
            out.write(buff);
            buff.clear();
        }
    }
}

现在回头看第一段代码,这里的get方法用到了强制类型转换:(char)get(),后面将介绍一种方式避免。
对于第二段的代码可以采用通道连接(暂时不了解,将书上的代码贴一下)

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.channels.FileChannel;
//通道直接相连
public class TransferTo {
    private final static String s1 = "";
    private final static String s2 = "";
    public static void main(String...args) throws Exception{
        FileChannel in = new FileInputStream(s1).getChannel(),
                out = new FileOutputStream(s2).getChannel();
        in.transferTo(0,in.size(),out);
        //or
        //out.transferFrom(in,0,in.size());
    }
}

好了,开始介绍如何进行数据的转换,也就是不用使用强制类型转换
获取基本类型

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;

public class BufferToText {
    //输出时解码 或 输入时转码
    private static final int BSIZE = 1024;
    public static void main(String...args) throws Exception{
        FileChannel fc =
                new FileOutputStream("data2.txt").getChannel();
        fc.write(ByteBuffer.wrap("some text".getBytes()));
        fc.close();

        fc = new FileInputStream("data2.txt").getChannel();
        ByteBuffer buff = ByteBuffer.allocate(BSIZE);
        fc.read(buff);
        buff.flip();
        //Doesn't work
        System.out.println(buff.asCharBuffer());
        buff.rewind();//回到文件开头

        //使用默认字符集进行decode
        String encoding = System.getProperty("file.encoding");//发现默认字符集
        System.out.println("Decoded using " + encoding + ": "
        + Charset.forName(encoding).decode(buff));//解码 输出正常
        //使用UTF-16BE写入
        fc = new FileOutputStream("data2.txt").getChannel();
        fc.write(ByteBuffer.wrap("Some text".getBytes("UTF-16BE")));
        fc.close();

        fc = new FileInputStream("data2.txt").getChannel();
        buff.clear();
        fc.read(buff);
        buff.flip();
        System.out.println("UTF-16BE:"+buff.asCharBuffer());
        //以CharBuffer写入
        fc = new FileOutputStream("data2.txt").getChannel();
        buff = ByteBuffer.allocate(20);//只需要9个字符,这里使用20,也就是10个字符的大小,会多出来一个字符“ ”
        buff.asCharBuffer().put("Some text");//写入
        fc.write(buff);
        fc.close();

        fc = new FileInputStream("data2.txt").getChannel();
        buff.clear();
        fc.read(buff);
        buff.flip();
        System.out.println(buff.asCharBuffer());
    }
}

补充代码

import java.nio.charset.Charset;
import java.util.*;

public class AviliableCharSets {
    public static void main(String...args){
        SortedMap<String, Charset> charSets =
                Charset.availableCharsets();
        Iterator<String> it = charSets.keySet().iterator();
        while(it.hasNext()){
            String csname = it.next();
            System.out.print(csname);
            Iterator aliases =
                    //aliases Returns a set containing this charset's aliases.(别名)
                    charSets.get(csname).aliases().iterator();
            if(aliases.hasNext())
                System.out.print(": ");
            while(aliases.hasNext()){
                System.out.print(aliases.next());
                if(aliases.hasNext())
                    System.out.print(", ");
            }
            System.out.println();
        }
    }
}

第一段会产生乱码
第二代运用了默认的读写,产生正确输出
第三段使用UTF-16BE也产生了正确输出
第四段buff.asCharBuffer().put("Some text");//写入
也产生正确输出
可以通过asCharBuffer来读取char
可以用一系列近似的方法读取基本类型

import java.nio.ByteBuffer;

public class GetData {
    //基本类型的获取
    private static final int BSIZE = 1024;
    public static void main(String...args){
        ByteBuffer bb = ByteBuffer.allocate(BSIZE);
        int i = 0;
        while(i++<bb.limit())
            if(bb.get()!=0)
                System.out.println("nonzero");
        System.out.println("i = " + i);
        bb.rewind();
        bb.asCharBuffer().put("howdy!");
        char c;
        while((c = bb.getChar())!=0)
            System.out.print(c);
        System.out.println();
        bb.rewind();

        bb.asShortBuffer().put((short)471142);//必须进行强制类型转换
        System.out.println(bb.getShort());//输出:12390  越界
        bb.rewind();

        bb.asIntBuffer().put(99471142);
        System.out.println(bb.getInt());
        bb.rewind();

        bb.asLongBuffer().put(4471142);
        System.out.println(bb.getLong());
        bb.rewind();

        bb.asFloatBuffer().put(99471142);
        System.out.println(bb.getFloat());
        bb.rewind();

        bb.asDoubleBuffer().put(99471142);
        System.out.println(bb.getDouble());
        bb.rewind();
    }
}

分配一个ByteBuffer,它会将你的内容自动置0,这里检测1024个值,由limit()方法决定,后面则是对基本类型的读取测试。

视图缓冲器

import java.nio.ByteBuffer;
import java.nio.IntBuffer;

public class IntBufferDemo {
    private static final int BSIZE = 1024;
    public static void main(String...args){
        ByteBuffer bb = ByteBuffer.allocate(BSIZE);
        IntBuffer ib = bb.asIntBuffer();
        //在IntBuffer 上操作 ByteBuffer
        ib.put(new int[]{1,2,3,4,5});
        System.out.println(ib.get(3));
        ib.put(3,18);
        ib.flip();
        while(ib.hasRemaining()){
            int i = ib.get();
            System.out.println(i);
        }
    }
}

例子

import java.nio.*;

public class ViewBuffers {
    public static void main(String...args){
        ByteBuffer bb = ByteBuffer.wrap(new byte[]{0,0,0,0,0,0,0,'a'});
        bb.rewind();

        System.out.print("Byte Buffer ");
        while(bb.hasRemaining())
            System.out.print(bb.position() + "->" + bb.get() + " ");
        System.out.println();

        CharBuffer cb = ((ByteBuffer)bb.rewind()).asCharBuffer();
        System.out.print("Char Buffer ");
        while(cb.hasRemaining())
            System.out.print(cb.position() + "->" + cb.get() + " ");
        System.out.println("-----");

        IntBuffer ib = ((ByteBuffer)bb.rewind()).asIntBuffer();
        System.out.print("Int Buffer ");
        while(ib.hasRemaining())
            System.out.print(ib.position() + "->" + ib.get() + " ");
        System.out.println("-----");

        FloatBuffer fb = ((ByteBuffer)bb.rewind()).asFloatBuffer();
        System.out.print("Float Buffer ");
        while(fb.hasRemaining())
            System.out.print(fb.position() + "->" + fb.get() + " ");
        System.out.println("-----");

        ShortBuffer sb = ((ByteBuffer)bb.rewind()).asShortBuffer();
        System.out.print("Short Buffer ");
        while(sb.hasRemaining())
            System.out.print(sb.position() + "->" + sb.get() + " ");
        System.out.println("-----");

        LongBuffer lb = ((ByteBuffer)bb.rewind()).asLongBuffer();
        System.out.print("Long Buffer ");
        while(lb.hasRemaining())
            System.out.print(lb.position() + "->" + lb.get() + " ");
        System.out.println("-----");

        DoubleBuffer db = ((ByteBuffer)bb.rewind()).asDoubleBuffer();
        System.out.print("Double Buffer ");
        while(db.hasRemaining())
            System.out.print(db.position() + "->" + db.get() + " ");
        System.out.println("-----");
    }
}

byte 1字节
int 4字节
char 2字节
short 2字节
float 4字节
long 4字节
double 8字节
大于一个字节的就要考虑ByteBuffer的读取顺序
改变读取顺序 会影响到上面的视图缓冲器的输出
一般是高位优先

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;

public class Endians {
    public static void main(String...args){
        ByteBuffer bb = ByteBuffer.wrap(new byte[12]);
        bb.asCharBuffer().put("abcdef");
        System.out.println(Arrays.toString(bb.array()));
        bb.rewind();
        //big_endian 高位优先
        bb.order(ByteOrder.BIG_ENDIAN);
        bb.asCharBuffer().put("abcdef");
        System.out.println(Arrays.toString(bb.array()));
        bb.rewind();
        //little_endian 低位优先
        bb.order(ByteOrder.LITTLE_ENDIAN);
        bb.asCharBuffer().put("abcdef");
        System.out.println(Arrays.toString(bb.array()));
        /*
          [0, 97, 0, 98, 0, 99, 0, 100, 0, 101, 0, 102]
          [0, 97, 0, 98, 0, 99, 0, 100, 0, 101, 0, 102]
          [97, 0, 98, 0, 99, 0, 100, 0, 101, 0, 102, 0]
        */
    }
}

缓冲器操纵数据

import java.nio.ByteBuffer;
import java.nio.CharBuffer;

public class UsingBuffers {
    //交换每两个字母的顺序 "user"  "us"互换  "er"互唤  输出 "sure"
    private static void symmetricScramble(CharBuffer buffer){
        while(buffer.hasRemaining()){
            //将mark设为position
            buffer.mark();//不加这句话的话报java.nio.InvalidMarkException 错误来源:reset()方法
            //相对的get() put()方法 调用后改变position
            //绝对的get(int i) put(int i) 填写需要的索引i 调用后不改变position
            char c1 = buffer.get();
            char c2 = buffer.get();
            buffer.reset();//使position回到mark 事先调mark() 此时回到get()方法前的位置
            buffer.put(c2).put(c1);//交换c1,c2位置 此时position再次回到2位置
        }
    }
    public static void main(String...args){
        char[]data = "UsingBuffers".toCharArray();//转换成数组
        ByteBuffer bb = ByteBuffer.allocate(data.length*2);//char 2个字节
        CharBuffer cb = bb.asCharBuffer();
        cb.put(data);
        System.out.println(cb.rewind());
        symmetricScramble(cb);
        System.out.println(cb.rewind());
        symmetricScramble(cb);
        System.out.println(cb.rewind());
    }
}

rewind()调用后 position为0 limit,capacity指向最后一位,mark的位置变得不确定(来自Java编程思想)
书上对ByteBuffer的方法解释
在这里插入图片描述

内存映射文件

假定可以把整个文件放入内存
使对文件的修改更简单

import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
//内存映射文件
public class LargeMappedFiles {
    static int length = 0x8ffffff;//128MB
    public static void main(String...args) throws Exception{
        MappedByteBuffer out =
                //MappedByteBuffer特殊类型的直接缓冲器
                //映射文件的初始位置和映射区域的长度必须写
                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));
    }
}

使用get(int i); 就可以访问文件指定位置的数据
非常简单

(未完待续)

发布了23 篇原创文章 · 获赞 4 · 访问量 847

猜你喜欢

转载自blog.csdn.net/qq_43656529/article/details/100861353