• 字符流读写文本文件


    字符流

    当使用字节流读取文本文件时,可能会有一个小问题。就是遇到中文字符时,可能不会显示完整的字符,那是因为一个中文字符可能占用多个字节存储。所以Java提供一些字符流类,以字符为单位读写数据,专门用于处理文本文件。

    输入流

    输入字符流(Reader)

    java.io.Reader 抽象类是表示用于读取字符流的所有类的超类,可以读取字符信息到内存中。下面是Reader的体系:

    在这里插入图片描述

    public abstract class Reader implements Readable, Closeable
    
    • 1

    它是个抽象类,因此不能被实例化。

    构造方法

    Reader提供了两个构造方法供子类使用

    protected Reader() 
    protected Reader(Object lock) 
    
    • 1
    • 2

    实例方法

    它定义了字符输入流的基本共性功能方法。

    • public void close() :关闭此流并释放与此流相关联的任何系统资源。
    • public int read() : 从输入流读取一个字符。
    • public int read(char[] cbuf) : 从输入流中读取一些字符
    • public abstract int read(char[] cbuf, int off, int len) : 从输入流中读取一些字符
    • public int read(CharBuffer target):将字符读入指定的字符缓冲区
    • public void mark(int readAheadLimit):标记流中的位置
    • boolean markSupported(): 是否支持 mark
    • public boolean ready() : 流是否准备好被读取
    • public void reset() : 重置流
    • public long skip(long n): 跳过字符

    由于public abstract int read(char[] cbuf, int off, int len) 是抽象方法,所以所有子类必须实现此方法。

    JDK10提供public long transferTo(Writer out)用于写出读取到的字符

    FileReader

    java.io.FileReader 类是读取字符文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。

    JDK11提供的构造方法中可以传入指定编码

    public class FileReader extends InputStreamReader
    
    • 1

    系统默认字符编码:

    Charset.defaultCharset(); // 系统默认编码
    
    • 1
    • eclipse中默认为GBK
    • jetbrains idea中默认UTF8

    编辑器的默认编码是可以更改的

    构造方法

    • FileReader(File file) : 创建一个新的 FileReader ,给定要读取的File对象。
    • FileReader(String fileName) : 创建一个新的 FileReader ,给定要读取的文件的名称。

    JDK11新增的构造

    FileReader(File file, Charset charset)
    FileReader(String fileName, Charset charset)
    
    • 1
    • 2

    例子:

    File file = new File("D:\\temp\\a.txt");
    FileReader reader = new FileReader(file);
    FileReader reader1 = new FileReader("D:\\temp\\a.txt");
    
    • 1
    • 2
    • 3

    构造方法内部都是调用了父类的构造方法,传入一个FileInputStream对象。

    实例方法

    FileReader中的所有方法都来自其父类InputStreamReader,没有重写任何方法同时也没有扩展的方法。也就是说,学习了FileReader同时也学习了InputStreamReader.

    读取单个字符

    read() 方法,每次可以读取一个字符的数据,提升为int类型,读取到文件末尾,返回 -1 ,循环读取

    package com.itlaobing.tor;
    
    import java.io.FileReader;
    import java.io.IOException;
    
    public class FileReaderTest {
    
    	public static void main(String[] args) throws IOException {
    		FileReader fr = new FileReader("D:/temp/reader.txt");
    		int ch = fr.read();//读取单个字符
    		System.out.println((char)ch);
    		fr.close();//关闭流
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    自行将上面代码修改为读取整个文本内容。

    读取多个字符

    read(char[] cbuf) ,每次读取cbuf的长度个字符到数组中,返回读取到的有效字符个数,读取到末尾时,返回 -1

    创建reader2.txt内容为:

    云创动力在西安学习中心
    
    • 1

    例子:

    package com.itlaobing.tor;
    
    import java.io.FileReader;
    import java.io.IOException;
    
    public class FileReaderTest2 {
    
    	public static void main(String[] args) throws IOException {
    		// 使用文件名称创建流对象
    		FileReader fr = new FileReader("D:/temp/reader2.txt");
    		// 定义变量,保存有效字符个数
    		int len;
    		// 定义字符数组,作为装字符数据的容器
    		char[] cbuf = new char[2];
    		// 循环读取
    		while ((len = fr.read(cbuf)) != -1) {
    			System.out.println(new String(cbuf));
    		}
    		// 关闭资源
    		fr.close();
    	}
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    输入结果为:

    云创
    动力
    在西
    安学
    习中
    心中
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    我们发现多了一个字符,这是为什么呢?尝试将代码中的:

    System.out.println(new String(cbuf));
    
    • 1

    修改为:

    System.out.println(new String(cbuf, 0, len));
    
    • 1

    BufferedReader

    java.io.BufferedReader 从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取。可以指定缓冲区大小,或者可以使用默认大小。 默认值足够大,可用于大多数用途。 它是一个缓冲流。

    public class BufferedReader extends Reader
    
    • 1

    构造方法

    BufferedReader提供了两个构造方法

    //创建使用默认大小的输入缓冲区的缓冲字符输入流
    public BufferedReader(Reader in)
    //创建使用指定大小的输入缓冲区的缓冲字符输入流
    public BufferedReader(Reader in, int sz)
    
    • 1
    • 2
    • 3
    • 4

    读取一行字符

    BufferedReader继承了Reader中所有public方法,还扩展了一个方法:

    public String readLine() //读取一行文字
    
    • 1

    readLine()读取完毕时,返回null

    创建bufferReader.txt文件,并写入如下内容:

    床前明月光,
    疑是地上霜。
    举头望明月,
    低头思故乡。
    
    • 1
    • 2
    • 3
    • 4

    例子:

    package com.itlaobing.demo.pm;
    
    import java.io.BufferedReader;
    import java.io.FileReader;
    import java.io.IOException;
    
    public class BufferedReaderTest {
    
    	public static void main(String[] args) throws IOException {
    		// 创建流对象
    		BufferedReader reader = new BufferedReader(new FileReader("D:/temp/bufferReader.txt"));
    		// 定义字符串,保存读取的一行文字
    		String line = null;
    		// 循环读取,读取到最后返回null
    		while ((line = reader.readLine()) != null) {
    			System.out.print(line);
    			System.out.println("------");
    		}
    		// 关闭流
    		reader.close();
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    InputStreamReader

    java.io.InputStreamReaderReader的子类,是从字节流到字符流的桥:它读取字节,并使用指定的Charset将其解码为字符 。 它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集。

    public class InputStreamReader extends Reader
    
    • 1

    构造方法

    InputStreamReader中提供了四个构造方法:

    //创建一个使用默认字符集的InputStreamReader
    public InputStreamReader(InputStream in)
    //创建一个使用命名字符集的InputStreamReader
    public InputStreamReader(InputStream in, String charsetName)
    //创建一个使用给定字符集的InputStreamReader
    public InputStreamReader(InputStream in, Charset cs)
    //创建一个使用给定字符集解码器的InputStreamReader
    public InputStreamReader(InputStream in, CharsetDecoder dec)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    其中常用的是前三个。

    实例方法

    InputStreamReader继承了Reader中的所有public方法,并扩展了getEncoding()方法。

    public String getEncoding()//获取使用的字符编码名称
    
    • 1

    其他方法在之前都有讲过,所以在此不再描述。

    指定编码读取

    在创建InputStreamReader对象时,指定编码字符集。

    例子:

    创建InputStreamReader.txt文件,内容如下:

    大家好,我是渣渣辉。
    是兄弟就来**蓝月砍我。
    
    • 1
    • 2
    package com.itlaobing.demo.pm;
    
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.InputStreamReader;
    
    public class InputStreamReaderTest {
    
    	public static void main(String[] args) throws IOException {
    		// 定义文件路径,文件为gbk编码
    		String FileName = "D:\\temp\\InputStreamReader.txt";
    		
    		// 创建流对象,默认UTF8编码
    		InputStreamReader isr = new InputStreamReader(new FileInputStream(FileName));
    		// 创建流对象,指定GBK编码
    		InputStreamReader isr2 = new InputStreamReader(new FileInputStream(FileName), "GBK");
    		// 定义变量,保存字符
    		int read;
    		// 使用默认编码字符流读取,乱码
    		while ((read = isr.read()) != -1) {
    			System.out.print((char) read); 
    		}
    		isr.close();
    		System.out.println();
    		
    		// 使用指定编码字符流读取,正常解析
    		while ((read = isr2.read()) != -1) {
    			System.out.print((char) read);
    		}
    		isr2.close();
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    输出结果如下:

    ��Һã����������ԡ�
    ���ֵܾ���**���¿��ҡ�
    大家好,我是渣渣辉。
    是兄弟就来**蓝月砍我。
    
    • 1
    • 2
    • 3
    • 4

    输出流

    字符输出流【Writer】

    java.io.Writer 抽象类是表示用于写出字符流的所有类的超类,将指定的字符信息写出到目的地。下面是Writer的体系:

    在这里插入图片描述

    public abstract class Writer implements Appendable, Closeable, Flushable
    
    • 1

    构造方法

    Writer提供了两个构造方法:

    protected Writer();
    protected Writer(Object lock)
    
    • 1
    • 2

    实例方法

    Writer类中提供了以下方法:

    • public abstract void close() :关闭此输出流并释放与此流相关联的任何系统资源。
    • public abstract void flush() :刷新此输出流并强制任何缓冲的输出字符被写出。
    • public void write(int c) :写出一个字符。
    • public void write(char[] cbuf) :将 cbuf.length字符从指定的字符数组写出此输出流。
    • public abstract void write(char[] b, int off, int len) :从指定的字符数组写出 len字符,从偏移量 off开始输出到此输出流。
    • public void write(String str) :写出一个字符串。

    FileWriter

    java.io.FileWriter类是写出字符到文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。

    public class FileWriter extends OutputStreamWriter
    
    • 1

    构造方法

    FileWriter类提供了5个构造方法:

    //构造一个给定文件名的FileWriter对象
    public FileWriter(String fileName);
    //构造一个FileWriter对象,给出一个带有布尔值的文件名,表示是否附加写入的数据
    public FileWriter(String fileName, boolean append);
    //给一个File对象构造一个FileWriter对象
    public FileWriter(File file);
    //给一个File对象构造一个FileWriter对象
    public FileWriter(File file, boolean append);
    //构造与文件描述符关联的FileWriter对象
    public FileWriter(FileDescriptor fd) 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    常用的是前4个构造方法。

    JDK11新增的构造

    public FileWriter(String fileName, Charset charset)
    public FileWriter(String fileName, Charset charset, boolean append)
    public FileWriter(File file, Charset charset)
    public FileWriter(File file, Charset charset, boolean append)   
    
    • 1
    • 2
    • 3
    • 4

    实例方法

    FileWriter类中的所有方法都来自其父类OutputStreamWriter,没有重写任何方法同时也没有扩展的方法。也就是说,学习了FileWriter同时也学习了OutputStreamWriter

    写出单个字符

    write(int b) 方法,每次可以写出一个字符数据。

    例子:

    package com.itlaobing.demo.pm;
    
    import java.io.FileWriter;
    import java.io.IOException;
    
    public class FileWriterTest {
    
    	public static void main(String[] args) throws IOException {
    		FileWriter writer = new FileWriter("D:/temp/fileWriter.txt");
    		
    		writer.write(97); //a
    		writer.write('a'); //a
    		writer.write("你好"); //你好
    		 
    		
    		writer.flush();//刷新缓冲区,流对象可以继续使用
    		writer.close();//关闭流,释放系统资源。关闭前会刷新缓冲区
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    执行完以上代码后,在D:/temp/文件夹中会多出一个fileWriter.txt文件,文件内容是:

    aa你好
    
    • 1

    文件的字符编码集是系统默认的字符编码集,此处我们的默认编码集是UTF-8

    写出多个字符

    • write(char[] cbuf)
    • write(char[] b, int off, int len)

    BufferedWriter

    java.io.BufferedWriter是将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入。 可以指定缓冲区大小,或者可以接受默认大小。 默认值足够大,可用于大多数用途。是一个缓冲流。

    public class BufferedWriter extends Writer
    
    • 1

    构造方法

    BufferedWriter提供了两个构造方法:

    //创建使用默认大小的输出缓冲区的缓冲字符输出流
    public BufferedWriter(Writer out);
    //创建一个新的缓冲字符输出流,使用给定大小的输出缓冲区
    public BufferedWriter(Writer out, int sz);
    
    • 1
    • 2
    • 3
    • 4

    实例方法

    BufferedWriter继承了Writer中所有public方法,还扩展了一个方法:

    public void newLine();//写一个行分隔符,由系统属性定义符号
    
    • 1

    例子:

    package com.itlaobing.tor;
    
    import java.io.BufferedWriter;
    import java.io.FileWriter;
    import java.io.IOException;
    
    public class BufferWriterTest {
    
    	public static void main(String[] args) throws IOException {
    		// 创建流对象
    		BufferedWriter bw = new BufferedWriter(new FileWriter("d:/temp/bufferedWriter.txt"));
    		// 写出数据
    		bw.write("云创动力");
    		// 写出换行
    		bw.newLine();
    		bw.write("XX学院");
    		bw.newLine();
    		bw.write("IT特种兵");
    		bw.newLine();
    		// 释放资源
    		bw.close();
    
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    运行以上程序后,会在创建一个新文件,编码字符集为默认编码字符集,此处为UTF-8,内容为:

    云创动力
    XX学院
    IT特种兵
    
    • 1
    • 2
    • 3

    OutputStreamWriter

    java.io.OutputStreamWriter ,是Writer的子类,是从字符流到字节流的桥梁。使用指定的字符集将字符编码为字节。它的字符集可以由名称指定,也可以接受平台的默认字符集。

    public class OutputStreamWriter extends Writer
    
    • 1

    构造方法

    OutputStreamWriter提供了四个构造方法:

    //创建一个使用默认字符编码的OutputStreamWriter
    public OutputStreamWriter(OutputStream out);
    //创建一个使用命名字符集的OutputStreamWriter。
    public OutputStreamWriter(OutputStream out, String charsetName);
    //创建一个使用给定字符集的OutputStreamWriter
    public OutputStreamWriter(OutputStream out, Charset cs);
    //创建一个使用给定字符集编码器的OutputStreamWriter
    public OutputStreamWriter(OutputStream out, CharsetEncoder enc);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    实例方法

    OutputStreamWrter的方法大都继承自Writer,除了getEncoding()方法。

    例子:

    package com.itlaobing.tor;
    
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.OutputStreamWriter;
    import java.nio.charset.Charset;
    
    public class OutputStreamWriterTest {
    
    	public static void main(String[] args) throws IOException {
    		FileOutputStream out = new FileOutputStream("d:/temp/outStreamWriter.txt");
    		Charset set = Charset.forName("utf-8");
    		OutputStreamWriter writer = new OutputStreamWriter(out, set);
    		System.out.println("编码格式为: " + writer.getEncoding());
    		writer.write("站在那别动,我去买橘子");
    		writer.close();
    	}
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    增强try

    JDK 7之前我们使用try-catchtry-catch-finally来处理异常。在使用资源的时最后都需要关闭资源,所以一般我们在finally处关闭资源。比如:

    public static void main(String[] args) throws IOException {
    		FileOutputStream out = null;
    		OutputStreamWriter writer = null;
    		try {
    			out = new FileOutputStream("d:/temp/outStreamWriter.txt");
    			Charset set = Charset.forName("utf-8");
    			writer = new OutputStreamWriter(out, set);
    			System.out.println("编码格式为: " + writer.getEncoding());
    			writer.write("站在那别动,我去买橘子");
    		} catch (IOException e) {
    			e.printStackTrace();
    		}finally {
                //关闭资源
    			writer.close();
    			out.close();
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    然而有的时候我们会忘记关闭资源。那么有没有更好的方法呢?

    jdk1.7开始, Java 7增强了try语句的功能——它允许在try关键字后跟一对圆括号(),圆括号可以声明,初始化一个或多个资源,此处的资源指得是那些必须在程序结束时必须关闭的资源(比如流操作,数据库连接,网络连接等),try语句在该语句结束时自动关闭这些资源。这种称为try-with-resources语句

    Charset set = Charset.forName("utf-8");
    		try (FileOutputStream out = new FileOutputStream("d:/temp/outStreamWriter.txt"); OutputStreamWriter writer = new OutputStreamWriter(out, set);){
    			System.out.println("编码格式为: " + writer.getEncoding());
    			writer.write("站在那别动,我去买橘子");
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    像这样的话,执行完会自动关闭,不用我们在finally中关闭,也再也不用担心忘记关闭了。

    注意:只要这些资源实现类实现了CloseableAutoCloseable接口,就可以自动关闭。几乎所有的资源都可以用这种方式实现自动关闭资源。

    如果try()里面有多个资源,用;分开,资源的close方法的调用顺序与它们的创建顺序相反。带有资源的try语句可以像一般的try语句一样具有catch和finally块。在try-with-resources语句中,任何catch或finally块都是在声明的资源被关闭后才会执行的

  • 相关阅读:
    解锁内存之谜:从C到Python、Java和Go的内存管理对比
    springboot使用@Validated校验不生效
    重学设计模式(三、设计模式-访问者模式)
    8张图带你全面了解kafka的核心机制
    前端面试常见问题总结
    理论第九课——进制转换(八进制篇)
    只要让我戴上面具 , 我就会马上逃跑 ! 等下眼镜卡住了
    重点用能单位能耗系统在湖南三立集团的应用
    哪吒GT无眉版上市,让你的小长假帅气开跑
    springBoot整合rabbitMQ,通过注册bean的方式创建exchange,queue,容器中成功注入,但rabbitserver中不显示
  • 原文地址:https://blog.csdn.net/m0_57229804/article/details/132948210