今天看啥  ›  专栏  ›  江湖非良人

IO操作深入

江湖非良人  · 简书  ·  · 2019-07-30 17:03

字符编码

  在实际开发中,常用的编码有如下几种:

  • GBK/GB2312:国标编码,可以描述中文信息,其中GB2312只描述简体中文,而GBK包含简体中文和繁体中文;
  • ISO8859-1:国际通用编码:可以用其描述所有的字母信息,如果是象形文字,则需要进行编码处理;
  • UNICODE编码:采用十六进制的方式,可以描述所有文字信息;
  • UTF编码:象形文字部分使用UNICODE编码,而普通的字母采用 ISO8859-1编码,它的优势在于适合快速的传输,节约带宽,也就成为了开发中首选的编码,主要使用“UTF-8”编码。
      如果要想知道当前系统中支持的编码规则,则可以采用如下代码列出全部的本机属性。
    范例:列出本机属性
public class JavaApiDemo {
    public static void main(String[] args) throws Exception {
        System.getProperties().list(System.out);
    }
}
  • 文件的路径分割符:file.separator=/;
  • 文件默认编码:file.encoding=UTF-8;
      也就是说如果现在什么都不设置,则采用的编码就是UTF-8;
    范例:编写程序
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
public class JavaApiDemo {
    public static void main(String[] args) throws Exception {
        OutputStream output=new FileOutputStream(new File("/Users/fzc/Documents/mydir/mldn.txt"));
        output.write("中华人民共和国万岁".getBytes());
        output.close();
    }
}

范例:强制性设置编码

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
public class JavaApiDemo {
    public static void main(String[] args) throws Exception {
        OutputStream output=new FileOutputStream(new File("/Users/fzc/Documents/mydir/mldn.txt"));
        output.write("中华人民共和国万岁".getBytes("utf-8"));
//        output.write("中华人民共和国万岁".getBytes("ISO8859-1"));//乱码
        output.close();
    }
}

  项目中出现的乱码问题就是编码和解码标准不统一,而解决乱码最常用的方式就是所有的编码都采用“utf-8”。

内存操作流

  在之前使用的全部都是文件操作流,文件操作流的特点:程序利用InputStream读取文件内容,而后程序利用OutputStream向文件输出内容,所有的操作都是以文件为终端的。

文件流

  如果说现在需要实现IO操作,又不希望产生文件(临时文件),则就可以以内存为终端进行处理,这个时候的流程为:

内存流

  在Java中提供提供了两类的内存操作流:

ByteArrayOutputStream
ByteArrayInputStream
CharArrayWriter
CharArrayReader

  下面使用ByteArrayOutputStream和ByteArrayInputStream类为主进行内存的使用分析,首先分析各自的构造方法:

  • ByteArrayInputStream构造:public ByteArrayInputStream​(byte[] buf);
  • ByteArrayOutputStream构造:public ByteArrayOutputStream();
      在ByteArrayOutputStream类中有两个很重要的方法,这两个个方法可以获取全部保存在内存流中的数据信息:
  • 获取数据:public byte[] toByteArray();
  • 使用字符串的形式获取:public String toString();
    范例:利用内存流实现一个小写字母转大写字母的操作
import java.io.*;
public class JavaApiDemo {
    public static void main(String[] args) throws Exception {
        String str="www.baidu.com";
        InputStream input=new ByteArrayInputStream(str.getBytes());//将数据保存在内存流
        OutputStream output=new ByteArrayOutputStream();//读取内存中的数据
        int data=0;
        while ((data=input.read())!=-1){//每次读取一个字节
            output.write(Character.toUpperCase((char)data));//保存数据
        }
        System.out.println(output.toString());//WWW.BAIDU.COM
        input.close();
        output.close();
    }
}

  如果不希望只是以字符串的形式返回,因为可能存放的是其他二进制的数据,那么此时就可以利用OutputStream的子类ByteArrayOutputStream的扩展功能获取全部字节数据。

import java.io.*;
public class JavaApiDemo {
    public static void main(String[] args) throws Exception {
        String str="www.baidu.com";
        InputStream input=new ByteArrayInputStream(str.getBytes());//将数据保存在内存流
        ByteArrayOutputStream output=new ByteArrayOutputStream();//读取内存中的数据
        int data=0;
        while ((data=input.read())!=-1){//每次读取一个字节
            output.write(Character.toUpperCase((char)data));//保存数据
        }
        byte[] result=output.toByteArray();//自己处理字节数据
        System.out.println(new String(result));//WWW.BAIDU.COM
        input.close();
        output.close();
    }
}

  在最初的时候,可以利用ByteArrayOutputStream实现大规模文本文件读取。

管道流

  管道流主要功能是实现两个线程之间的IO处理操作。

管道流

  对于管道流也分为两类

  • 字节管道流PipedOutputStreamPipedInputStream
      - 连接处理:public void connect​(PipedInputStream snk) throws IOException;
  • 字符管道流PipedWriterPipedReader
      - 连接处理:public void connect​(PipedReader snk) throws IOException;
PipedOutputStream
PipedInputStream
PipedWriter
PipedReader

范例:实现管道操作

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
public class JavaApiDemo {
    public static void main(String[] args) throws Exception {
        SendThread send=new SendThread();
        ReceiveThread receive=new ReceiveThread();
        send.getOutput().connect(receive.getInput());//进行管道连接
        new Thread(send,"消息发送线程").start();
        new Thread(receive,"消息接收线程").start();
        /**
         * 【第1次信息发送 - 消息发送线程】www.baidu.com
         * 【第2次信息发送 - 消息发送线程】www.baidu.com
         * 【第3次信息发送 - 消息发送线程】www.baidu.com
         * 【第4次信息发送 - 消息发送线程】www.baidu.com
         * 【第5次信息发送 - 消息发送线程】www.baidu.com
         * 【第6次信息发送 - 消息发送线程】www.baidu.com
         * 【第7次信息发送 - 消息发送线程】www.baidu.com
         * 【第8次信息发送 - 消息发送线程】www.baidu.com
         * 【第9次信息发送 - 消息发送线程】www.baidu.com
         * 【第10次信息发送 - 消息发送线程】www.baidu.com
         */
    }
}
class SendThread implements Runnable{
    private PipedOutputStream output;//管道输出流

    public SendThread() {
        this.output = new PipedOutputStream();//实例化管道输出流
    }
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            try {
                output.write(("【第"+(i+1)+"次信息发送 - "+Thread.currentThread().getName()+"】www.baidu.com\n").getBytes());
            }catch (IOException e){
                e.printStackTrace();
            }
        }
        try {
            this.output.close();
        }catch (IOException e){
            e.printStackTrace();
        }
    }
    public PipedOutputStream getOutput(){
        return output;
    }
}
class ReceiveThread implements Runnable{
    private PipedInputStream input;

    public ReceiveThread() {
        input=new PipedInputStream();
    }
    @Override
    public void run() {
        byte[] data = new byte[1024];
        int len = 0;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();//所有的数据保存到内存输出流
        try {
            while ((len = input.read(data)) != -1) {
                bos.write(data, 0, len);
            }
            System.out.println(new String(bos.toByteArray()));
            bos.close();
            input.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public PipedInputStream getInput() {
        return input;
    }
}

  管道中一个负责发送,一个负责接收,中间靠一个管道连接。

RandomAccessFile

  对于文件内容的处理操作主要通过InputStream(Reader)、OutputStream(Writer)来实现,但是利用这些类实现的内容读取,只能够将数据部分部分读取进来,如果说现在有这样一种需求。
  现在有一个20G大小的文件,如果按照传统的IO操作进行读取和分析很明显不能很好的完成,所以这种情况下在java.io包中提供了一个RandomAccessFile类,这个类可以实现文件的跳跃式的读取,可以只读取中间的部分内容(前提:需要有一个完善的保存形式),数据的保存位数要确定好。

  RandomAccessFile类中定义有如下的操作方法:

  • 构造方法:
      public RandomAccessFile​(File file, String mode) throws FileNotFoundException
      - 文件处理模式:r、rw;
    范例:实现文件的保存
import java.io.File;
import java.io.RandomAccessFile;
public class JavaApiDemo {
    public static void main(String[] args) throws Exception {
        String dirPath = "/Users/fzc/Documents/mydir/mldn_save.txt";//定义操作的文件
        File file = new File(dirPath);
        RandomAccessFile raf=new RandomAccessFile(file,"rw");//读写模式
        String[] names=new String[]{"zhangsan","wangwu  ","lisi    "};
        int[] ages=new int[]{30,20,16};
        for (int i = 0; i < names.length; i++) {
            raf.write(names[i].getBytes());
            raf.writeInt(ages[i]);
        }
        raf.close();
    }
}

  RandomAccessFile最大的特点是在于数据的读取处理上,因为所有的数据是按照固定的长度进行保存的,所以读取时就可以进行跳字节读取:
  向下跳:public int skipBytes​(int n) throws IOException;
  向上跳:public void seek​(long pos) throws IOException;
范例:读取数据

import java.io.File;
import java.io.RandomAccessFile;
public class JavaApiDemo {
    public static void main(String[] args) throws Exception {
        String dirPath = "/Users/fzc/Documents/mydir/mldn_save.txt";//定义操作的文件
        File file = new File(dirPath);
        RandomAccessFile raf=new RandomAccessFile(file,"rw");//读写模式
       //读取"lisi"数据,跳过24位
        raf.skipBytes(24);
        byte[] data=new byte[8];
        int len=raf.read(data);
        System.out.println("姓名:"+new String(data,0,len)+"、年龄:"+raf.readInt());//姓名:lisi    、年龄:16
        //读取"wangwu"数据,回跳到12位
        raf.seek(12);
         data=new byte[8];
         len=raf.read(data);
        System.out.println("姓名:"+new String(data,0,len)+"、年龄:"+raf.readInt());//姓名:wangwu  、年龄:20
        //读取"zhangsan"数据,回跳到0位
        raf.seek(0);
        data=new byte[8];
        len=raf.read(data);
        System.out.println("姓名:"+new String(data,0,len)+"、年龄:"+raf.readInt());//姓名:zhangsan、年龄:30
    }
}

  整体的使用之中由用户自行定义要读取的位置,而后按照指定的结构进行数据的读取。




原文地址:访问原文地址
快照地址: 访问文章快照