字符流解决中文乱码问题、字符缓冲流提高读写效率、IO流小结、转换流、对象操作流、Properties集合与IO流集合的方法

1. 为什么字节流读取纯文本文件,可能会出现乱码?

  • 计算机中储存的信息都是用二进制数表示的
  • 按照某种规则,将字符存储到计算机中,称为编码
  • 按照同样的规则,将存储在计算机中的二进制数解析显示出来,称为解码
  • 编码和解码的方式必须一致,否则会导致乱码。

 ASCII(美国信息交换标准代码):包括了数字,大小写字符和一些常见的标点符号, ASCII码表中没有中文

GBK:window系统默认的码表,兼容ASCII码表,也包含了21003个汉字,一个中文以两个字节的形式存储

Unicode码表:由国际组织ISO 制定,是统一的万国码,IDEA默认使用UnicodeUTF-8编解码格式。以UTF-8编码后一个中文以三个字节的形式存储

因为字节流一次读一个字节,而不管GBK还是UTF-8一个中文都是多个字节,用字节流每次只能读其中的一部分,所以就会出现乱码问题。

2. 字符串中的编码解码问题

方法名 说明
byte[] getBytes() 使用平台的默认字符集将该 String编码为一系列字节
byte[] getBytes(String charsetName) 使用指定的字符集将该 String编码为一系列字节
String(byte[] bytes) 使用平台的默认字符集解码指定的字节数组来创建字符串
String(byte[] bytes, String charsetName) 通过指定的字符集解码指定的字节数组来创建字符串
import java.io.UnsupportedEncodingException;
import java.util.Arrays;

public class Demo01 {
    public static void main(String[] args) throws UnsupportedEncodingException {
        String str = "落霞与孤鹜齐飞,秋水共长天一色";
        //解码
        byte[] bytes1 = str.getBytes("utf-8");
        byte[] bytes2 = str.getBytes("gbk");
        System.out.println("utf-8:"+ Arrays.toString(bytes1));
        System.out.println("gbk:"+ Arrays.toString(bytes2));
        //编码
        String s1 = new String(bytes1, "utf-8");
        String s2 = new String(bytes2, "gbk");
        System.out.println(s1);
        System.out.println(s2);
    }
}

3. 字符流

字符流在读取纯文本文件时,当遇到中文时会根据码表,读取对应2个字节或者3个字节

  • 对文件进行拷贝,一律使用字节流或者字节缓冲流
  • 把文件中的数据读到内存中打印或者读到内存中运算,使用字符输入流。
  • 把集合,数组,键盘录入等数据写到文件中,使用字符输出流

3.1 字符输出流写数据

Writer: 字符输出流的抽象父类

FileWriter: 字符输出流的常用子类

构造方法:

方法名 说明
FileWriter(File file) 根据给定的 File 对象构造一个 FileWriter 对象
FileWriter(String fileName) 根据给定的文件名构造一个 FileWriter 对象

成员方法:

方法名 说明
void write(int c) 写一个字符
void write(char[] cbuf) 写入一个字符数组
void write(char[] cbuf, int off, int len) 写入字符数组的一部分
void write(String str) 写一个字符串
void write(String str, int off, int len) 写一个字符串的一部分

刷新和关闭的方法:

方法名 说明
flush() 刷新流,之后还可以继续写数据
close() 关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据
import java.io.FileWriter;
import java.io.IOException;

public class Demo02 {
    public static void main(String[] args) throws IOException {
        //字符输出流对象
        FileWriter fileWriter = new FileWriter("day12_exercise\\aaa.txt");
        //一次写一个字符
        int a = 97;
        fileWriter.write(a);//将97对应字符写到aaa.txt
        //一次写一个字符数组的一部分
        char[] chars = {97, 98, 99, 100};
        fileWriter.write(chars,0,2);
        //写一个字符串
        String line = "落霞与孤鹜齐飞";
        fileWriter.write(line);//直接将字符串写到aaa.txt
        //刷新
        fileWriter.flush();
        //释放资源
        fileWriter.close();
    }
}

3.2 字符输入流读数据

Reader: 字符输入流的抽象父类

FileReader: 字符输入流的常用子类

构造方法:

方法名 说明
FileReader(File file) 在给定从中读取数据的 File 的情况下创建一个新 FileReader
FileReader(String fileName) 在给定从中读取数据的文件名的情况下创建一个新 FileReader

成员方法:

方法名 说明
int read() 一次读一个字符数据
int read(char[] cbuf) 一次读一个字符数组数据
import java.io.FileReader;
import java.io.IOException;

public class Demo03 {
    public static void main(String[] args) throws IOException {
        FileReader fileReader = new FileReader("day12_exercise\\aaa.txt");
        //一次读取一个字符
        int cha;
        while ((cha = fileReader.read()) != -1){
            System.out.println((char) cha);
        }
        fileReader.close();
        System.out.println("========================");
        FileReader fileReader1 = new FileReader("day12_exercise\\aaa.txt");
        //一次读取一个字符数组
        int len;
        char[] chars = new char[1024];
        while ((len = fileReader1.read(chars)) != -1){
            String s = new String(chars, 0, len);
            System.out.println(s);
        }
        fileReader1.close();
    }
}

3.3 字符缓冲流

BufferedWriter:将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入。

BufferedReader:从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取。

构造方法:

方法名 说明
BufferedWriter(Writer out) 创建字符缓冲输出流对象
BufferedReader(Reader in) 创建字符缓冲输入流对象

特有方法介绍:

BufferedWriter:void newLine() 写一行行分隔符,行分隔符字符串由系统属性定义(换行)

BufferedReader:String readLine() 读一行文字。 结果包含一行的内容,不包括任何行终止字符如果流的结尾已经到达,则为返回null

案例:使用字符缓冲流读取文件中的数据,排序后再次写到本地文件

import java.io.*;
import java.util.Arrays;

public class Demo02 {
    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(
                new FileReader("day12_exercise\\bbb.txt"));
        //读取一行数据
        String line = br.readLine();
        String[] strArr = line.split(" ");
        int[] intArr = new int[strArr.length];
        for (int i = 0; i < strArr.length; i++) {
            intArr[i] = Integer.parseInt(strArr[i]);
        }
        //排序
        Arrays.sort(intArr);
        //重新写回本地文件
        BufferedWriter bw = new BufferedWriter(
                new FileWriter("day12_exercise\\bbb.txt"));
        for (int i = 0; i < intArr.length; i++) {
            bw.write(intArr[i]+" ");
        }
        bw.flush();
        bw.close();
        br.close();
    }
}

4. IO流小结

字符流解决中文乱码问题、字符缓冲流提高读写效率、IO流小结、转换流、对象操作流、Properties集合与IO流集合的方法_第1张图片

5. 转换流

  • InputStreamReader:是从字节流到字符流的桥梁,父类是Reader。它读取字节,并使用指定的编码将其解码为字符,它使用的字符集可以指定,或者接受平台的默认字符集
  • OutputStreamWriter:是从字符流到字节流的桥梁,父类是Writer。它使用指定的编码将写入的字符编码为字节,它使用的字符集可以指定,或者接受平台的默认字符集
  • JDK11以及以后FileReader可以直接指定

构造方法:

方法名 说明
InputStreamReader(InputStream in) 使用默认字符编码创建InputStreamReader对象
InputStreamReader(InputStream in,String chatset) 使用指定的字符编码创建InputStreamReader对象
OutputStreamWriter(OutputStream out) 使用默认字符编码创建OutputStreamWriter对象
OutputStreamWriter(OutputStream out,String charset) 使用指定的字符编码创建OutputStreamWriter对象
import java.io.*;
import java.nio.charset.Charset;

public class Demo {
    public static void main(String[] args) throws IOException {
        //文件是什么码表,就指定一样的码表去读取
        //字节输入流对象作为参数传入转换流对象
        FileInputStream fis = new FileInputStream("day12_exercise\\dddcopy.txt");
        InputStreamReader isr = new InputStreamReader(fis, "utf-8");
        int ch;
        while ((ch = isr.read()) != -1) {
            System.out.println((char) ch);
        }
        isr.close();

        //使用转换流,指定码表向文件写数据
        //字节输出流对象作为参数传入装换流
        FileOutputStream fos = new FileOutputStream("day12_exercise\\dddcopy.txt");
        OutputStreamWriter osw = new OutputStreamWriter(fos, "utf-8");
        osw.write("秋水共长天一色");
        osw.close();

        //JDK11之后字符流推出了一个新的构造方法,可以指定码表
        FileReader fileReader = new FileReader(
                "day12_exercise\\dddcopy.txt",
                Charset.forName("utf-8"));
        int c;
        while ((c = fileReader.read()) != -1) {
            System.out.println((char) c);
        }
        fileReader.close();
    }
}

6. 对象操作流

对象操作流可以把对象以字节的形式写到本地文件,直接打开文件是读不懂的,需要再次用对象操作流将文件读到内存中。

  • 对象操作输出流(对象序列化流):将对象写到本地文件中,或者在网络中传输对象
  • 对象操作输入流 (对象反序列化流):把写到本地文件中的对象读到内存中,或者接受网络中传输的对象

User类:

import java.io.Serializable;
//如果想要一个类能够被序列化,必须实现接口Serializable
//Serializable是一个标记性接口,没有任何抽象方法
public class User implements Serializable {
    //自定义序列号
    private static final long serialVersionUID = 5586850569152704422L;
    private String name;
    private transient String password;

    public User(String name, String password) {
        this.name = name;
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", password='" + password + '\'' +
                '}';
    }

    public User() {
    }
}

序列化和反序列化:

import java.io.*;

public class Demo01 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        User user = new User("王勃", "twgx");
        //反序列化user对象
        ObjectInputStream ois = new ObjectInputStream(
                new FileInputStream("day12_exercise\\ccc.txt"));
        User o = (User)ois.readObject();
        System.out.println(o);
        ois.close();
    }

    //序列化user对象
    private static void writeObject(User user) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream("day12_exercise\\ccc.txt"));
        oos.writeObject(user);
        oos.close();
    }
}

若不希望某个成员变量被序列化,可以在变量前加一个瞬态关键字 transient

案例:创建多个学生类对象写到文件中,再次读取到内存中

public class Demo{
    /**
     *  read():
     *      读取到文件末尾返回值是 -1
     *  readLine():
     *      读取到文件的末尾返回值 null
     *  readObject():
     *      读取到文件的末尾 直接抛出异常
     *  如果要序列化的对象有多个,不建议直接将多个对象序列化到文件中,因为反序列化时容易出异常
     *      建议: 将要序列化的多个对象存储到集合中,然后将集合序列化到文件中
     */
    public static void main(String[] args) throws Exception {
        // 序列化
        //1.创建序列化流对象
        ObjectOutputStream oos = new ObjectOutputStream(new                 
                           FileOutputStream("myCode\\oos.txt"));
        ArrayList arrayList = new ArrayList<>();
        //2.创建多个学生对象
        Student s = new Student("佟丽娅",30);
        Student s01 = new Student("佟丽娅",30);
        //3.将学生对象添加到集合中
        arrayList.add(s);
        arrayList.add(s01);
        //4.将集合对象序列化到文件中
        oos.writeObject(arrayList);
        oos.close();

        // 反序列化
      	//5.创建反序列化流对象
        ObjectInputStream ois = new ObjectInputStream(new 
                          FileInputStream("myCode\\oos.txt"));
      	//6.将文件中的对象数据,读取到内存中
        Object obj = ois.readObject();
        ArrayList arrayList = (ArrayList)obj;
        ois.close();
        for (Student s : arrayList) {
            System.out.println(s.getName() + "," + s.getAge());
        }
    }
}

7. Properties集合

  • Properties是一个Map体系的集合类
  • Properties有和IO流相关的方法
  • 键及其对应的值通常都用于存储字符串类型

Properties作为Map集合的特有方法:

方法名 说明
Object setProperty(String key, String value) 设置集合的键和值,都是String类型,底层调用 Hashtable方法 put
String getProperty(String key) 使用此属性列表中指定的键搜索属性
Set stringPropertyNames() 从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串

Properties 和IO流结合的方法:

方法名 说明
void load(Reader reader) 从字符输入流读取属性列表(键和元素对)
void store(Writer writer, String comments) 将此属性列表(键和元素对)写入此 Properties表中,以适合使用 load(Reader)方法的格式写入输出字符流

将properties文件中的键值对,存到properties集合

import java.io.FileReader;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Properties;

public class Demo01 {
    public static void main(String[] args) throws IOException {
        Properties prop = new Properties();
        FileReader fr = new FileReader(
                "day12_exercise\\prop.properties", Charset.forName("utf-8"));
        prop.load(fr);
        fr.close();
        System.out.println(prop);
    }
}

将properties集合中的键值对,存到本地配置文件

import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;

public class Demo02 {
    public static void main(String[] args) throws IOException {
        Properties prop = new Properties();
        FileWriter fileWriter = new FileWriter("day12_exercise\\prop.properties");
        prop.put("张三","123456");
        prop.put("李四","123456");
        //"abc为写入文件中的注释"
        prop.store(fileWriter, "abc");
        fileWriter.close();
    }
}

如有错误欢迎留言评论,及时更正。2021年6月11日,羽露风

你可能感兴趣的:(基础,java)