IO流浅谈

在这篇文章里,我会分别和大家聊字节流和字符流
一字节流
File
 
  File:文件和目录(文件夹)路径名的抽象表示形式。 
  

  File的构造方法:
  		File(String pathname):把一个路径名称封装成File对象
  		File(String parent, String child):把一个父路径和一个子路径封装成一个File对象
  		File(File parent, String child):把一个父路径File对象和一个子路径封装成一个File对象

		
     创建功能:
  		A:创建文件
  			public boolean createNewFile():如果文件不存在,就创建。否则,不创建。
			需求:D盘下造一个文件a.txt
  		B:创建目录
  			public boolean mkdir():如果目录不存在,就创建。否则,不创建。
			需求:D盘下造一个文件夹test
 			public boolean mkdirs():如果目录不存在,就创建。否则,不创建。
 									即时父目录不存在,也可以连父目录一起创建。
      
      创建文件的时候,一定要保证路径存在。
  
  删除功能:
  		public boolean delete():既可以删除文件,又可以删除目录。
 
  路径问题:
  		A:绝对路径	就是以盘符开始的路径(d:\\test\\aaa\\b.txt)
  		B:相对路径	就是不以盘符开始的路径(a.txt)
  					一般都是相对应当前的项目而言的。
  
  注意事项:Java程序的删除不走回收站,如果目录内还有内容就不能删除。
  		

	
   
  一些方法
  public boolean isDirectory():是否是目录
  public boolean isFile():是否是文件
  public boolean exists():是否存在
  public boolean canRead():是否可读
  public boolean canWrite():是否可写
  public boolean isHidden():是否隐藏

 
  获取功能
  public String getAbsolutePath():获取绝对路径
  public String getPath():获取相对路径
  public String getName():获取名称
    相对路径是指相对于当前包而言的

二:字节流及字节高效流
2.1 I/O流的分类(画图讲解)
  IO流分类:
  		流向:
  			输入流
 			输出流
  		数据类型:
  			字节流
  				字节输入流
  				字节输出流
 			字符流
  				字符输入流
  				字符输出流
  
  注意:一般我们在讨论IO的分类时,默认是按照数据类型分的。
  
  字节流:
  		字节输入流		InputStream(抽象类)
  		字节输出流		OutputStream(抽象类)
  字符流:
  		字符输入流		Reader
  		字符输出流		Writer
  
  学习习惯:
  		字节流
 		字符流
  
 
  输出流具体操作步骤如下:
  字节输出流对象
  调用写数据的方法
  释放资源
 
做法:
A:	两个构造的区别?
 		FileOutputStream(File file)
		FileOutputStream(String name)
		FileOutputStream fos  = new FileOutputStream("fos.txt");
		请问上面这个操作做了哪几件事情?
		 * 1.创建了一个文件输出流fos,指向文件a.txt
		 * 2.创建了a.txt这个文件
		
B:     fos.write("helloworld".getBytes());

C:		fos.close();关流

D:		fos.write("java".getBytes());


FileOutputStream写数据的方法
write(byte[] b) 
write(int b) :一次写一个字节
write(byte[] b, int off, int len) :一次写一个字节数组的一部分
代码实现(只是核心代码)
        FileOutputStream fos = new FileOutputStream("b.txt");
		
		调用写书数据的方法,写书文件
		先一次写一个字节
		write(int b) :
		fos.write(97);
		
		然后呢一次写一个字节数组的一部分
		write(byte[] b, int off, int len) ;
		byte[] byf = {97,98,99,100};
		fos.write(byf, 1, 2);


字节输入流:
具体操作步骤:
  字节输入流操作步骤:
  A:创建字节输入流对象
 FileInputStream  fis = new FileInputStream("a.txt");
 
  B:调用方法读取数据
 一次读取一个字节:read() -- 测试读取不到内容的时候的返回值(并且用循环改进)
 
  C:释放资源
 fis.close
    这里举一个例子:
	复制一个文件
          //1.封装数据源和目的地
		FileInputStream fis = new FileInputStream("D://b.mp4");
		FileOutputStream fos = new FileOutputStream("d.mp4");
		
		//2.读取数据源,写入目的地
		int by;
		while ((by=fis.read())!=-1) {
			fos.write(by);
		}
		
		//3.释放资源
		fos.close();
		fis.close();
 
  数据源:
  		IODemo.java	--	读取数据	--	InputStream	--	FileInputStream	--	一次读取一个字节
  目的地:
  		Copy.java	--	写出数据	--	OutputStream -- FileOutputStream -- 一次写一个字节



字节输入流:
具体操作步骤:
  字节输入流操作步骤:
  A:创建字节输入流对象
 FileInputStream  fis = new FileInputStream("a.txt");
 
  B:调用方法读取数据(一次读取一个字节数组,提升效率)
 一次读取一个字节数组: public int read(byte[] b):返回实际读取长度,数据被读取到数组中。
 -- 测试方法返回长度?根据String类的构造方法构造字符串
  C:释放资源
 fis.close

 
 
 
  字节缓冲区流(也叫高效流):
  		BufferedInputStream(read() 一次读取一个字节, public int read(byte[] b):返回实际读取长度,数据被读取到数组中。)
  		BufferedOutputStream(write(byte[] b))
  
  流
  		低级流: 基本的流,可以直接操作文件。
  		高级流:是操作基本流的流。
 

 
 
 

从这里开始聊聊字符流 字符流
  String中的编码和解码问题。
  
  编码:
  		把我们能够看懂的字符转换为看不懂的数据
 解码:
  		把我们看不懂的数据转换为看得懂的字符
  
  
 
 
 public byte[] getBytes(String charsetName)  按照给定的编码方式,编码字节数组(gbk,utf-8)
 String(byte[] bytes, String charsetName) 按照给定的编码方式解码字符数组
 
 		String s = "中国好";
		byte[] bytes = s.getBytes("utf-8");
		System.out.println(Arrays.toString(bytes));
		
		//String(byte[] bytes, String charsetName)
		System.out.println(new String(bytes,"gbk"));
 
 结论:用什么编码,就必须用什么解码。。
 
 转换流 这边不再多说(详见十三天笔记)
 InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"),"gbk");
 int ch;
		while ((ch=isr.read())!=-1) {
			System.out.println((char)ch);
		}
		
		//关流
		isr.close();



字符流

Reader

Writer


  IO流中的编码和解码问题

  OutputStreamWriter:把字节输出流转换为字符输出流

  InputStreamReader:把字节输入流转换为字符输入流

 

转换流

 

写入数据,把字节输出流转换为字符输出流(不指定码表)

OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("osw.txt"));

把字节输出流转换为字符输出流(指定码表)

OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("osw.txt"), "GBK");

OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("osw.txt"), "UTF-8"); 


读取数据, 把字节输入流转换为字符输入流(不指定码表)

InputStreamReader isr = new InputStreamReader(new FileInputStream("osw.txt"));

把字节输入流转换为字符输入流(指定码表)

InputStreamReader isr = new InputStreamReader(new FileInputStream("osw.txt"), "GBK");

总结:我们一般创建字符输入或者输出流一般情况下使用系统默认的码表就可以,

     如果来来回回需要指定码表的话,就显得非常的麻烦了

(将以上创建方式继续简化)

  如果采用刚才的方式,创建对象写起来比较复杂。

 InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"));

  OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("b.txt"));

  为了简化这种操作,java就针对这两个转换的字符流提供其子类。

  FileReader

  FileWriter

  它们的默认编码是采用系统编码。

构造学习:

FileWriter(File file) 

FileWriter(String fileName) 

FileReader(File file) 

FileReader(String fileName)


  flush()和close()的区别? 

  A:flush刷新缓冲区,流对象可以继续

  B:close先刷新缓冲区,再关闭流对象。流对象不可以继续使用了。


练习:给字符输出流里面利用方法写数据


  字符输出流操作步骤:

  A:创建字符输出流对象

 B:调用写数据方法

  C:释放资源

  

 写数据方法:

 一次写一个字符  write(int c) 

  一次写一个字符数组write(char[] cbuf)

 一次写一个字符数组的一部分write(char[] cbuf, int off,int len)

 一次写一个字符串write(String str)

  一次写一个字符串的一部分write(String str,int off,int len)


字符输入流

  字符输入流操作步骤:

  A:创建字符输入流对象

FileReader fr = new FileReader("a.txt");

 

 B:读取数据并显示在控制台

  a:一次读取一个字符

  一次读取一个字符

int ch;

while ((ch = fr.read()) != -1) {

System.out.print((char) ch);

}

 

  b:一次读取一个字符数组

char[] chs = new char[1024];

int len;

while ((len = fr.read(chs)) != -1) {

System.out.print(new String(chs, 0, len));

}

 

  C:释放资源

fr.close();


练习

注意:字符流输入输出流复制的文件是有要求的,简单来说只要是记事本打开文件的内容你能够看得懂,

就可以用字符流来进行复制,否则不行


具体原因:像复制MP3或者一些视频文件的时候,如果他的字节个数不是偶数的话,就会造成文件的缺损,

因为一个字符等于两个字节


1.利用字符流复制java文件(两种方式,一次读写一个字符,一次读写一个字符数组)



高效流:(重要)

 直接来一个例子

给文件中写入十个"helloworld",每写一个换一行,每写一行必须写入一个换行符“\r\n”

这样写的弊端,windows系统下的换行符是“\r\n”,Linux是"\n",Mac是"\r",这样会造成代码的通用性不强



按步骤写代码,核心代码如下:

    //1.创建字符高效输出流,并指向一个txt文件

BufferedWriter bw = new BufferedWriter(new FileWriter("f.txt"));

//2.调用里面写数据的方法,给文件中写入数据

for (int i = 0; i < 10; i++) {

//给文件中写入helloworld

bw.write("helloworld");

//写入换行符

bw.newLine();

//刷新缓冲区

bw.flush();

}

//关流

bw.close();

}

 BufferedReader:字符缓冲输入流

构造:BufferedReader(Reader in) 

特殊方法:public String readLine():包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回 null 

 

  BufferedWriter:字符缓冲输出流

构造:BufferedWriter(Writer out) 

特殊方法:public void newLine():会根据系统来确定写入不同的换行符



最终练习:

 复制文本文件:

  4种

    基本字符流一次读写一个字符

  基本字符流一次读写一个字符数组

高效字符流一次读写一个字符

  高效字符流一次读写一个字符数组

  

  数据源:

  a.txt

  目的地:

  b.txt


创建一个test类,实现以上四个要求(代码复制来自我的elipse,运行输出均无误)

public class Test {

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

method();//基本字符流一次读写一个字符

method2();//基本字符流一次读写一个字符数组

method3();//高效字符流一次读写一个字符

method4();//高效字符流一次读写一个字符数组

}


private static void method4() throws IOException {

//高效字符流一次读写一个字符数组

BufferedReader br = new BufferedReader(new FileReader("a.txt"));

BufferedWriter bw = new BufferedWriter(new FileWriter("j.txt"));

//一次读写一个字符数组

char[] chs = new char[1024];

int len;

while ((len = br.read(chs))!=-1) {

bw.write(chs, 0, len);

bw.flush();

}

//释放资源

bw.close();

br.close();

}


private static void method3() throws IOException {

//高效字符流一次读写一个字符

BufferedReader br = new BufferedReader(new FileReader("a.txt"));

BufferedWriter bw = new BufferedWriter(new FileWriter("j.txt"));

//一次读写一个字符

int ch;

while ((ch=br.read())!=-1) {

bw.write(ch);

bw.flush();

}

//关流

bw.close();

br.close();

}


private static void method2() throws IOException {

//基本字符流一次读写一个字符数组

FileReader fr = new FileReader("a.txt");

FileWriter fw = new FileWriter("j.txt");

//一次读写一个字节数组

char[] chs = new char[1024];

int len;

while ((len = fr.read(chs))!=-1) {

fw.write(chs, 0, len);

fw.flush();

}

//关流

fw.close();

fr.close();

}


private static void method() throws IOException {

//基本字符流一次读写一个字符

FileReader fr = new FileReader("a.txt");

FileWriter fw = new FileWriter("j.txt");

//一次读取一个字符,返回的是该字符对应的int值

/*System.out.println((char)fr.read());

System.out.println((char)fr.read());

System.out.println((char)fr.read());

System.out.println((char)fr.read());

System.out.println((char)fr.read());

*/

int ch;

while ((ch=fr.read())!=-1) {

//System.out.println((char)ch);

fw.write(ch);

fw.flush();

}

//释放资源

fr.close();

fw.close();

}


}


俩个比较重要的东西

BufferedReader:字符缓冲输入流

BufferedWriter:字符缓冲输出流

的特性。


1.需求:向文件中写入十个中国好,每写一个换一行(普通方式实现,高效流输出流特性实现)

2.利用字符高效流的一次读写一行的特性复制文件

写数据注意三部曲:

bw.write(line);

bw.newLine();

bw.flush();


IO流练习

1.键盘录入数据并存储

  键盘录入5个学生信息(姓名,语文成绩,数学成绩,英语成绩),按照总分从高到低存入文本文件

  

  分析:

  A:定义学生类

  B:创建集合,采用TreeSet集合,并按照比较器排序

  C:键盘录入5个学生信息并存入集合

  D:遍历集合,并把数据写到文本文件


代码测试类如下,(之前需要一个学生类,自动生成即可,这里不再多说)

public class StudentTest {

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

//创建一个TreeSet集合,并写一个比较器

TreeSet ts = new TreeSet(new Comparator() {

@Override

public int compare(Student s1, Student s2) {

int num = s1.getAllScore() - s2.getAllScore();

int num2 = num==0?s1.getName().compareTo(s2.getName()):num;

return num2;

}

});

//键盘录入5个学生信息

for (int i = 0; i < 5; i++) {

//创建键盘录入对象

Scanner sc = new Scanner(System.in);

//接受键盘录入数据

System.out.println("请输入你的姓名");

String name = sc.nextLine();

System.out.println("请输入你的语文成绩");

int chinese = sc.nextInt();

System.out.println("请输入你的数学成绩");

int math = sc.nextInt();

System.out.println("请输入你的英语成绩");

int english = sc.nextInt();

//封装成学生对象,存储集合

Student s = new Student(name, chinese, math, english);

ts.add(s);

}

System.out.println("数据录入完毕。。");

//遍历集合,将集合中的数据全取不出来之后写入文件中,每一个学生信息占一行

BufferedWriter bw = new BufferedWriter(new FileWriter("score.txt"));

for (Student s : ts) {

String info = s.getName()+"  "+s.getChinese()+"  "+s.getMath()+"  "+s.getEnglish()+"  "+s.getAllScore();

//将拼接成的字符串写入文件中

bw.write(info);

bw.newLine();

bw.flush();

}

//释放资源

bw.close();

io流是SE很重要的一部分,时常复习还是很重要的,代码也得练。懂得原理就不会感到很难。