×

Java总结IO第一篇之字符流与字符编码

96
张风捷特烈 Excellent
2018.10.09 22:22* 字数 610

零、前言

Java总结IO四篇:

名称
第零篇 Java总结IO之总集篇
第一篇 Java总结IO第一篇之字符流与字符编码
第二篇 Java总结IO第二篇之字节流与字符流转化
第三篇 Java总结IO第三篇之File类和Properties类
第四篇 Java总结IO第四篇之其他IO流对象

1.字符流只能操作文本
2.本质底层依然是使用字节操作,只不过坐了一层封装
3.字符流是由Java虚拟机将字节转化为2个字节的Unicode字符为单位的字符而成的,对多国语言支持性比较好
4.讲述一下字符编码的相关知识。

一、字符流之FileWriter和FileReader

1.字符流之FileWriter的使用

注:new FileWriter(fileName,true) 追加模式添加数据

FileWriter fileWriter = null;
try {
    String fileName = "I:\\Java\\Base\\Thinking\\src\\IOTest\\FileWriter.txt";
    //1.创建一个输出流--写出文件:需明确被操作文件----文件不存在,会自动创建文件(同名覆盖)
    fileWriter = new FileWriter(fileName);
    //2.写入数据
    fileWriter.write("Line1 第一行\r\n");
    //3.刷新流中的缓冲
    fileWriter.flush();
    fileWriter.write("Line2 第二行");
    //获取文件编码格式
System.out.println(fileWriter.getEncoding());//UTF8
} catch (IOException e) {
    e.printStackTrace();
} finally {
    //4.关闭流也会刷新缓冲
    try {
        //不为空,才能关闭流
        if (fileWriter != null) {
            fileWriter.close();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}
FileWriter.png

2.字符流之FileReader的使用

注:为了简单起见,将FileWriter.txt中的Line2 第二行删除

public class Client {
    public static void main(String[] args) {
        String fileName = "I:\\Java\\Base\\Thinking\\src\\IOTest\\FileWriter.txt";
        try {
            //1.创建一个输入流--读取文件
            FileReader fileReader = new FileReader(fileName);
            //2.读取字符
            
            //readOneByOne(fileReader);//一个一个读
            //readOneByWhile(fileReader);//循环一个一个读
            //readByCharArray(fileReader);//字符数组读取
            //readByCharArrayByWhile(fileReader);///字符数组循环读取
            
            fileReader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
1).read()方法,一次读一个字符
private static void readOneByOne(FileReader fileReader) throws IOException {
    int ch1 = fileReader.read();
    System.out.println(ch1 + "=" + (char) ch1);//76=L
    int ch2 = fileReader.read();
    System.out.println(ch2 + "=" + (char) ch2);//105=i
    int ch3 = fileReader.read();
    System.out.println(ch3 + "=" + (char) ch3);//110=n
    int ch4 = fileReader.read();
    System.out.println(ch4 + "=" + (char) ch4);//101=e
    int ch5 = fileReader.read();
    System.out.println(ch5 + "=" + (char) ch5);//49=1
    int ch6 = fileReader.read();
    System.out.println(ch6 + "=" + (char) ch6);//32=
    int ch7 = fileReader.read();
    System.out.println(ch7 + "=" + (char) ch7);//31532=第
    int ch8 = fileReader.read();
    System.out.println(ch8 + "=" + (char) ch8);//19968=一
    int ch9 = fileReader.read();
    System.out.println(ch9 + "=" + (char) ch9);//34892=行
    int ch10 = fileReader.read();
    System.out.println(ch10 + "=" + (char) ch10);//-1 //结束标志
}
read.png
2).循环读取

可见一个一个读十分麻烦,既然有结束标识,每次read()都会走到下一个位置,很像迭代器模式

 private static void readOneByWhile(FileReader fileReader) throws IOException {
     int ch = 0;
     while ((ch = fileReader.read()) != -1) {
         System.out.println(ch + "=" + (char) ch);
     }
 }

结果

76=L
105=i
110=n
101=e
49=1
32= 
31532=第
19968=一
34892=行
3).使用字符数组读取
private static void readByCharArray(FileReader fileReader) throws IOException {
    //字符数组
    char[] buf = new char[8];
    int len = fileReader.read(buf);
    System.out.println("len=" + len + "---->" + new String(buf));
    //len=8---->Line1 第一
    char[] buf2 = new char[8];
    int len2 = fileReader.read(buf2);
    System.out.println("len=" + len2 + "---->" + new String(buf2));
    //len=3---->行\r\n
    char[] buf3 = new char[8];
    int len3 = fileReader.read(buf3);
    System.out.println("len=" + len3 + "---->" + new String(buf3));
    //len=-1---->读完后len为-1
}
4).使用字符数组循环读取
private static void readByCharArrayByWhile(FileReader fileReader) throws IOException {
    //字符数组循环读取
    char[] buf = new char[8];
    int len = 0;
    while ((len = fileReader.read(buf)) != -1) {
        System.out.println(new String(buf, 0, len));
        //Line1 第一
        //行
    }
}
read.png

二、使用字符流FileWriter和FileReader拷贝文件

/**
 * 作者:张风捷特烈
 * 时间:2018/10/9 0009:12:47
 * 邮箱:1981462002@qq.com
 * 说明:将 I:\Java\Base\Thinking\src\IOTest\Activity.md
 *      拷贝到 F:\javaTest\IO 文件夹中
 */
public class Copy {
    public static void main(String[] args) {
        FileWriter fileWriter = null;
        FileReader fileReader = null;
        try {
            //创建一个输出流--写出文件:到F:\javaTest\IO\Activity.md
            fileWriter = new FileWriter("F:\\javaTest\\IO\\Activity.md");
            //创建一个输入流--读取文件:I:\Java\Base\Thinking\src\IOTest\Activity.md
            fileReader = new FileReader("I:\\Java\\Base\\Thinking\\src\\IOTest\\Activity.md");
            int len = 0;//读取的长度
            char[] buf = new char[1024];//2K的字符数组
            while ((len = fileReader.read(buf)) != -1) {
                fileWriter.write(buf, 0, len);//写有效个字符
            }
        } catch (IOException e) {
            throw new RuntimeException("读取失败");
        }finally {
            try {//关闭输入流
                if (fileReader != null) {
                    fileReader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {//关闭输出流
                if (fileWriter != null) {
                    fileWriter.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

三、字符缓冲:BufferedWriter和BufferedReader

将字符读入内存缓冲区,避免频繁执行读写操作,提高流的操作效率

默认字符数组缓冲区大小:8192 (即16K的缓冲)
1.BufferedWriter:缓存写出
BufferedWriter bfw = null;
try {
    String fileName = "I:\\Java\\Base\\Thinking\\src\\IOTest\\BufferedWriter.txt";
    //1.创建一个输出流--写出文件:需明确被操作文件----文件不存在,会自动创建文件(同名覆盖)
    bfw = new BufferedWriter(new FileWriter(fileName));
    //2.写入数据
    bfw.write("Line1 第一行");
    bfw.newLine();//换行 BufferedWriter特有方法,可区分不同平台换行
    //3.刷新流中的缓冲
    bfw.flush();
    bfw.write("Line2 第二行");
} catch (IOException e) {
    e.printStackTrace();
} finally {
    //4.关闭流也会刷新缓冲
    try {
        //不为空,才能关闭流
        if (bfw != null) {
            bfw.close();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}
2.BufferedReader:缓存读取
public static void main(String[] args) {
    String fileName = "I:\\Java\\Base\\Thinking\\src\\IOTest\\Activity.md";
    try {
        //1.创建一个输入流--读取文件
        BufferedReader  bfr= new BufferedReader(new FileReader(fileName));
        //2.读取字符
        //读取一行:BufferedReader特有方法
        String line = null;
        while ((line = bfr.readLine()) != null) {
            System.out.println(line);
        }
        bfr.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

四、效率测试

通过读取一个1275K的文件看时间消耗情况

/**
 * 一个一个字符读取测试
 *
 * @param fileName
 * @return
 */
private static String readOneByOne(String fileName) {
    FileReader fileReader = null;
    try {
        StringBuilder sb = new StringBuilder();
        fileReader = new FileReader(fileName);
        int ch = 0;
        while ((ch = fileReader.read()) != -1) {
            sb.append(new String((new char[]{(char) ch})));
        }
        return sb.toString();
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    } finally {
        try {
            if (fileReader != null) {
                fileReader.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
    /**
     * 字符数组效率测试
     *
     * @param fileName
     * @return
     */
    private static String readByArray(String fileName) {
        FileReader fileReader = null;
        try {
            StringBuilder sb = new StringBuilder();
            fileReader = new FileReader(fileName);
            int len = 0;
            char[] buf = new char[1024];
            while ((len = fileReader.read(buf)) != -1) {
                sb.append(new String(buf, 0, len));
            }
            return sb.toString();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                if (fileReader != null) {
                    fileReader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * BufferedReader效率测试
     *
     * @param fileName
     * @return
     */
    private static String readByBuffer(String fileName) {
        BufferedReader bfr = null;
        try {
            StringBuilder sb = new StringBuilder();
            bfr = new BufferedReader(new FileReader(fileName));
            String line = null;
            while ((line = bfr.readLine()) != null) {
                sb.append(line);
            }
            return sb.toString();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                if (bfr != null) {
                    bfr.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
\ 一个字符 字符数组 BufferedReader
耗时 0.2798秒 0.1043秒 0.1165秒

五、字符编码

计算机只能识别二进制数据(即用来描述电平高低位的一串0和1数据组合)
使用若干0和1的排列组合来唯一表示各个国家的文字,形成编码表。
可以看作计算机语言通向人类语言的一个大词典。

1.字节里位的排列组合
一个字节(8个0或1)的排列组合有多少中可能:2^8 = 256种
两个字节(16个0或1)的排列组合有多少中可能:2^16 = 65,536种
三个字节(24个0或1)的排列组合有多少中可能:2^24 = 16,777,216种
要打造一种万国码,由于世界上不同国家,不同民族,的文字非常多,
2个字节65,536的容量也不够,所以采用3个字节16,777,216。
2.常见码表
ASCII 码表:美国标准信息交换码      1字节--使用:7位
ISO8859-1:拉丁码表。欧洲码表       1字节--使用:8位
GB2312:中国的中文编码表。          2字节--使用:16位
GBK:GB2312升级版,增加中文         2字节--使用:16位                   
Unicode:国际标准码                 2字节--使用:16位
UTF-8:Unicode升级版 能用1个字符表示用1个字符,不然用2个,要还是不够用,使用3个字节
3.指定编码表写出文件
public class 编码表测试 {
    public static void main(String[] args) throws IOException {
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("UTF-8.txt"),"utf-8");
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("GBK.txt"),"gbk");
        osw.write("张风toly");
        osw.close();
    }
}
utf-8和gbk比较.png
utf-8和gbk.png

4.计算机怎么识别字的

这个字符串用utf-8编码转化为字节数组,可看到是[-27, -68, -96]三个数
这三个数又代表什么?

String str = "张";
System.out.println(Arrays.toString(str.getBytes("utf-8")));
//[-27, -68, -96]

用计算器查看十进制数的字节型的二进制,可以看到:

计算器查看字节二进制.png
编码.png

5.使用不同码表读取测试

InputStreamReader isr_GBK_gbk = new InputStreamReader(new FileInputStream("GBK.txt"),"gbk");
char[] buf = new char[10];
int len = isr_GBK_gbk.read(buf);
System.out.println(new String(buf, 0, len));//张风toly

InputStreamReader isr_GBK_utf8 = new InputStreamReader(new FileInputStream("GBK.txt"),"utf-8");
char[] buf2 = new char[10];
int len2 = isr_GBK_utf8.read(buf2);
System.out.println(new String(buf2, 0, len2));//�ŷ�toly

InputStreamReader isr_UTF8_utf8 = new InputStreamReader(new FileInputStream("UTF-8.txt"),"utf-8");
char[] buf4 = new char[10];
int len4 = isr_UTF8_utf8.read(buf4);
System.out.println(new String(buf4, 0, len4));//张风toly

InputStreamReader isr_UTF8_gbk = new InputStreamReader(new FileInputStream("UTF-8.txt"),"gbk");
char[] buf3 = new char[10];
int len3 = isr_UTF8_gbk.read(buf3);
System.out.println(new String(buf3, 0, len3));//寮犻toly


后记:捷文规范

1.本文成长记录及勘误表
项目源码 日期 备注
V0.1--无 2018-10-9 Java中的字符流
V0.2--无 2018-10-13 添加字符编码的相关知识,为下文做铺垫
2.更多关于我
笔名 QQ 微信 爱好
张风捷特烈 1981462002 zdl1994328 语言
我的github 我的简书 我的CSDN 个人网站
3.声明

1----本文由张风捷特烈原创,转载请注明
2----欢迎广大编程爱好者共同交流
3----个人能力有限,如有不正之处欢迎大家批评指证,必定虚心改正
4----看到这里,我在此感谢你的喜欢与支持

Android技术栈
Android技术栈
8.2万字 · 1.9万阅读 · 130人关注
决战安卓
Web note ad 1