java_io流
2019-08-16 09:34:49来源:博客园 阅读 ()
java_io流
流的概念和作用
流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。
IO流的分类
- 根据处理数据类型的不同分为:字符流和字节流
- 根据数据流向不同分为:输入流和输出流
字符流和字节流
字符流的由来: 因为数据编码的不同,而有了对字符进行高效操作的流对象。本质其实就是基于字节流读取时,去查了指定的码表。 字节流和字符流的区别:
- 读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。
-
处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。
- 字节流:一次读入或读出是8位二进制。
-
字符流:一次读入或读出是16位二进制。
-
设备上的数据无论是图片或者视频,文字,它们都以二进制存储的。二进制的最终都是以一个8位为数据单元进行体现,所以计算机中的最小数据单元就是字节。意味着,字节流可以处理设备上的所有数据,所以字节流一样可以处理字符数据。
结论:只要是处理纯文本数据,就优先考虑使用字符流。 除此之外都使用字节流。
输入流和输出流
输入流只能进行读操作,输出流只能进行写操作,程序中需要根据待传输数据的不同特性而使用不同的流。
-
输入字节流 InputStream
InputStream
是所有的输入字节流的父类,它是一个抽象类。ByteArrayInputStream
、StringBufferInputStream
、FileInputStream
是三种基本的介质流,它们分别从Byte 数组
、StringBuffer
、和本地文件
中读取数据。PipedInputStream
是从与其它线程共用的管道中读取数据,与Piped 相关的知识后续单独介绍。ObjectInputStream
和所有FilterInputStream
的子类都是装饰流(装饰器模式的主角)。
输出字节流 OutputStream
OutputStream
是所有的输出字节流的父类,它是一个抽象类。ByteArrayOutputStream
、FileOutputStream
是两种基本的介质流,它们分别向Byte 数组
、和本地文件
中写入数据。PipedOutputStream
是向与其它线程共用的管道中写入数据。ObjectOutputStream
和所有FilterOutputStream
的子类都是装饰流。
总结:
- 输入流:InputStream或者Reader:从文件中读到程序中;
- 输出流:OutputStream或者Writer:从程序中输出到文件中;
节点流
节点流:直接与数据源相连,读入或读出。
直接使用节点流,读写不方便,为了更快的读写文件,才有了处理流。常用的节点流
- 父 类 :
InputStream
、OutputStream
、Reader
、Writer
- 文 件 :
FileInputStream
、FileOutputStrean
、FileReader
、FileWriter
文件进行处理的节点流 - 数 组 :
ByteArrayInputStream
、ByteArrayOutputStream
、CharArrayReader
、CharArrayWriter
对数组进行处理的节点流(对应的不再是文件,而是内存中的一个数组) - 字符串 :
StringReader
、StringWriter
对字符串进行处理的节点流 - 管 道 :
PipedInputStream
、PipedOutputStream
、PipedReader
、PipedWriter
对管道进行处理的节点流
处理流
处理流和节点流一块使用,在节点流的基础上,再套接一层,套接在节点流上的就是处理流。如
BufferedReader
.处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装,称为流的链接。常用的处理流
- 缓冲流:
BufferedInputStrean
、BufferedOutputStream
、BufferedReader
、BufferedWriter
增加缓冲功能,避免频繁读写硬盘。 - 转换流:
InputStreamReader
、OutputStreamReader
实现字节流和字符流之间的转换。 - 数据流:
DataInputStream
、DataOutputStream
等-提供将基础数据类型写入到文件中,或者读取出来。
转换流
InputStreamReader
、OutputStreamWriter
要InputStream
或OutputStream
作为参数,实现从字节流到字符流的转换。
字节流
FileOutputStream
我们常用来向文件中写入数据的流是FileOutputStream
,主要用于写入诸如图像数据之类的原始字节的流。
构造函数如下:
FileOutputStream(File file) 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。 FileOutputStream(File file, boolean append) 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。 FileOutputStream(FileDescriptor fdObj) 创建一个向指定文件描述符处写入数据的输出文件流,该文件描述符表示一个到文件系统中的某个实际文件的现有连接。 FileOutputStream(String name) 创建一个向具有指定名称的文件中写入数据的输出文件流。 FileOutputStream(String name, boolean append) 创建一个向具有指定 name 的文件中写入数据的输出文件流。
写入的方法有3个,具体如下:
void write(byte[] b) 将 b.length 个字节从指定 byte 数组写入此文件输出流中。 void write(byte[] b, int off, int len) 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此文件输出流。 void write(int b) 将指定字节写入此文件输出流。
FileInputStream
和FileOutputStream对应的是FileInputStream
,用于读取诸如图像数据之类的原始字节流,文件不存在会创建。
构造函数如下:
FileInputStream(File file) 通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的 File 对象 file 指定。 FileInputStream(FileDescriptor fdObj) 通过使用文件描述符 fdObj 创建一个 FileInputStream,该文件描述符表示到文件系统中某个实际文件的现有连接。 FileInputStream(String name) 通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的路径名 name 指定。
读取的方法如下,read方法从此输入流中读取数据字节。如果没有输入可用,则此方法将阻塞。如果因为已经到达文件末尾而没有更多的数据,则返回 -1
。
int read() 从此输入流中读取一个数据字节。 int read(byte[] b) 从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。 int read(byte[] b, int off, int len) 从此输入流中将最多 len 个字节的数据读入一个 byte 数组中。
String filePath = "D:\\Learning\\doc\\test1.txt"; //读取test文件内容 FileInputStream fis=new FileInputStream(filePath);//方式一:一个字节一个字节的读取 int ch=0; while ((ch=fis.read())!=-1) { System.out.print((char)ch); } //方式二:批量读取到缓冲区,推荐使用方式 int len=0; byte[] buffer=new byte[1024]; while ((len=fis.read(buffer))!=-1){ System.out.print(new String(buffer,0,len)); } //方式三:一次性读取,不建议使用,如果文件过大,那么数组会非常大,可能内存不够,创建数组也会非常耗时 //fis.available()获取文件的字节数 byte[] buf = new byte[fis.available()]; fis.read(buf); System.out.println(new String(buf)); fis.close();
//需求:把vedio拷贝到vedio1 String sourceFilePath = "D:\\Learning\\doc\\vedio.avi"; String destFilePath = "D:\\Learning\\doc\\vedio1.avi"; //创建读取文件对象 FileInputStream fis=new FileInputStream(sourceFilePath); //创建写入文件对象 FileOutputStream fos=new FileOutputStream(destFilePath); //创建1M的缓冲区 byte[] buffer =new byte[1024*1024*1]; //每次读取的长度,可能最后一次的读取长度并没有buffer.length那么多 int len=0; while ((len=fis.read(buffer))!=-1){ fos.write(buffer,0,len); } fos.close(); fis.close();
上面我们手动创建了缓冲区用来提高字节读写效率,java也提供了同样的功能,即BufferInputStream,BufferOutputStream。
BufferedInputStream
BufferedInputStream 为另一个输入流添加一些功能,即缓冲输入以及支持 mark 和 reset 方法的能力。在创建 BufferedInputStream 时,会创建一个内部缓冲区数组。
构造函数如下:
BufferedInputStream(InputStream in) 创建一个 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。 BufferedInputStream(InputStream in, int size) 创建具有指定缓冲区大小的 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。
读取方法如下:
int read() 参见 InputStream 的 read 方法的常规协定。 int read(byte[] b, int off, int len) 从此字节输入流中给定偏移量处开始将各字节读取到指定的 byte 数组中。
BufferOutputStream
该类实现缓冲的输出流。通过设置这种输出流,应用程序就可以将各个字节写入底层输出流中,而不必针对每次字节写入调用底层系统。
构造方法如下:
BufferedOutputStream(OutputStream out) 创建一个新的缓冲输出流,以将数据写入指定的底层输出流。 BufferedOutputStream(OutputStream out, int size) 创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层输出流。
write方法如下:
void write(byte[] b, int off, int len) 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此缓冲的输出流。 void write(int b) 将指定的字节写入此缓冲的输出流。
可以自己再创建一个缓冲区进一步提高效率,也可以在构造函数的第二个参数传入缓冲区大小:
import java.io.*; class Test { public static void main(String[]args) throws IOException { //测试拷贝一个大小为200M时的性能问题 test1(); test2(); test3(); } public static final int BUFFER_SIZE = 1024 * 1024 * 1; /** * 方式一:不加缓冲区的情况 * * @throws IOException */ public static void test1() throws IOException { //需求:把vedio拷贝到vedio1 String sourceFilePath = "E:\\desktop.zip"; String destFilePath = "E:\\desktop1.zip"; //创建读取文件对象 FileInputStream fis = new FileInputStream(sourceFilePath); //创建写入文件对象 FileOutputStream fos = new FileOutputStream(destFilePath); BufferedInputStream bis = new BufferedInputStream(fis); BufferedOutputStream bos = new BufferedOutputStream(fos); long start = System.currentTimeMillis(); int len = 0; while ((len = bis.read()) != -1) { bos.write(len); } long end = System.currentTimeMillis(); System.out.println(end - start);//21365 bos.flush(); bis.close(); bos.close(); } /** * 方式二:加缓冲区的情况 * @throws IOException */ public static void test2() throws IOException { //需求:把vedio拷贝到vedio1 String sourceFilePath = "E:\\desktop.zip"; String destFilePath = "E:\\desktop2.zip"; //创建读取文件对象 FileInputStream fis = new FileInputStream(sourceFilePath); //创建写入文件对象 FileOutputStream fos = new FileOutputStream(destFilePath); BufferedInputStream bis = new BufferedInputStream(fis); BufferedOutputStream bos = new BufferedOutputStream(fos); long start = System.currentTimeMillis(); byte[] buffer = new byte[BUFFER_SIZE]; int len = 0; while ((len = bis.read(buffer)) != -1) { bos.write(buffer, 0, len); } long end = System.currentTimeMillis(); System.out.println(end - start);//975 bos.flush(); bis.close(); bos.close(); } /** * 方式3:构造函数加入缓冲大小的情况 * @throws IOException */ public static void test3() throws IOException { // 需求:把vedio拷贝到vedio1 String sourceFilePath = "E:\\desktop.zip"; String destFilePath = "E:\\desktop3.zip"; //创建读取文件对象 FileInputStream fis = new FileInputStream(sourceFilePath); //创建写入文件对象 FileOutputStream fos = new FileOutputStream(destFilePath); BufferedInputStream bis = new BufferedInputStream(fis, BUFFER_SIZE); BufferedOutputStream bos = new BufferedOutputStream(fos, BUFFER_SIZE); long start = System.currentTimeMillis(); int len = 0; while ((len = bis.read()) != -1) { bos.write(len); } long end = System.currentTimeMillis(); System.out.println(end - start);//3036 bos.flush(); bis.close(); bos.close(); } }
由此可见,手动加缓冲区的效率最高。
上面对于IO的异常处理是直接抛出,并未处理,下面介绍一下IO流的正确异常处理方式:
String filePath = "D:\\Learning\\doc\\test.txt"; FileOutputStream fos= null; try { fos = new FileOutputStream(filePath); fos.write("123456".getBytes()); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally {
if(fos!=null){ try { //一定要记得关闭流,否则文件可能一直被占用而无法操作 fos.close(); } catch (IOException e) { e.printStackTrace(); }
} }
字符流
上面介绍了字节流,如果我们写入的是中文,然后使用字节流读取出来,就会出现乱码。
为什么会乱码呢?因为java中的char存的是Unicode字符,占用2个字节,而中文也是2个字节,上面的代码每次去一个字节区unicode字符集中查找,肯定找到的不是对应的中文,因此乱码。下面对上面的代码改造一下,手动指定编码方式,就可以解决乱码问题。关于字符的编码问题可以参考这里。
String filePath = "D:\\Learning\\doc\\test.txt"; FileOutputStream fos= null; FileInputStream fis =null; try { fos = new FileOutputStream(filePath); fis=new FileInputStream(filePath); fos.write("学习IO流".getBytes("UTF-8")); byte [] buffer =new byte[1024]; int len; while ((len=fis.read(buffer))!=-1){ System.out.print(new String(buffer,0,len,"UTF-8")); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally { try {
//忘了判断null fis.close(); fos.close(); } catch (IOException e) { e.printStackTrace(); } } }
FileWriter
FileWriter 用于写入字符流。用来写入字符文件的便捷类。使用平台(操作系统)默认字符编码。比如Windows简体中文版操作系统使用的编码为gbk。
构造函数如下:
FileWriter(File file) 根据给定的 File 对象构造一个 FileWriter 对象。 FileWriter(File file, boolean append) 根据给定的 File 对象构造一个 FileWriter 对象。 FileWriter(FileDescriptor fd) 构造与某个文件描述符相关联的 FileWriter 对象。 FileWriter(String fileName) 根据给定的文件名构造一个 FileWriter 对象。 FileWriter(String fileName, boolean append) 根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象。
写入方法如下:
void write(char[] cbuf) 写入字符数组。 void write(int c) 写入单个字符。 void write(String str) 写入字符串。 void write(char[] cbuf, int off, int len) 写入字符数组的某一部分。 void write(String str, int off, int len) 写入字符串的某一部分。
FileReader
FileReader
用于读取字符流。用来读取字符文件的便捷类。
构造方法如下:
FileReader(File file) 在给定从中读取数据的 File 的情况下创建一个新 FileReader。 FileReader(FileDescriptor fd) 在给定从中读取数据的 FileDescriptor 的情况下创建一个新 FileReader。 FileReader(String fileName) 在给定从中读取数据的文件名的情况下创建一个新 FileReader。
读取方法如下:
int read() 读取单个字符。 int read(char[] cbuf) 将字符读入数组。 int read(char[] cbuf, int off, int len) 将字符读入数组的某一部分。 int read(CharBuffer target) 试图将字符读入指定的字符缓冲区。
//复制文本文件 String sourceFilePath = "D:\\Learning\\doc\\test.txt"; String destFilePath = "D:\\Learning\\doc\\test1.txt"; FileReader fr = null; FileWriter fw=null; try { fr = new FileReader(sourceFilePath); fw=new FileWriter(destFilePath); char[] buffer =new char[1024]; int len = 0; while ((len = fr.read(buffer)) != -1) { fw.write(buffer,0,len); } } catch (IOException e) { e.printStackTrace(); } finally { try { if (fr != null) { fr.close(); } if (fw != null) { fw.close(); } } catch (IOException e) { e.printStackTrace(); } }
上面我们手动创建了缓冲区用来提高字符读写效率,java也提供了同样的功能,即BufferReader,BufferWriter。
BufferReader
从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。可以指定缓冲区的大小,或者可使用默认的大小。大多数情况下,默认值就足够大了。
构造函数如下:
BufferedReader(Reader in) 创建一个使用默认大小输入缓冲区的缓冲字符输入流。 BufferedReader(Reader in, int sz) 创建一个使用指定大小输入缓冲区的缓冲字符输入流。
reader方法如下:
int read() 读取单个字符。 int read(char[] cbuf, int off, int len) 将字符读入数组的某一部分。 String readLine() 读取一个文本行。
BufferWriter
将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。可以指定缓冲区的大小,或者接受默认的大小。在大多数情况下,默认值就足够大了。
构造方法如下:
BufferedWriter(Writer out) 创建一个使用默认大小输出缓冲区的缓冲字符输出流。 BufferedWriter(Writer out, int sz) 创建一个使用给定大小输出缓冲区的新缓冲字符输出流。
writer方法如下:
void newLine() 写入一个行分隔符。 void write(char[] cbuf, int off, int len) 写入字符数组的某一部分。 void write(int c) 写入单个字符。 void write(String s, int off, int len) 写入字符串的某一部分。
接下来看看如何使用:
//复制文本文件 String sourceFilePath = "D:\\Learning\\doc\\git简单操作.txt"; String destFilePath = "D:\\Learning\\doc\\test1.txt"; BufferedReader br = null; BufferedWriter bw = null; try { FileReader fr = new FileReader(sourceFilePath); FileWriter fw = new FileWriter(destFilePath); br = new BufferedReader(fr); bw = new BufferedWriter(fw); String line = null;//一行一行的读取和写入 while ((line = br.readLine()) != null) { bw.write(line); bw.newLine(); } //使用缓冲对象一定要flush bw.flush(); } catch (IOException e) { e.printStackTrace(); } finally { try { if (br != null) { br.close(); } if (bw != null) { bw.close(); } } catch (IOException e) { e.printStackTrace(); } }
LineNumberReader
跟踪行号的缓冲字符输入流。此类定义了方法 setLineNumber(int) 和 getLineNumber(),它们可分别用于设置和获取当前行号。
默认情况下,行编号从 0 开始。该行号随数据读取在每个行结束符处递增,并且可以通过调用 setLineNumber(int) 更改行号。
但要注意的是,setLineNumber(int) 不会实际更改流中的当前位置;它只更改将由 getLineNumber() 返回的值。
下面看看简单的使用方式:
String sourceFilePath = "D:\\Learning\\doc\\git简单操作.txt"; FileReader fr = null; LineNumberReader lnr =null; try { fr = new FileReader(sourceFilePath); lnr = new LineNumberReader(fr); String line = null; lnr.setLineNumber(100);//设置起始行号为100 while((line=lnr.readLine())!=null){ System.out.println(lnr.getLineNumber()+":"+line); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally { if(lnr!=null){ try { lnr.close(); } catch (IOException e) { e.printStackTrace(); } } }
转换流
InputStreamReader
字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。
为了达到最高效率,可要考虑在 BufferedReader 内包装 InputStreamReader。例如:
BufferedReader in= new BufferedReader(new InputStreamReader(System.in));
构造方法如下:
InputStreamReader(InputStream in) 创建一个使用默认字符集的 InputStreamReader。 InputStreamReader(InputStream in, Charset cs) 创建使用给定字符集的 InputStreamReader。 InputStreamReader(InputStream in, CharsetDecoder dec) 创建使用给定字符集解码器的 InputStreamReader。 InputStreamReader(InputStream in, String charsetName) 创建使用指定字符集的 InputStreamReader。
读取方法如下:
int read() 读取单个字符。 int read(char[] cbuf, int offset, int length) 将字符读入数组中的某一部分。
举个例子,比如获取键盘输入的内容,并打印在控制台上,要求不能用Scanner对象:
//需求:把键盘输入的内容打印在控制台 InputStreamReader isr = new InputStreamReader(System.in); //方式一:直接读取 // int ch = 0; // while ((ch=isr.read())!=-1){ // System.out.print((char) ch+""); // } //方式二:读到缓冲区 int len = 0; char [] buffer =new char[1024]; while ((len=isr.read(buffer))!=-1){ System.out.print(new String(buffer,0,len)); } //方式三:使用缓冲流提供的便捷方法 BufferedReader br =new BufferedReader(isr) ; String line=null; while((line=br.readLine())!=null){ System.out.print(line); } }
OutputStreamWriter
是字符流通向字节流的桥梁:可使用指定的 charset 将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。
为了获得最高效率,可考虑将 OutputStreamWriter 包装到 BufferedWriter 中,以避免频繁调用转换器。例如:
Writer out = new BufferedWriter(new OutputStreamWriter(System.out));
构造方法如下:
OutputStreamWriter(OutputStream out) 创建使用默认字符编码的 OutputStreamWriter。 OutputStreamWriter(OutputStream out, Charset cs) 创建使用给定字符集的 OutputStreamWriter。 OutputStreamWriter(OutputStream out, CharsetEncoder enc) 创建使用给定字符集编码器的 OutputStreamWriter。 OutputStreamWriter(OutputStream out, String charsetName) 创建使用指定字符集的 OutputStreamWriter。
写入方法如下:
void write(char[] cbuf, int off, int len) 写入字符数组的某一部分。 void write(int c) 写入单个字符。 void write(String str, int off, int len) 写入字符串的某一部分。
举个例子,比如把文本文件的内容打印在控制台上,要求不能用print方法:
OutputStreamWriter osw=new OutputStreamWriter(System.out); String line="学习IO流"; //方式一:直接写入 // osw.write(line); // osw.close(); //方式二:使用缓冲流高效写入 BufferedWriter bw=new BufferedWriter(osw); bw.write(line); bw.close();
再来个例子,将键盘录入的数据写入到一个文件中:
String filePath="E:\\test.txt"; //需求:将键盘录入的数据写入到一个文件中。 BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in)); BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(filePath))); String line = null; while((line=bufr.readLine())!=null){ if(":wq".equals(line)) break; bufw.write(line.toUpperCase()); bufw.newLine(); bufw.flush(); }
上面的例子举得有点牵强,正在的用处在于上面这两个对象可以手动指定编码,比如读取文本文件,必须指定utf-8来编码。用FileReader是做不到的。
InputStreamReader isr = new InputStreamReader(new FileInputStream("E:\\test.txt"),"utf-8");
原文链接:https://www.cnblogs.com/2019lgg/p/11115777.html
如有疑问请与原作者联系
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
- 国外程序员整理的Java资源大全(全部是干货) 2020-06-12
- 2020年深圳中国平安各部门Java中级面试真题合集(附答案) 2020-06-11
- 2020年java就业前景 2020-06-11
- 04.Java基础语法 2020-06-11
- Java--反射(框架设计的灵魂)案例 2020-06-11
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash