Java的IO流——(七)
目录结构:
--------------------------------------FileInputOutputStream(字节流)-----------------
1 package IOTest; 2 3 import java.io.*; 4 public class FileInputOutputStream { 5 public static void main(String[] args)throws IOException { 6 FileOutputStream fos; 7 fos=new FileOutputStream(".\\test.txt"); //打开文件输出流 8 byte[] array={1,3,5,7,9,11,13,15,17,19}; 9 for(int i=0;i<array.length ;i++) 10 fos.write(array[i]); //写数据到文件输出流,也就是写入文件 11 fos.close(); //关闭文件输出流,即关闭文件 12 FileInputStream fis; 13 fis=new FileInputStream(".\\test.txt"); //打开文件输入流 14 int value; 15 while((value=fis.read())!=-1) //从文件输入流读数据,也就是从文件读数据 16 System.out.print(value+" "); 17 fis.close(); //关闭文件输入流,即关闭文件 18 } 19 }
-----------------------------------------FileReaderWriter(字符流)-----------------------
package IOTest; import java.io.*; public class FileReaderWriterStream { public static void main(String[] args)throws IOException { FileWriter fw; fw=new FileWriter(".\\filestream.txt"); //打开文件输出流 char array[]={'文','本','输','入','输','出','示','例','。'}; for(int i=0;i<array.length ;i++) fw.write(array[i]); //写数据到文件输出流,也就是写入文件 fw.close(); //关闭文件输出流,即关闭文件 FileReader fr; fr=new FileReader(".\\filestream.txt"); //打开文件输入流 int value; while((value=fr.read())!=-1) //从文件输入流读数据,也就是从文件读数据 System.out.print((char)value); fr.close(); //关闭文件输入流,即关闭文件 } }
结果:
追加文本
1 package IOTest; 2 3 import java.io.*; 4 public class FileAppendStream { 5 public static void main(String[] args)throws IOException { 6 String str="Welcom to Java!"; 7 FileWriter fw; 8 fw=new FileWriter(".\\filestream.txt",true); //打开输出流 9 fw.write(str); //写字符串到文件输出流 10 fw.close(); //关闭文件输出流 11 FileReader fr; 12 fr=new FileReader(".\\filestream.txt"); //打开输入流 13 int value; 14 while((value=fr.read())!=-1) //从文件输入流读数据 15 System.out.print((char)value); 16 fr.close(); //关闭文件输入流 17 } 18 }
结果:
-------------------------------------------BuffereReaderWriter(缓存字符流)-----------------------------
带缓存的流
1 package IOTest; 2 3 import java.io.*; 4 public class BuffereStream { 5 public static void main(String[] args) throws IOException{ 6 InputStreamReader isr = new InputStreamReader(System.in); 7 BufferedReader br = new BufferedReader(isr); 8 OutputStream os=new FileOutputStream(".\\buffer.bin");//打开字节文件写 9 OutputStreamWriter fout = new OutputStreamWriter(os);//字节流转换字符流 10 BufferedWriter bout=new BufferedWriter(fout);//字符流转换为缓冲流 11 String str; 12 while(true){ 13 str=br.readLine(); //从键盘读一行字符 14 if(str.equals("end")) 15 break; 16 bout.write(str); //将读入的字符写入文件中 17 bout.newLine(); //写行分隔符到文件中 18 } 19 bout.close(); //关闭文件 20 InputStream is=new FileInputStream(".\\buffer.bin");//打开字节文件读 21 InputStreamReader fin = new InputStreamReader(is);//字节流转换字符流 22 BufferedReader bin=new BufferedReader(fin);//字符流转换为缓冲流 23 while((str=bin.readLine())!=null)//从文件中读一行字符 24 System.out.println(str); //显示 25 bin.close(); 26 } 27 }
结果:
InputStreamReader 是字节流通向字符流的桥梁。
---------------------------------------BufferReaderWriter------------------------------------------------------------------
带缓存的流
1 package IOTest; 2 3 import java.io.*; 4 public class BuffereStream_1 { 5 public static void main(String[] args) throws IOException{ 6 InputStreamReader isr = new InputStreamReader(System.in); 7 BufferedReader br = new BufferedReader(isr); 8 FileWriter fout = new FileWriter(".\\buffer.txt");//打开文本文件写 9 BufferedWriter bout=new BufferedWriter(fout);//字符流转换为缓冲流 10 String str; 11 while(true){ 12 str=br.readLine(); //从键盘读一行字符 13 if(str.equals("end")) 14 break; 15 bout.write(str); //将读入的字符写入文件中 16 bout.newLine(); //写行分隔符到文件中 17 } 18 bout.close(); //关闭文件 19 FileReader fin=new FileReader(".\\buffer.txt");//打开文本文件读 20 BufferedReader bin=new BufferedReader(fin);//字符流转换为缓冲流 21 while((str=bin.readLine())!=null)//从文件中读一行字符 22 System.out.println(str); //显示 23 bin.close(); 24 } 25 }
结果:
-----------------------------------------------DataInputOutputStream----------------------------------------
输入任意类型数据
1 package IOTest; 2 3 import java.io.*; 4 public class DataInputOutputStream { 5 public static void main(String[] args) throws IOException{ 6 char c='A'; 7 int i=3721; 8 long l=123456; 9 float f=3.14f; 10 double d=3.1415926535; 11 String str="基本类型数据输入输出示例"; 12 DataOutputStream output=new DataOutputStream(new FileOutputStream(".\\datastream.dat")); 13 output.writeChar(c); 14 output.writeInt(i); 15 output.writeLong(l); 16 output.writeFloat(f); 17 output.writeDouble(d); 18 output.writeUTF(str); 19 output.close(); 20 DataInputStream input=new DataInputStream(new FileInputStream(".\\datastream.dat")); 21 char cc=input.readChar(); 22 int ii=input.readInt(); 23 long ll=input.readLong(); 24 float ff=input.readFloat(); 25 double dd=input.readDouble(); 26 String sstr=input.readUTF(); 27 input.close(); 28 System.out.println(cc+"\n"+ii+"\n"+ll+"\n"+ff+"\n"+dd+"\n"+sstr); 29 } 30 }
结果:
----------------------------------------------ObjectStream------------------------------------------------------
将类对象写入文件(必须实现序列化接口)
1 package IOTest; 2 import java.io.*; 3 public class ObjectStream{ 4 public static void main(String arg[])throws Exception{ 5 Employee e1 = new Employee(1001,"Wang",5678.50); 6 FileOutputStream fos = new FileOutputStream(".\\object.dat"); 7 ObjectOutputStream out = new ObjectOutputStream(fos); //创建输出对象流 8 out.writeObject(e1); //写入对象 9 out.close(); 10 FileInputStream fis = new FileInputStream(".\\object.dat"); 11 ObjectInputStream in = new ObjectInputStream(fis); //创建输入对象流 12 Employee e2 = (Employee)in.readObject(); //读取对象 13 System.out.println("Id: "+e2.id); 14 System.out.println("Name: "+e2.name); 15 System.out.println("Salary: "+e2.salary); 16 in.close(); 17 } 18 } 19 class Employee implements Serializable{ //必须实现Serializable接口 20 int id; 21 String name; 22 double salary; 23 Employee(int i,String n,double s) 24 { 25 id=i; 26 name=n; 27 salary=s; 28 } 29 }
结果:
不希望序列化的可以加transient修饰。
-----------------------------------------------------LineNumberReader----------------------------------------------------
获取带行号的内容
1 package IOTest; 2 import java.io.*; 3 public class AddlineNo { 4 public static void main(String[] args) { 5 String str=null; 6 try{ 7 FileReader file=new FileReader(".\\buffer.txt"); 8 LineNumberReader in=new LineNumberReader(file); 9 while((str=in.readLine())!=null) 10 System.out.println(in.getLineNumber()+":"+str); 11 in.close(); 12 }catch(IOException e){ 13 System.out.println("文件打不开或读文件错误!"); 14 } 15 } 16 }
结果:
两个案例:
--------------------------------------------文件流实现拷贝图片----------------------------------------
package cn.itcast.output; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; /* 需求: 拷贝一张图片。 */ public class CopyImage { public static void main(String[] args) throws IOException { //找到目标文件 File inFile = new File("F:\\美女\\1.jpg"); File destFile = new File("E:\\1.jpg"); //建立数据的输入输出通道 FileInputStream fileInputStream = new FileInputStream(inFile); FileOutputStream fileOutputStream = new FileOutputStream(destFile); //追加数据.... //每新创建一个FileOutputStream的时候,默认情况下FileOutputStream 的指针是指向了文件的开始的位置。 每写出一次,指向都会出现相应移动。 //建立缓冲数据,边读边写 byte[] buf = new byte[1024]; int length = 0 ; while((length = fileInputStream.read(buf))!=-1){ //最后一次只剩下了824个字节 fileOutputStream.write(buf,0,length); //写出很多次数据,所以就必须要追加。 } //关闭资源 原则: 先开后关,后开先关。 fileOutputStream.close(); fileInputStream.close(); } }
--------------------------------------------缓冲流实现拷贝图片----------------------------------------
package cn.itcast.buffered; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; /* 练习: 使用缓冲输入输出字节流拷贝一个图片。 */ public class CopyImage { public static void main(String[] args) throws IOException { //找到目标文件 File inFile = new File("F:\\美女\\1.jpg"); File outFile = new File("E:\\1.jpg"); //建立数据输入输出通道 FileInputStream fileInputStream = new FileInputStream(inFile); FileOutputStream fileOutputStream = new FileOutputStream(outFile); //建立缓冲输入输出流 BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream); BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream); //边读边写 int content = 0; // int length = bufferedInputStream.read(buf); 如果传入了缓冲数组,内容是存储到缓冲数组中,返回值是存储到缓冲数组中的字节个数。 while((content = bufferedInputStream.read())!=-1){ // 如果使用read方法没有传入缓冲数组,那么返回值是读取到的内容。 bufferedOutputStream.write(content); // bufferedOutputStream.flush(); //一般带缓冲的不刷新,否则缓冲没意义 } //关闭资源 bufferedInputStream.close(); bufferedOutputStream.close(); } }
注意:
如果传入了缓冲数组,内容是存储到缓冲数组中,返回值是存储到缓冲数组中的字节个数。
如果使用read方法没有传入缓冲数组,那么返回值是读取到的内容。
不加缓存数组:
//边读边写 int content = 0; while((content = bufferedInputStream.read())!=-1){ // 如果使用read方法没有传入缓冲数组,那么返回值是读取到的内容。 bufferedOutputStream.write(content); // bufferedOutputStream.flush(); //一般带缓冲的不刷新,否则缓冲没意义 }
加缓存数组
//建立缓冲数据,边读边写 byte[] buf = new byte[1024]; int length = 0 ; while((length = fileInputStream.read(buf))!=-1){ //最后一次只剩下了824个字节 fileOutputStream.write(buf,0,length); //写出很多次数据,所以就必须要追加,读取buf数组中从0开始的length长度的字节数。 }
补充:org.apache.commons.io.IOUtils可以简单的将一个inputStream的文件读取到另一个outputStream,实现文件的拷贝,例如:
只用传两个参数,第一个传递InputStream,第二个传递OutputStream
package cn.qlq.craw.Jsoup; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URL; import java.net.URLConnection; import org.apache.commons.io.IOUtils; public class IOutilsDownloadFile { public static void main(String[] args) throws IOException { String url = "http://qiaoliqiang.cn/fileDown/zfb.bmp"; URL url1 = new URL(url); URLConnection conn = url1.openConnection(); InputStream inputStream = conn.getInputStream(); String path = "C:\\Users\\liqiang\\Desktop\\test.bmp"; OutputStream outputStream = new FileOutputStream(path); // 利用IOutiks拷贝文件,简单快捷 IOUtils.copy(inputStream, outputStream); } }
----------------------字符流和字节流的区别-------------------
1. 什么是流
Java中的流是对字节序列的抽象,我们可以想象有一个水管,只不过现在流动在水管中的不再是水,而是字节序列。和水流一样,Java中的流也具有一个“流动的方向”,通常可以从中读入一个字节序列的对象被称为输入流;能够向其写入一个字节序列的对象被称为输出流。
2. 字节流
Java中的字节流处理的最基本单位为单个字节,它通常用来处理二进制数据。Java中最基本的两个字节流类是InputStream和OutputStream,它们分别代表了组基本的输入字节流和输出字节流。InputStream类与OutputStream类均为抽象类,我们在实际使用中通常使用Java类库中提供的它们的一系列子类。下面我们以InputStream类为例,来介绍下Java中的字节流。
InputStream类中定义了一个基本的用于从字节流中读取字节的方法read,这个方法的定义如下:
public abstract int read() throws IOException;
这是一个抽象方法,也就是说任何派生自InputStream的输入字节流类都需要实现这一方法,这一方法的功能是从字节流中读取一个字节,若到了末尾则返回-1,否则返回读入的字节。关于这个方法我们需要注意的是,它会一直阻塞知道返回一个读取到的字节或是-1。另外,字节流在默认情况下是不支持缓存的,这意味着每调用一次read方法都会请求操作系统来读取一个字节,这往往会伴随着一次磁盘IO,因此效率会比较低。有的小伙伴可能认为InputStream类中read的以字节数组为参数的重载方法,能够一次读入多个字节而不用频繁的进行磁盘IO。那么究竟是不是这样呢?我们来看一下这个方法的源码:
public int read(byte b[]) throws IOException { return read(b, 0, b.length); }
它调用了另一个版本的read重载方法,那我们就接着往下追:
public int read(byte b[], int off, int len) throws IOException { if (b == null) { throw new NullPointerException(); } else if (off < 0 || len < 0 || len > b.length - off) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return 0; } int c = read(); if (c == -1) { return -1; } b[off] = (byte)c; int i = 1; try { for (; i < len ; i++) { c = read(); if (c == -1) { break; } b[off + i] = (byte)c; } } catch (IOException ee) { } return i; }
从以上的代码我们可以看到,实际上read(byte[])方法内部也是通过循环调用read()方法来实现“一次”读入一个字节数组的,因此本质来说这个方法也未使用内存缓冲区。要使用内存缓冲区以提高读取的效率,我们应该使用BufferedInputStream。
3. 字符流(重要)
Java中的字符流处理的最基本的单元是Unicode码元(大小2字节),它通常用来处理文本数据。Writer和Reader这两个抽象类是是基本的字符流的祖先。所谓Unicode码元,也就是一个Unicode代码单元,范围是0x0000~0xFFFF。在以上范围内的每个数字都与一个字符相对应,Java中的String类型默认就把字符以Unicode规则编码而后存储在内存中。然而与存储在内存中不同,存储在磁盘上的数据通常有着各种各样的编码方式。使用不同的编码方式,相同的字符会有不同的二进制表示。实际上字符流是这样工作的:
- 输出字符流:把要写入文件的字符序列(实际上是Unicode码元序列)转为指定编码方式下的字节序列,然后再写入到文件中;
- 输入字符流:把要读取的字节序列按指定编码方式解码为相应字符序列(实际上是Unicode码元序列从)从而可以存在内存中。
我们通过一个demo来加深对这一过程的理解,示例代码如下:
import java.io.FileWriter; import java.io.IOException; public class FileWriterDemo { public static void main(String[] args) { FileWriter fileWriter = null; try { try { fileWriter = new FileWriter("demo.txt"); fileWriter.write("demo"); } finally { fileWriter.close(); } } catch (IOException e) { e.printStackTrace(); } } }
以上代码中,我们使用FileWriter向demo.txt中写入了“demo”这四个字符,我们用十六进制编辑器WinHex查看下demo.txt的内容:
从上图可以看出,我们写入的“demo”被编码为了“64 65 6D 6F”,但是我们并没有在上面的代码中显式指定编码方式,实际上,在我们没有指定时使用的是操作系统的默认字符编码方式来对我们要写入的字符进行编码。
由于字符流在输出前实际上是要完成Unicode码元序列到相应编码方式的字节序列的转换,所以它会使用内存缓冲区来存放转换后得到的字节序列,等待都转换完毕再一同写入磁盘文件中。
下面附两个例子,一个写内容到文件,一个读取文件内容:
一个利用FileWriter写文件的例子:
@Test public void test1() throws IOException{ File file = new File("./test.txt"); if(!file.exists()){//如果文件不存在就创建文件 file.createNewFile(); } Writer writer = new FileWriter(file);//此构造方法也接收一个URI参数(文件名字) writer.write("this is test"); writer.close(); }
结果:
一个利用FileReader读文件的例子:
@Test public void test2() throws IOException{ Reader reader = new FileReader("./test.txt");//此构造方法也接收一个URI参数(文件名字) int read = 0; while ((read = reader.read())!=-1) { System.out.print((char)read); } reader.close(); }
结果:
this is test
4. 字符流与字节流的区别
经过以上的描述,我们可以知道字节流与字符流之间主要的区别体现在以下几个方面:
- 字节流操作的基本单元为字节;字符流操作的基本单元为Unicode码元。
- 字节流默认不使用缓冲区;字符流使用缓冲区。
- 字节流通常用于处理二进制数据,实际上它可以处理任意类型的数据,但它不支持直接写入或读取Unicode码元;字符流通常处理文本数据,它支持写入及读取Unicode码元。
5.InputStreamReader:字节到字符的桥梁。OutputStreamWriter:字符到字节的桥梁。
1. InputStreamReader 是字节流通向字符流的桥梁
// 该方法中nputStreamReader使用系统默认编码读取文件. private static void testReadFile(File file) throws IOException { FileInputStream fis = new FileInputStream(file); InputStreamReader ins = new InputStreamReader(fis); int len = 0; while ((len = ins.read()) != -1) { System.out.print((char) len); } ins.close(); fis.close(); } // 该方法使用指定编码读取文件 private static void testReadFile(File file, String encod) throws IOException { FileInputStream fis = new FileInputStream(file); InputStreamReader ins = new InputStreamReader(fis, encod); int len = 0; while ((len = ins.read()) != -1) { System.out.print((char) len); } ins.close(); } }
另一种Runtime执行的时候inputStream转BufferReader
public static void main(String[] args) { String cmd1 = "jps | findstr server"; Runtime runtime = Runtime.getRuntime(); Process process = null; try { process = runtime.exec(cmd1); InputStream inputStream = process.getInputStream(); Reader reader = new InputStreamReader(inputStream); BufferedReader bufferedReader = new BufferedReader(reader); String line = null; while ((line = bufferedReader.readLine()) != null) { System.out.println(line); } } catch (IOException e) { e.printStackTrace(); } }
2. OutputStreamWriter 是字符流通向字节流的桥梁
// 使用系统码表将信息写入到文件中 private static void testWriteFile(File file) throws IOException { FileOutputStream fos = new FileOutputStream(file); OutputStreamWriter ops = new OutputStreamWriter(fos); ops.write("中国"); ops.close(); } // 使用指定码表,将信息写入到文件中 private static void testWriteFile(File file, String encod) throws IOException { FileOutputStream fos = new FileOutputStream(file); OutputStreamWriter ops = new OutputStreamWriter(fos, encod); ops.write("中国"); ops.close();
参考:https://www.cnblogs.com/qlqwjy/p/7513187.html
补充:关于FileInputStream中的读取方法的解释
如果传入了缓冲数组,内容是存储到缓冲数组中,返回值是存储到缓冲数组中的字节个数。
如果使用read方法没有传入缓冲数组,那么返回值是读取到的内容。
比如,一个文件内容如下:
what?1
(1)不传缓冲数组:
FileInputStream fileInputStream = new FileInputStream(new File("G:/test.txt")); int readData = -1; while ((readData = fileInputStream.read()) != -1) { System.out.println((char) readData); }
结果:
w
h
a
t
?
1
(2)传入缓冲数组:
public static void main(String[] args) throws IOException { FileInputStream fileInputStream = new FileInputStream(new File("G:/test.txt")); byte[] allDatas = new byte[0]; byte[] buffer = new byte[1024]; int length = 0; // while ((length = fileInputStream.read(buffer, 0, 1024)) != -1) { // 等价于上面注释掉的 while ((length = fileInputStream.read(buffer)) != -1) { allDatas = ArrayUtils.addAll(allDatas, ArrayUtils.subarray(buffer, 0, length)); } System.out.println(new String(allDatas)); }
结果:
what?1
源码查看:
(1)read: 调用本地方法获取数据
/** * Reads a byte of data from this input stream. This method blocks * if no input is yet available. * * @return the next byte of data, or <code>-1</code> if the end of the * file is reached. * @exception IOException if an I/O error occurs. */ public int read() throws IOException { Object traceContext = IoTrace.fileReadBegin(path); int b = 0; try { b = read0(); } finally { IoTrace.fileReadEnd(traceContext, b == -1 ? 0 : 1); } return b; } private native int read0() throws IOException;
(2)read(byte[])和read(byte[], int off, int length):将内容读取到缓存数组中
public int read(byte b[]) throws IOException { Object traceContext = IoTrace.fileReadBegin(path); int bytesRead = 0; try { bytesRead = readBytes(b, 0, b.length); } finally { IoTrace.fileReadEnd(traceContext, bytesRead == -1 ? 0 : bytesRead); } return bytesRead; } public int read(byte b[], int off, int len) throws IOException { Object traceContext = IoTrace.fileReadBegin(path); int bytesRead = 0; try { bytesRead = readBytes(b, off, len); } finally { IoTrace.fileReadEnd(traceContext, bytesRead == -1 ? 0 : bytesRead); } return bytesRead; } /** * Reads a subarray as a sequence of bytes. * @param b the data to be written * @param off the start offset in the data * @param len the number of bytes that are written * @exception IOException If an I/O error has occurred. */ private native int readBytes(byte b[], int off, int len) throws IOException;
两个方法内部原理一样,都是调用了一个本地方法,传入三个参数,第一个参数是需要缓存的字节数组,第二个是数组的起始地址,length是最大读取的字节数。