JAVA输入输出流(.IO)

补:编码方式

在不同编码方式下,得到的字节流不一样。

import java.io.UnsupportedEncodingException;
import java.util.*;
import java.util.Scanner;

public class Main {
	
	public static void main(String args[]) throws UnsupportedEncodingException {
		Scanner sc = new  Scanner(System.in);
		String str="中华人民共和国";
		byte[] bytes = str.getBytes("GBK");
		//以GBK / GB2312编码转化为字节
		System.out.println(bytes.length);
		//输出字符长度:14
		byte[] bytes = str.getBytes("UTF-8");
		//以UTF-8编码转化为字节
		System.out.println(bytes.length);
		//输出字符长度:21
		sc.close();
	}
}

如果是新建.txt文件,系统自动在最前面加入几个字节
在不同编码方式下,得到的字节流不一样。

基础知识

输入:泛指对某设备进行数据的输入
输出:泛指从某设备进行数据的输出

流(Stream)是从源读取并写入目标的数据序列。
输入流用于从源读取数据。

输出流用于将数据写入目标。

流的类型

按数据流的方向不同: 输入流,输出流。
按处理数据单位不同: 字节流,字符流。
(1) 字节流:数据流中最小的数据单元是字节。
(2)字符流:数据流中最小的数据单元是字符, Java中的字符是Unicode编码,一个字符占用两个字节。
按功能不同: 节点流,处理流。
(1)程序用于直接操作目标设备所对应的类叫节点流。
(2)程序通过一个间接流类去调用节点流类,以达到更加灵活方便地读写各种类型的数据,这个间接流类就是处理流。

字节流(Byte)

JAVA.IO
字节流用于读取和写入单个字节(8位)的数据。

所有字节流类都派生自称为InputStream和OutputStream的基本抽象类。

InputStream类

InputStream是字节输入流基类,是抽象类抽象类(不能直接new,创建对象)),是表示字节输入流的所有类的超类。
我们主要学习他的FileInputStream 文件流的子类。
FileInputStream:字节文件输入流,从文件系统中的某个文件中获得输入字节,用于读取原始字节流。

FileInputStream 文件流

输入流读取的步骤
image

常用构造方法:

FileInputStream(File file)// 通过打开一个到实际文件的连接来创建一个FileInputStream,该文件通过文件系统中的File对象file指定
FileInputStream(String name)// 通过打开一个到实际文件的连接来创建一个FileInputStream,该文件通过文件系统中的路径name指定

常用成员方法:
int read

int read()//从输入流中读取一个字符,返回读到的字节编码,-1代表读取结束。
int read(byte[])//效率比较高,返回读入数据的数量,并且从输入流中将数据放到字符串数组。
int read(byte[],int begin,int end)//当有需要固定某些字符时候才使用,不常用

int read()
注意:
虽然返回值是读取到的数据,-1代表读取结束。
但是read方法本身类似于指针:
第一次调用读取第一个字符,第二次调用读取第二个字符。
测试代码:

import java.io.FileInputStream;
import java.io.IOException;
import java.util.*;
public class Main {
	
	public static void main(String args[]) throws IOException {
		FileInputStream fis=new FileInputStream("E:\\data.txt");
		System.out.println(fis.available());//输出字节长度
		
		//一次读取一个字节,获取ASCII值
		//如果越界,返回-1
		int len=fis.read();//读取了第1个
		System.out.println(len);
		
		len=fis.read();//读取了第2个
		System.out.println(len);
		
		len=fis.read();//读取了第3个
		System.out.println(len);
		
		len=fis.read();//读取了第4个
		System.out.println(len);//如果越界,返回-1
		 long l1=System.currentTimeMillis();
		System.out.println(fis.available());//输出字节长度
		  len=fis.read();
		  while(len!=-1) {
			  System.out.println(len);
			  len=fis.read();
		  }
		  long l2=System.currentTimeMillis();
		  System.out.println(l2-l1);//计算读取时间
		 fis.close();
		 
	}
}

int read(byte[])
注意:返回值是读入的数据的数量
并且效率比较高,
因为每次访问消耗时间较大,而这个函数每次访问读入的数据多,所有效率高
测试代码:


import java.io.FileInputStream;
import java.io.IOException;
import java.util.*;
public class Main {
	
	public static void main(String args[]) throws IOException {
		 
		  FileInputStream fis2=new FileInputStream("E:\\data.txt");
		  byte[] bytes=new byte[2];
		int  le=fis2.read(bytes);
		  System.out.println(le);
		  System.out.println(Arrays.toString(bytes));
		  
		  bytes=new byte[2];
		  le=fis2.read(bytes);
		  System.out.println(le);
		  System.out.println(Arrays.toString(bytes));
		  
		  bytes=new byte[2];
		  le=fis2.read(bytes);
		  System.out.println(le);
		  System.out.println(Arrays.toString(bytes));
		  
		  bytes=new byte[2];
		  le=fis2.read(bytes);
		  System.out.println(le);
		  System.out.println(Arrays.toString(bytes));
		  fis2.close();
/*结果:
*2
[97, 100]
2
[115, 100]
2
[115, 103]
2
[102, 100]
*可得:返回的数据是获取的数据的长度,而不是指针位置。
*/
	}
}

遍历输出:
上面直接输出得到的都是数字,如果想还原实际数据,需要转化为字符串再输出。


import java.io.FileInputStream;
import java.io.IOException;
import java.util.*;

public class Main {

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

		FileInputStream fis3 = new FileInputStream("C:\\Users\\WangYa\\Desktop\\java io\\jaba.txt");
		byte[] bytes2 = new byte[2];
		int le2 = fis3.read(bytes2);
		while (le2 != -1) {
			String strr = new String(bytes2, 0, le2);
			System.out.println(strr);

			le2 = fis3.read(bytes2);
		}
		System.out.println("------");
		// 再简化
		while ((fis3.read(bytes2) != -1)) {
			String strr = new String(bytes2, 0, le2);
			System.out.println(strr);
		}
		fis3.close();
	}
}
/*
结果:
ad
sd
sg
fd
gh
hy
kj
------
分析可知:
第二个循环输出没有执行。所以read(bytes)在系统内部也是类似于指针的形式执行。

*/

OutputStream类

OutputStream是字节输出流基类,是抽象类,是表示输出字节流的所有类的超类。
我们主要学习他的FileOutputStream 文件流的子类。
FileOutputStream:字节文件输出流,用于将数据写入到File,从程序中写入到其他位置。

FileOutputStream 文件流

构造方法:

FileOutputStream(File file)// 创建一个向指定File对象表示的文件中写入数据的文件输出流
FileOutputStream(File file, boolean append)// 创建一个向指定File对象表示的文件中写入数据的文件输出流
FileOutputStream(String name)// 创建一个向具有指定名称的文件中写入数据的输出文件流
FileOutputStream(String name, boolean append)// 创建一个向具有指定name的文件中写入数据的输出文件流

常用方法:
输出数据(从程序到其他位置。)
注意:默认对于某个输出流都是覆盖输出到文件,但是对于同一输出流的多次输出是进行追加。

void write(byte[] b)// 将 b.length 个字节从指定的 byte 数组写入此输出流
void write(byte[] b, int off, int len)// 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流
abstract void write(int b) // 将指定的字节写入此输出流

关闭输出流

void close()// 关闭此输出流并释放与此流有关的所有系统资源
void flush() // 刷新此输出流并强制写出所有缓冲的输出字节

测试代码

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.*;

public class Main {

	public static void main(String args[]) throws IOException, InterruptedException {
		FileOutputStream fos = new FileOutputStream("data1.dat");
		fos.write(65);// a
		fos.write(49);// 1
		fos.write(97);// A
		fos.close();
/*结果:A1a*/

		byte[] bytes = "ceshiceshiceshiceshiceshi123456".getBytes();
		FileOutputStream fos2 = new FileOutputStream("data2.dat");
		fos2.write(bytes);
		fos2.write("测试".getBytes());
		fos2.close();
/*结果:ceshiceshiceshiceshiceshi123456测试*/

		FileOutputStream fos3 = new FileOutputStream("data3.dat");
		byte[] bytes2 = "测试ceshiceshiceshiceshiceshi123456".getBytes();
		fos3.write(bytes,2,10);//从2开始写10个字符
		fos3.close();
/*结果:shiceshice*/
	}
}

JAVA字节处理流

image

Buffering缓冲流

在读入或写出时,对数据进行缓存,以减少I/O的次数

BufferedInputStream

父子类关系:
InputStream->FilterInputStream->BufferedInputStream
特点:
当创建BufferedInputStream时,将创建一个内部缓冲区数组。
当从流中读取或跳过字节时,内部缓冲区将根据需要从所包含的输入流中重新填充,一次有多个字节。
mark操作会记住输入流中的一点,并且reset操作会导致从最近的mark操作之后读取的所有字节在从包含的输入流中取出新的字节之前重新读取。

相对于 FileInputStream优点
使用BufferedInputStream读资源比FileInputStream读取资源的效率高(BufferedInputStream的read方法会读取尽可能多的字节,执行read时先从缓冲区读取,当缓冲区数据读完时再把缓冲区填满。),因此,当每次读取的数据量很小时,FileInputStream每次都是从硬盘读入,而BufferedInputStream大部分是从缓冲区读入。读取内存速度比读取硬盘速度快得多,因此BufferedInputStream效率高,且FileInputStream对象的read方法会出现阻塞;BufferedInputStream的默认缓冲区大小是8192字节。当每次读取数据量接近或远超这个值时,两者效率就没有明显差别了。
BufferedOutputStream和FileOutputStream同理,差异更明显一些。

构造方法:

BufferedInputStream(InputStream in)
创建一个 BufferedInputStream并保存其参数,输入流 in ,供以后使用。
BufferedInputStream(InputStream in, int size)
创建 BufferedInputStream具有指定缓冲区大小,并保存其参数,输入流 in ,供以后使用。

常用方法:
读入

int read()//和InputStream的差不多。
int read(byte[] b, int off, int len)//从给定的偏移开始,将字节输入流中的字节读入指定的字节数组。

关闭:

void close()//关闭此输入流并释放与流相关联的任何系统资源。

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class BufferedStreamTest {

	public static void main(String[] args) throws IOException {
        BufferedInputStream bis1=
        		new BufferedInputStream(new FileInputStream("data2.dat"));
       
        long l2=System.currentTimeMillis();
        
        byte[] a=new byte[10000];
      int len=  bis1.read(a);
	    //读一个字节,放在缓冲区(默认为8k/8192字节),还继续在访问,不返回,故效率高
	    System.out.println(len);
	    
	    long l1=System.currentTimeMillis();
	    
	    System.out.println(l1-l2);
	}

}

DataConversion数据流

父子类关系:(以DataOutputStream为例子)
InputStream->FilterInputStream->DataOutputStream
特点:
数据输出流允许应用程序以适当方式将基本 Java 数据类型写入输出流中。
然后,应用程序可以使用数据输入流将数据读入。
即:系统会自动转化数据为java基本数据类型。
注意:

  1. 对于一个数据流内部也是类似于指针,第一次读取第一个元素,第二次读取第二个元素。
  2. 按什么格式输入,就按什么格式输出。如果不匹配会抛出异常
  3. 输入输出字符串使用UTF dos.writeUTF("String");String str=dis.readUTF();

Data In/Out putStream

测试代码


import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;


public class Datatext {
public static void main(String[] args) throws IOException {
DataOutputStream  dos1=
  new DataOutputStream(new FileOutputStream("data2.dat"));
     dos1.writeInt(100);//int类型
     dos1.writeLong(2121212121212l);//long类型
     dos1.writeUTF("你好sfsafsfsafsf");//String类型
     dos1.close();
DataInputStream dis1
  =new DataInputStream(new FileInputStream("data2.dat"));
	//第一个数据一定要是int类型,改为其他类型会报错
     int data1=dis1.readInt();
     System.out.println(data1);
     long l=dis1.readLong();
     System.out.println(l);
     String str1=dis1.readUTF();
     System.out.println(str1);
     dis1.close();
}
}

RandomAccessFile类

父子类关系:
java.lang.Object-> java.io.RandomAccessFile

特点(优点)

支持任意访问的方式,程序可以直接跳到任意地方来读写数据。
也就是说,可是实现对文本进行零内存追加数据这个非常常用的操作。
概述
此类的实例支持对随机访问文件的读取和写入。
随机访问文件的行为类似存储在文件系统中的一个大型 byte 数组。
存在指向该隐含数组的光标或索引,称为文件指针;
输入操作 从文件指针开始读取字节,并随着对字节的读取而前移此文件指针。

输出操作从文件指针开始写入字节,并随着对字节的写入而前移此文件指针。

构造方法

RandomAccessFile(File file, String mode)
RandomAccessFile(String name, String mode)

对于mode,Java给开发者提供了四种mode:

  • r: 表示以只读方式打开,调用结果对象的任何write方法都将导致抛出IOException
  • rw: 打开以便读取和写入,如果该文件尚不存在,则尝试创建该文件
  • rws: 打开以便读取和写入,相对于"rw",还要求对文件内容或元数据的每个更新都同步写入到底层存储设备
  • rwd: 打开以便读取和写入,相对于"rw",还要求对文件内容的每个更新都同步写入到底层存储设备

常用方法

重要/基础

void seek(long pos) 重要,设置到此文件开头测量到的文件指针偏移量,在该位置发生下一个读取或写入操作
int skipBytes(int n) 重要,尝试跳过输入的n个字节以丢弃跳过的字节,返回跳过的字节数
void close() 重要,关闭此随机访问文件流并释放与该流关联的所有系统资源
long getFilePointer() 返回此文件中的当前偏移量
long length() 返回此文件的长度

从文件读入

int read() 从此文件中读取一个数据字节
int read(byte[] b) 将最多b.length个数据字节从此文件读入byte数组,返回读入的总字节数,如果由于已经达到文件末尾而不再有数据,则返回-1。在至少一个输入字节可用前,此方法一直阻塞
int read(byte[] b, int off, int len) 将最多len个数据字节从此文件的指定初始偏移量off读入byte数组
boolean readBoolean() 从此文件读取一个boolean,其余readByte()、readChar()、readDouble()等类似
String readLine() 从此文件读取文本的下一行

向文件写入

void write(byte[] b) 将b.length个字节从指定byte数组写入到此文件中
void write(byte[] b, int off, int len) 将len个字节从指定byte数组写入到此文件,并从偏移量off处开始
void write(int b) 向此文件写入指定的字节
void writeBoolean(boolean v) 按单字节值将boolean写入该文件,其余writeByte(int v)、writeBytes(String s)、writeChar(int v)等都类似

实现在文件末尾后面插入

public static void randomWrite(String path){
    try{
        //以读写的方式建立一个RandomAccessFile对象
        RandomAccessFile raf=new RandomAccessFile(path, "rw");
        //将记录指针移动到文件最后
        raf.seek(raf.length());
        raf.write("追加信息\n".getBytes());
    }catch(Exception e){
        e.printStackTrace();
    }
}

测试代码:

import java.awt.RadialGradientPaint;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;

import org.omg.Messaging.SYNC_WITH_TRANSPORT;

public class RandomAccessFileTest {

	public static void main(String[] args) throws IOException {
		
		//文件指针
		RandomAccessFile raf1=
				new RandomAccessFile("data1.dat", "rw");
		raf1.writeInt(1000);
		raf1.writeDouble(1.1231);
		raf1.writeUTF("13213");
		
		//指针回位
		raf1.seek(0);
		int i=raf1.readInt();
		double d=raf1.readDouble();
		String str1=raf1.readUTF();
		System.out.println(i);
		System.out.println(d);
		System.out.println(str1);
		raf1.close();
		
		
	}

}

字符流(Character )

字符流用于读取和写入单个数据字符。

所有字符流类都派生自基本抽象类Reader和Writer。

FileReader类

常用构造方法:

FileReader(File file)
FileReader(String name)

常用成员方法:

int read()
从输入流中读取一个字符的数据,返回读到的字符编码,-1表示读取结束。
int read(char[] chars)
从输入流中将数据读入chars数组中,返回读取字符的数量,-1表示读取结束。
int read(char[] chars, int off, int len)
将字符读入数组中的某一部分,从数组的off位置处开始存放数据,返回读取字符数量,-1表示读取结束。

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class FileReadWriterTest {

	public static void main(String[] args) throws IOException {
		// 写
		FileWriter writer = // 以字符为单位写数据:“中” 以GBK输入需要2个字节,1个字符
				new FileWriter("data1.txt", false);
		// 默认为覆盖false ,true代表追加。

		writer.write(65);
		writer.write("\n中国\n".toCharArray());
		writer.write("中华人民共和国\n".toCharArray(), 2, 4);
		writer.write("中国人民解放军\n");
		writer.write("中华人民共和国\n", 2, 6);
		writer.close();

		// 读
		FileReader reader = new FileReader("data1.txt");
		char[] chars = new char[5];
		int len = 0;
		while ((len = reader.read(chars)) != -1) {
			System.out.println(chars);
			String str = new String(chars, 0, len);
			System.out.println(str);
		}

	}

}

FileWriter类

常用构造方法:

FileWriter(File file)
FileWriter(File file , boolean append)
FileWriter(String name)
FileWriter(String name, boolean append)

常用成员方法:

void write(int c) 写入单个字符
void write(char[] ch) 写入字符数组
void write(char[] ch , int off, int len) 写入字符数组的一部分
void write(String str)写入字符串
void write(String str, int off, int len) 写入字符串的某一部分




标准流重定向

优点/特点

可以直接与用户交互,获得其输入。
使用其他的方法,需要先获得输入,然后再写入到文件,不方便。

概述

在Java中标准流一共有三种,
分别是标准输入流System.in,标准输出流System.out,标准错误流System.err
标准流默认指向控制台。
但有时程序从文件中输入数据并将结果输送到文件中,这是就需要用到流的重定向。

方法

在System类中提供了三个重定向标准输入/输出的方法

static void setErr(PrintStream err) //重定向“标准”错误输出流
static void setIn(InputStream in) // 重定向“标准”输入流
static void setOut(PrintStream out) //重定向“标准”输出流

若想重定向之后恢复流的原始指向,就需要保存下最原始的标准输入输出流。
方法:

备份:
InputStream in = System.in;
PrintStream out = System.out;
恢复:
System.setIn(in);
System.setOut(out);

测试代码


import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.*;
public class Main {
	public static void main(String args[]) throws IOException, InterruptedException {
		//输出重定向
		System.setOut(new PrintStream("data7.txt"));
        System.out.println("sifjisfjdifjdsfidjfifjdsif");

          //输入重定向
      		System.setIn(new FileInputStream("data7.txt"));
      		Scanner sc=new Scanner(System.in);
      		String str=sc.next();
      		System.out.println(str);
	}
}

File类


import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream.GetField;
import java.io.PrintStream;
import java.util.*;
public class Main {
	
	public static void main(String args[]) throws IOException, InterruptedException {
      File file=new File("E:\\eclipse-workspace\\my\\data1");//绝对路径“/”=="\\"
      System.out.println(file.exists());//文件是否存在
      if(file.exists()) {
    	  file.delete();//只能删除空文件夹/文件。
    	  
      }
      else {
    	  file.mkdir();//创建
      }
      
      file =new File("src/Main.java");//相对路径     “/”=="\\"
      System.out.println(file.exists());
      System.out.println(file.getAbsolutePath());//从相对路径找到绝对路径(不需要文件存在)
      System.out.println(file.getTotalSpace());//如果是真的,则输出文件所在磁盘的容量大小
      System.out.println(file.getFreeSpace());//如果是真的,则输出文件所在磁盘的可用容量的大小
      System.out.println(file.getParent());//返回文件的上一级文件名
      System.out.println(file.getName());//返回文件名
      System.out.println(file.getPath());//返回相对路径
      
      System.out.println(file.lastModified());//返回此抽象路径名表示的文件最后一次被修改的时间。
      
      System.out.println(file.length());//返回文件大小(字节数)
      
      file=new File("abc.txt");
      System.out.println(file.exists());
      file.createNewFile();//创建新文件.txt
      System.out.println(file.exists());
      
      
    
      
  //    file.deleteOnExit();//虚拟机执行结束/程序结束时候再删除文件;
	}
}

List系列方法


import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream.GetField;
import java.io.PrintStream;
import java.nio.file.DirectoryStream.Filter;
import java.util.*;

import org.omg.CosNaming.NamingContextExtPackage.StringNameHelper;
public class Main {
	
	public static void main(String args[]) throws IOException, InterruptedException {
      File file=new File("E:\\eclipse-workspace");

      //list
      String[]  files=file.list();//返回目录中的文件和目录。
      for(String filename:files) {
    	  System.out.println(filename);
      }
      String[] files1=  file.list(new FilenameFilter() {//匿名内部类
		
		
		public boolean accept(File dir, String name) {
		//	System.out.println(dir);//父路径
		//	System.out.println(name);//文件名称
			if(name.endsWith(".txt")) return true;
			else 
			return false;//返回false的不能被输出
		}
	});
         for(String afile:files1) {
        	 System.out.println(afile);
         }
	}
}


posted @ 2021-11-22 09:44  kingwzun  阅读(282)  评论(0编辑  收藏  举报