黑马程序员-------IO流

------ <a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------

1.File

1,描述文件和文件夹的对象。

2,提供了和系统平台相关的分隔符  File.separator

3,提供了一些用于操作文件的方法。

                   3.1获取操作:getXXX.获取文件大小,名称,目录,修改时间....

                   3.2增加 createNewFile():创建文件,mkdir():创建目录 mkdirs():创建多级目录。

                            删除delete(),判断存在exists()。

                   3.3注意:创建文件:文件已存在不创建,不存在则创建,该方法抛IOException。

                            删除文件:文件不存在,以及文件存在正在被使用,删除都为false。

                            删除目录:目录内容为空,可以直接删除,目录内容不为空,删除不了,必须从里往外删。

                   3.4获取目录中的内容。

                            String[]list():获取目录中的文件和文件夹名称。

                            File[]listFiles():获取目录中的所有的文件和文件File对象。

                            File[]listFiles(FileFilter):根据指定的文件过滤器进行获取。

                            File[]listFiles(FilenameFilter):根据指定的文件名称过滤器进行获取。

1.1 File的构造函数,分隔符

import java.io.File;

public class FileDemo {

//	private static final String FILE_SEPARATOR = System.getProperty("file.separator");

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		/*
		 * File类的构造函数。
		 * 如何创建文件对象。
		 */
		String pathName = "e:\\java_code\\day22e\\hello.java";
		File f1 = new File(pathName);//将Test22文件封装成File对象。注意;有可以封装不存在文件或者文件夹,变成对象。
		System.out.println(f1);
		
		
		File f2 = new File("e:\\java_code\\day22e","hello.java");
		System.out.println(f2);
		
		//将parent封装成file对象。
		File dir = new File("e:\\java_code\\day22e");
		File f3 = new File(dir,"hello.java");
		System.out.println(f3);
		
		
//		File f4 = new File("e:"+FILE_SEPARATOR+"java_code"+FILE_SEPARATOR+"day22e"+FILE_SEPARATOR+"hello.java");
		File f5 = new File("e:"+File.separator+"java_code"+File.separator+"day22e"+File.separator+"hello.java");
		
		
	}

}

1.2 File类的方法演示

import java.io.File;
import java.text.DateFormat;
import java.util.Date;

public class FileMethodDemo {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		/*
		 * File类的方法演示。
		 * 获取文件的信息。名称,大小,时间。
		 * 
		 */

		File file = new File("Test22.java");
		
		String absPath = file.getAbsolutePath();
		String path = file.getPath();//File中封装的路径是什么获取到的就是什么。
		String filename = file.getName();
		long size = file.length();
		long time = file.lastModified();
		
		System.out.println("absPath="+absPath);
		System.out.println("path="+path);
		System.out.println("filename="+filename);
		System.out.println("size="+size);
		System.out.println("time="+time);
		
//		毫秒值--Date--格式化--字符串文本
		String str_date = DateFormat.getDateTimeInstance(DateFormat.LONG,DateFormat.LONG).format(new Date(time));
		System.out.println(str_date);
		
	}

}


import java.io.File;
import java.io.IOException;

public class FileMethodDemo2 {

	/**
	 * @param args
	 * @throws IOException
	 */

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

		/*
		 * File类中的方法2.
		 * 
		 * 对文件或者文件加进行操作。
		 */

		File file = new File("e:\\file.txt");

		/*
		 * 创建文件,如果文件不存在,创建 true 如果文件存在,则不创建 false。 如果路径错误,IOException。
		 */
		boolean b1 = file.createNewFile();
		System.out.println("b1=" + b1);

		// 删除。
		// boolean b2 = file.delete();//注意:不去回收站。慎用。
		// System.out.println("b2="+b2);

		// 需要判断文件是否存在。
		// boolean b3 = file.exists();
		// System.out.println("b3="+b3);

		// 对目录操作 创建,删除,判断。
		File dir = new File("e:\\abc");
		// boolean b4 = dir.mkdirs();//创建多级目录。//dir.mkdirs();
		// System.out.println("b4="+b4);

		boolean b5 = dir.delete();// 删除目录时,如果目录中有内容,无法直接删除。
		// 只有将目录中的内容都删除后,保证该目录为空。这时这个目录才可以删除。
		System.out.println("b5=" + b5);
		
		System.out.println("-----------判断文件,目录------------");
		File f = new File("e:\\javahaha");//要判断是否是文件还是目录,必须先判断存在。
//		f.mkdir();
		f.createNewFile();
		System.out.println(f.isFile());
		System.out.println(f.isDirectory());
		
	}

}

1.3  listFiles、list和FIlter过滤器

-------------------------------list--------------------------------------------------------------------------

import java.io.File;

public class FileMethodDemo3 {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
		//需求:对给定的目录获取内部的内容。
		File dir = new File("e:\\java_code");
		//健壮性判断。1,必须是存在的,2,必须是目录。否则容易引发返回数组为null,出现NullPointerException
		String[] names = dir.list();//获取的是目录下的当前的文件以及文件夹的名称。
//		System.out.println(names);
		for(String name : names){
//			System.out.println(name);
		}
		
		
		File[] files = dir.listFiles();//获取目录下当前文件以及文件对象。
		for(File file : files){
			System.out.println(file.lastModified());
		}
	}

}

---------------------------------listFiles-------------------------------------------------

import java.io.File;

import cn.itcast.io.b.filter.FilenameFilterBySuffix;

public class FileMethodDemo4 {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		/*
		 * 需求2:获取目录中的内容,但是只要.java文件。
		 */
		File dir = new File("E:\\Java_Code\\day06");

		File[] files = dir.listFiles(new FilenameFilterBySuffix(".JPG"));// 需要传递一个文件名过滤器 FilenameFilter对象。
		
		/*listFiles源码解析。
		public File[] listFiles(FilenameFilter filter) {//FilenameFilter filter = new FilenameFilterByJava()
	        String ss[] = list();//调用了File类中的list()获取到所有的名称数组 ss。
	        if (ss == null) return null;//健壮性判断,如果数组为null,就返回。
	        ArrayList<File> files = new ArrayList<>();//创建一个集合。元素是File类型。
	        for (String s : ss)//遍历名称数组。
	            if ((filter == null) || filter.accept(this, s))//对遍历的名称进行过滤器的判断。将 当前目录this,遍历到名称s传递给accept方法。
	            			//一旦条件满足过滤器的过滤条件。
	                files.add(new File(s, this));//将满足过滤条件添加到集合中。添加是 将文件名称和当前目录封装成File对象。new File(dir,name);
	                
	        return files.toArray(new File[files.size()]);//将集合转成数组返回,为啥呢?不需要增删操作。
	    }
		*/
		
		for (File file : files) {
			System.out.println(file.getName());
		}
	}

}


import java.io.File;

import cn.itcast.io.b.filter.FileFilterByDir;


public class FileMethodDemo5 {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		File dir = new File("E:\\Java_Code\\day06");

		File[] files = dir.listFiles(new FileFilterByDir());
		
		for(File file : files){
			System.out.println(file);
		}

	}

}

2遍历文件递归和不用递归的两种方式

2.1 递归

/*
递归:其实就是功能的重复使用,但是每次该功能被调用参数都变化(使用了上一次运算的结果)。
		 * 1,函数自身调用自身。
		 * 2,一定要定义条件,否则.StackOverflowError。栈溢出。
		 * 3,注意:递归次数过来容易溢出。
		 *
*/
import java.io.File;

public class GetAllFiles {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		/*
		 * File类的listFiles()列出当前目录下文件以及文件夹。
		 * 
		 * 需求:能不能列出当前目录下的子目录中的所有内容。 思路: 1,在遍历当前目录时,会获取到当前的所有的文件以及文件夹,
		 * 2,要遍历子目录需要对获取到的当前的file对象进行判断,只有是目录才可以作为子目录进行继续遍历。
		 */

		File dir = new File("E:\\test");

		listAll(dir);

	}

	public static void listAll(File dir) {// dir:接收目录以及子目录。

		System.out.println("dir:" + dir.getName());
		File[] files = dir.listFiles();
		for (File file : files) {

			if (file.isDirectory()) {// 如果遍历到当前的file对象是个目录,继续遍历。
				listAll(file);
			} else {
				System.out.println("file:"+file.getName());
			}
		}

	}

}

2.2 不用递归---LinkedList

import java.io.File;
import java.util.LinkedList;

public class GetAllFileByQueue {

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		/*
		 * 遍历文件夹,不用递归咋办? 
		 * 思路:
		 * 1,可以通过对每一个目录进行for循环,但是目录层级很多,for会死掉。
		 * 2,每遍历到一个目录,不对其进行遍历,而是先临时存储起来。 相当于把所有目录(无论同级不同级)都存储起来。
		 * 3,遍历容器时取到就是目录,然后对目录遍历即可。 4,从容器中取到目录遍历时发现内部还有目录,一样将这些目录存储到容器中。
		 * 5,只要不断的遍历这个容器就哦了。
		 * 
		 * 通过图解:发现这个容器只要是一个简单的队列就可以解决这个问题。
		 */

		File dir = new File("E:\\test");
		System.out.println(dir.getName());
		Queue<File> queue = new Queue<File>();

		// 1,对dir进行当前目录的遍历。
		File[] files = dir.listFiles();
		for (File file : files) {
			// 2, 如果有子目录,存储到队列中。
			if (file.isDirectory()) {
				queue.myAdd(file);
			} else {
				System.out.println(file.getName());
			}
		}
		System.out.println("---------------------");
		//3,遍历队列容器。因为子目录都在队列中。
		while(!queue.isNull()){
			
			File subDir = queue.myGet();//从队列中取出子目录。
			System.out.println(subDir.getName());
			//4,遍历子目录。
			File[] subFiles = subDir.listFiles();
			for(File subFile : subFiles){
				if(subFile.isDirectory()){//如果子目录中还有子目录,继续存储到队列中。
					queue.myAdd(subFile);
				}else{
					System.out.println(subFile.getName());
				}
			}
			
			
		}
	}

}

/**
 * 队列结构。先进先出。
 */
class Queue<E> {

	private LinkedList<E> link;

	/**
	 * 提供了构造队列对象的构造器。
	 */
	public Queue() {
		link = new LinkedList<E>();
	}

	/**
	 * 添加元素的方法。
	 */
	public void myAdd(E obj) {
		link.addFirst(obj);
	}

	/**
	 * 获取的方法。
	 */
	public E myGet() {
		return link.removeLast();
	}

	/**
	 * 判断队列是否有元素。
	 */
	public boolean isNull() {
		return link.isEmpty();
	}
}

3. OutputStream 和InputStream字节流

3.1 FileInputStream----read

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class FileInputStreamDemo2 {

	/**
	 * @param args
	 * @throws IOException 
	 */
	public static void main(String[] args) throws IOException {
		/*
		 * 演示第二个读取方法, read(byte[]);
		 */

		File file = new File("tempfile\\file.txt");

		// 创建一个字节输入流对象,必须明确数据源,其实就是创建字节读取流和数据源相关联。
		FileInputStream fis = new FileInputStream(file);
		
		//创建一个字节数组。
		byte[] buf = new byte[1024];//长度可以定义成1024的整数倍。
		
		int len = 0;
		while((len=fis.read(buf))!=-1){
			System.out.println(new String(buf,0,len));
		}
		
		/*int len1 = fis.read(buf);
		System.out.println(len1+":"+new String(buf,0,len1));
		int len2 = fis.read(buf);
		System.out.println(len2+":"+new String(buf,0,len2));
		int len3 = fis.read(buf);
		System.out.println(len3+":"+new String(buf,0,len3));
		int len4 = fis.read(buf);
		System.out.println(len4);
		*/
		
		fis.close();
	}

}

3.2 FileOutputStream---write

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileOutputStreamDemo {

	/**
	 * @param args
	 * @throws IOException 
	 */
	public static void main(String[] args) throws IOException {
		//需求:将数据写入到文件中。
		//创建临时目录,
		File dir = new File("tempfile");
		if(!dir.exists()){
			dir.mkdir();
		}
		
		//创建存储数据的文件。
		File file = new File(dir,"file.txt");
		
		//创建一个用于操作文件的字节输出流对象。一创建就必须明确数据存储目的地。
		//输出流目的是文件,会自动创建。如果文件存在,则覆盖。
		FileOutputStream fos = new FileOutputStream(file);
		
		//调用父类中的write方法。
		byte[] data = "abcde".getBytes();
		fos.write(data);
		
		//关闭流资源。
		fos.close();
		
	}
}

3.3 案例----复制文件

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class CopyFileTest {

	/**
	 * @param args
	 * @throws IOException 
	 */
	public static void main(String[] args) throws IOException {
		
		/*
		 * 练习:复制文件。
		 * 原理;读取一个已有的数据,并将这些读到的数据写入到另一个文件中。
		 */
		//1,明确源和目的。
		File srcFile = new File("E:\\1.mp3");
		File destFile = new File("E:\\copy_2.mp3");
		
		//2,明确字节流 输入流和源相关联,输出流和目的关联。
		FileInputStream fis = new FileInputStream(srcFile);
		FileOutputStream fos = new FileOutputStream(destFile);
		
		//3, 使用输入流的读取方法读取字节,并将字节写入到目的中。
		int ch = 0;
		while((ch=fis.read())!=-1){
			fos.write(ch);
		}
		
		
		//4,关闭资源。
		fos.close();
		fis.close();
		
	}

}

3.4 缓冲BufferedInputStream和BufferedOutputStream

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class ByteStreamBufferCopyFileTest {

	/**
	 * @param args
	 * @throws IOException 
	 */
	public static void main(String[] args) throws IOException {
		
		//复制文件,通过字节流已有的缓冲区对象。
		
		File srcFile = new File("tempfile\\file.txt");
		File destFile = new File("tempfile\\bufcopy_file.txt");
		
		//1,创建好流对象。
		FileInputStream fis = new FileInputStream(srcFile);
		FileOutputStream fos = new FileOutputStream(destFile);
		//2,创建缓冲区对象并和指定的流相关联
		BufferedInputStream bufis = new BufferedInputStream(fis);
		BufferedOutputStream bufos = new BufferedOutputStream(fos);

		byte[] buf = new byte[1024];
//		int ch = 0;
		int len = 0;
		while((len=bufis.read(buf))!=-1){//缓冲区的read方法从缓冲区中读取一个字节。
			bufos.write(buf,0,len);//将一个字节写入到缓冲区中。
			bufos.flush();//刷新缓冲区,将数据刷到目的地。
		}
	
		
		bufos.close();//缓冲区的关闭方法内部其实调用的是流的关闭方法。
		bufis.close();
	}

}

4 FileWriter和FileReader字符串

4.1 FileReader

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;

public class CharStreamDemo {

	/**
	 * @param args
	 * @throws IOException 
	 */
	public static void main(String[] args) throws IOException {
		
		/*
		 * 需求1:通过流写一个篇文章,里面有中文。"你好你好"。indexOf('好');
		 */
//		writeCNText();
		
		
		/*
		 * 需求2:一篇文章中出现了多少个好字。读取数据。判断好字并计数。
		 * 思路:读取一个文本,获取内容判断好字。
		 */
		
		 readCNText();
		
		/*
		 * 解决需求2问题:
		 * 使用FileReader。
		 * 
		 */
		 System.out.println("-----------读取字符-------------");
		readCNTextByReader();
		
	}

	public static void readCNTextByReader() throws IOException {

		//创建一个读取字符文件的读取流对象。FileReader。
		FileReader fr = new FileReader("tempfile\\cn.txt");//这个流的底层使用的是FileInputStream
		
//		int ch = fr.read();
//		System.out.println("读取一个字符:"+ch);
//		int ch1 = fr.read();//一次读取一个中文,读取多个字节查表转成中文。
//		System.out.println("读取一个字符:"+(char)ch1);
		int ch = 0;
		int count = 0;
		while((ch=fr.read())!=-1){
			if(ch=='好'){
				count++;
			}
		}
		System.out.println("count="+count);
		fr.close();
	}

4.2. FileWriter

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

public class FileWriterDemo {

	/**
	 * @param args
	 * @throws IOException 
	 */
	public static void main(String[] args) throws IOException {
		//演示FileWriter 用于操作文件的便捷类。
		FileWriter fw = new FileWriter("tempfile\\fw.txt");
		
		fw.write("你好谢谢再见");//这些文字都要先编码。都写入到了流的缓冲区中。
		
		fw.flush();
		
		fw.close();
		
		/*
		 * flush()和close()的区别?
		 * 
		 * flush():将流中的缓冲区缓冲的数据刷新到目的地中,刷新后,流还可以继续使用。
		 * close():关闭资源,但在关闭前会将缓冲区中的数据先刷新到目的地,否则丢失数据,然后在关闭流。流不可以使用。
		 * 
		 * 如果写入数据多,一定要一边写一边刷新,最后一次可以不刷新,由close完成刷新并关闭。
		 * 
		 * 
		 */
	}

}

4.3字符串缓冲BufferedReader和 BufferedWriter

 

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
 
public class CharStreamBufferedTest {
 
         /**
          * @param args
          * @throws IOException
          */
         publicstatic void main(String[] args) throws IOException {
                   /*
                    * 字符流中是否有提供缓冲区中。
                    * 注意:其实自定义数组就可以解决问题缓冲区问题并提高效率。
                    * 为什么还要使用流中的缓冲区对象呢?因为缓冲区对象中除了封装数组以外,
                    * 还提供了更多的操作缓冲区数据的方法。
                    * BufferedReader  BufferedWriter
                    *
                    * 讲解字符流缓冲区中的特有方法。
                    * 操作字符数据时,有一个文本特有的表形实行:行(hang)
                    * 操作行的方法。
                    * BufferedReader:readLine():一次读取一行。
                    * BufferedWriter:
                    */
                  
                   copyTextByBuffer();
//               readText();
                  
//               writeText();
         }
 
         publicstatic void writeText() throws IOException {
                   BufferedWriterbufw = new BufferedWriter(newFileWriter("tempfile\\test24_buf.txt"));
                  
                   for(intx=1; x<=4; x++){
                            bufw.write(x+"-itcast");
                            bufw.newLine();
                            bufw.flush();
                   }
                   bufw.close();
         }
 
         publicstatic void readText() throws IOException {
                  
                   BufferedReaderbufr = new BufferedReader(new FileReader("Test24.java"));
                  
                  
                   Stringline = null;
                   while((line=bufr.readLine())!=null){
                           
                            System.out.println(line);
                           
                   }
                  
//               Stringline = bufr.readLine();
//               System.out.println("-"+line+"-");
//               Stringline1 = bufr.readLine();
//               System.out.println("-"+line1+"-");
                  
                   bufr.close();
                  
                  
         }
 
         publicstatic void copyTextByBuffer() throws IOException {
                  
                   BufferedReaderbufr = new BufferedReader(new FileReader("Test24.java"));
                   BufferedWriterbufw = new BufferedWriter(newFileWriter("tempfile\\test24_bufcopy.txt"));
                  
                   //循环读写一行数据。
                   Stringline = null;
                   while((line=bufr.readLine())!=null){
                            bufw.write(line);
                            bufw.newLine();
                            bufw.flush();
                   }
                  
                   bufw.close();
                   bufr.close();
                  
         }
 
}

5.OutputStreamWriter和InputStreamReader是字符和字节的桥梁

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;

public class TransStreamDemo {

	/**
	 * @param args
	 * @throws IOException 
	 * @throws UnsupportedEncodingException 
	 */
	public static void main(String[] args) throws Exception {
		
//		writeCN();
		
		readCN();
		/*
		总结:
		发现继承关系是这样的。
		OutputStreamWriter:
			|--FileWriter:
				
		InputStreamReader:
			|--FileReader;

		父类和子类的功能有什么区别呢?
		
		OutputStreamWriter和InputStreamReader是字符和字节的桥梁:也可以称之为字符转换流。
		字符转换流原理:字节流+编码表。
		
		FileWriter和FileReader:作为子类,仅作为操作字符文件的便捷类存在。
		当操作的字符文件,使用的是默认编码表时可以不用父类,而直接用子类就完成操作了,简化了代码。
		
		InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"));//默认字符集。
		InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"),"GBK");//指定GBK字符集。
		FileReader fr = new FileReader("a.txt");
		这三句代码的功能是一样的,其中第三句最为便捷。
		
		注意:一旦要指定其他编码时,绝对不能用子类,必须使用字符转换流。
		什么时候用子类呢?
		条件:
		1,操作的是文件。
		2,使用默认编码。
		
		字节--->字符 : 看不懂的--->看的懂的。  需要读。输入流。 InputStreamReader
		字符--->字节 : 看的懂的--->看不懂的。  需要写。输出流。 OutputStreamWriter
		
		
		*/
	}

	public static void readCN() throws IOException {
		
		//创建InputStreamReader对象。
		InputStreamReader isr = new InputStreamReader(new FileInputStream("tempfile\\u8cn.txt"),"UTF-8");
		
		char[] buf = new char[1024];
		int len = isr.read(buf);
		System.out.println(new String(buf,0,len));
		isr.close();
	}

	//读取中文。
	public static void readCN_no() throws IOException {
		
		// 使用FileReader没出来,因为文件是UTF-8编码。读取UTF-8字节时,用该指定用UTF-8解码。
		// 说明需要指定码表。那就需要使用InputStreamReader。
		FileReader fr = new FileReader("tempfile\\u8cn.txt");
//		int ch = (char)fr.read();
//		System.out.println((char)ch);
		char[] buf = new char[1024];
		int len = fr.read(buf);
		System.out.println(new String(buf,0,len));//浣犲ソ

		
		fr.close();
		
	}

	public static void writeCN() throws Exception {
		//需求:既然识别中文的码表有两个,GBK UTF-8
		//能不能将中文数据按照utf-8的方式进行文件的存储呢?
		//还能使用FileWriter吗?不能使用了,因为FileWriter中默认的是GBK
		//通过FileWriter的api描述,要指定编码表这些值,需要使用OutputStreamWriter 
		//OutputStreamWriter 是字符流通向字节流的桥梁:可使用指定的 charset 将要写入流中的字符编码成字节。
		//它的作用的就是,将字符串按照指定的编码表转成字节,在使用字节流将这些字节写出去。
		
		OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("tempfile\\u8cn.txt"),"utf-8");
		
		osw.write("你好");//写入缓冲区。
		
		osw.close();
	}

}

6.编码表

编码表:其实就是生活中文件和计算机二进制的对应关系表。

1,ascii: 一个字节中的7位就可以表示。对应的字节都是正数。0-xxxxxxx

2,iso8859-1:拉丁码表 latin,用了一个字节用的8位。1-xxxxxxx  负数。

3,GB2312:简体中文码表。6,7仟的中文和符号。用两个字节表示。两个字节都是开头为1 两个字节都是负数。

  GBK:目前最常用的中文码表,2万的中文和符号。用两个字节表示,一部分文字,第一个字节开头是1,第二字节开头是0

  GB18030:

4, unicode:国际标准码表:无论是什么文字,都用两个字节存储。Java中的char类型用的就是这个码表。char c = 'a';占两个字节。

                            在Java中,字符串是按照系统默认码表来解析的。简体中文版 字符串默认的码表是GBK。

5,UTF-8:基于unicode,一个字节就可以存储数据,不要用两个字节存储,而且这个码表更加的标准化,在每一个字节头加入了

                            编码信息(后期到api中查找)。

能识别中文的码表:GBK  UTF-8  正因为识别中文码表不唯一,涉及到了编码解码问题。

对于我们开发而言;常见的编码 GBK  UTF-8 ISO8859-1

文字--->二进制(数字) :编码。

二进制(数字)--->文字  : 解码。

字符串:String  字节数组:byte[]

字符串--编码(getBytes())-->字节数组

字节数组--解码(new String(byte[]))-->字符串

import java.io.UnsupportedEncodingException;

public class CutStringTest {

	/**
	 * @param args
	 * @throws UnsupportedEncodingException 
	 */
	public static void main(String[] args) throws UnsupportedEncodingException {
		
		/*
		1,对字符串按照字节数截取(默认码表),"abc你好" 有5个字符,有7个字节。
		按照3个字节截取 abc ,按照四个字节截取 abc和你字的一半,如果出现中文一半舍弃。
		请定义一个功能解决这个问题。
		你好的gbk:-60 -29 -70 -61
		思路:
		1,一个中文gbk两个字节,都是负数。
		2,在截取时,先看最后一位是负数吗?不是,直接截取就哦了。
			如果是,不要直接舍弃,最好在看一下该负数之前连续出现了几个负数。
		3,因为中文两个字节,出现的负数个数是偶数,不需要舍弃的,是奇数,就舍弃最后一个。哦耶。
		
		*/

		String str = "abc你好cd谢谢";
		
		str = "abc琲琲cd琲琲";

		byte[] buf = str.getBytes("GBK");
		for (int i = 0; i < buf.length; i++) {
			String s = cutString(str,i+1);
			System.out.println(str+",截取"+(i+1)+"个结果是:"+s);
			
		}
	}
	
	public static String cutString(String str,int len) throws UnsupportedEncodingException {
		
		//1,将字符串编码成字节数组。
		byte[] buf = str.getBytes("GBK");
		int count = 0;
		//2,对数组进行遍历。从截取位开始遍历。往回遍历。
		for(int i = len - 1; i >=0 ; i--){
			//判断最后截取位上是否是负数
			if(buf[i]<0){
				count++;
			}else{
				break;
			}
		}
		//判断奇偶数。
		if(count%2==0){
			return new String(buf,0,len);
		}else{
			return new String(buf,0,len-1);//舍弃最后一个。
		}
		
	}

}

7.键盘录入System.in和BufferedReader

/*
		 * 思路:
		 * 1,将数据存储到的文件,没有问题的。
		 * 2,怎么获取数据来源呢?键盘录入怎么弄呢?
		 * 键盘录入是输入,系统中应该具备的一部分。
		 * 在System类找到了标准输入流 属性 in。
		 * System.in 对应的类型是InputStream。字节输入流。
		 */


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

public class ReadKeyDemo2 {

	/**
	 * @param args
	 * @throws IOException 
	 */
	public static void main(String[] args) throws IOException {
		//读取的数据更多。
		/*
		 * 读取一个字节先不要操作,将其存储起来,转成一个字符串。
		 * 
		 * 能不能一次就读取一行字符串呢?readLine();
		 * 可是readLine()是BufferedReader方法。
		 * BufferedReader使用时必须接收字符流对象。
		 * 键盘录入是字节流。要是将字节流转成字符流是不是就哦了呢?咋转呢?
		 * 字节流---桥梁InputStreamReader--->字符流
		 * 
		 */
		// //读取键盘录入的字节输入流。
		// InputStream in = System.in;
		// //通过桥梁,将字节输入流转成字符输入流。
		// InputStreamReader isr = new InputStreamReader(in);
		// //对字符流进行效率提高,而且使用缓冲区对象的特有方法readLine();
		// BufferedReader bufr = new BufferedReader(isr);
		
		//记住:以后但凡提到了键盘录入就写这句,一行一行的读取,除非要对读取每一个字节操作。
		BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
		
		String line = null;
		while((line=bufr.readLine())!=null){//键盘录入记住定义结束标记。强制结束。
			
			if("over".equals(line)){
				break;
			}
			System.out.println(line);
			
		}
		
		
		
	}

}

8.文件切割---Properties(配置文件)

 

1,切割文件的原理:一个源对应多个目的;切割文件的两种方式。

2,碎片文件的命名和编号。

3,程序代码体现。

4,如何记录源文件的类型以及碎片的个数(建立配置信息文件)(其实也可以将这些信息记录碎片文件中)

5,通过Properties属性集建立配置文件。

                            常见方法:load(InputStream) load(Reader) 

                            store(OutputStream,conmments),store(Writer,conmments)

6,Properties的作为配置在应用程序很常见,主要用于将配置信息持久化。

                            建立的配置文件扩展名规范: .properties。

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;

public class SplitFileTest {

	private static final int BUFFER_SIZE = 1048576;//1024*1024
	private static final String LINE_SEPARATOR = System.getProperty("line.separator");

	/**
	 * @param args
	 * @throws IOException 
	 */
	public static void main(String[] args) throws IOException {
//		练习:将一个媒体文件切割成多个碎片。
		/*
		 * 思路:
		 * 1,读取源文件,将源文件的数据分别复制到多个文件中。
		 * 2,切割方式有两种:按照碎片个数切,要么按照指定大小切。
		 * 3,一个输入流对应多个输出流。
		 * 4,每一个碎片都需要编号,顺序不要错。
		 * 
		 */
		
		File srcFile = new File("E:\\1.mp3");
		File partsDir = new File("E:\\PartFiles");
		splitFile(srcFile,partsDir);

	}

	/**
	 * 切割文件。
	 * @param srcFile
	 * @param partsDir
	 * @throws IOException 
	 */
	public static void splitFile(File srcFile, File partsDir) throws IOException {
	
		//健壮性的判断。
		if(!(srcFile.exists() && srcFile.isFile())){
			throw new RuntimeException("源文件不是正确的文件或者不存在");
		}
		
		if(!partsDir.exists()){
			partsDir.mkdirs();
		}
		
		//1,使用字节流读取流和源文件关联。
		FileInputStream fis = new FileInputStream(srcFile);
		
		//2,明确目的。目的输出流有多个,只创建引用。
		FileOutputStream fos = null;
		
		//3,定义缓冲区。1M.
		byte[] buf = new byte[BUFFER_SIZE];//1M
		
		//4,频繁读写操作。
		int len = 0;
		int count = 1;//碎片文件的编号。
		while((len=fis.read(buf))!=-1){
			//创建输出流对象。只要满足了缓冲区大小,碎片数据确定,直接往碎片文件中写数据 。
			//碎片文件存储到partsDir中,名称为编号+part扩展名。
			fos = new FileOutputStream(new File(partsDir,(count++)+".part")); 
			//将缓冲区中的数据写入到碎片文件中。
			fos.write(buf,0,len);
			//直接关闭输出流。
			fos.close();
		}
		
		/*
		 * 将源文件以及切割的一些信息也保存起来随着碎片文件一起发送。
		 * 信息;
		 * 1,源文件的名称(文件类型)
		 * 2,切割的碎片的个数。
		 * 将这些信息单独封装到一个文件中。
		 * 还要一个输出流完成此动作。
		 */
		String filename = srcFile.getName();
		int partCount = count;
		
		//创建一个输出流。
		fos = new FileOutputStream(new File(partsDir,count+".properties"));
		//创建一个属性集。
		Properties prop = new Properties();
		//将配置信息存储到属性集中。
		prop.setProperty("filename", srcFile.getName());
		prop.setProperty("partcount", Integer.toString(partCount));
		
		//将属性集中的信息持久化。
		prop.store(fos, "part file info");
		
//		fos.write(("filename="+filename+LINE_SEPARATOR).getBytes());
//		fos.write(("partcount="+Integer.toString(partCount)).getBytes());
		fos.close();
		fis.close();
	}


}


9. 文件的合并 SequenceInputStream

 

1,文件合并的原理:多个源对应一个目的。

2,每一个碎片对应一个输入流,多个输入流对象先要进行集合存储。

3,SequenceInputStream可以解决这个问题。将多个源合并成一个源。

4,读取配置文件信息,并加入健壮性的判断。

5,独立解决碎片文件缺少的判断。

 

// 合并碎片文件。

                   /*

                    * 思路: 1,碎片文件有很多,每一个碎片都需要和读取流关联。2,每一个读取流读取到的数据都需要通过一个输出流写入到一个文件中。

                    * 3,原理:多个源--->一个目的地。

                    *

                    *

                    * 如下代码的问题: 碎片过多,会产生很多的输入流对象,这是正常的,不正常在于,面对每一个输入流对象去操作。

                    * 当流对象过多时,必须先存储起来。面的流对象的容器操作更容易。 1,需要容器。 2,将流对象和碎片文件关联后存储到容器中。

                    * 3,遍历容器获取其中的流对象,在进行频繁的读写操作。 4,即使关流也是遍历容器对每一个流对象进行close的调用。

                    

                    */

import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.SequenceInputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;

public class MergerFileTest3 {

	/**
	 * @param args
	 * @throws IOException 
	 */
	public static void main(String[] args) throws IOException {
		
		File partsDir = new File("E:\\PartFiles");
		
		mergerFile(partsDir);
	}

	public static void mergerFile(File partsDir) throws IOException {
		
		/*
		 * 虽然合并成功,问题如下:
		 * 1,如何明确碎片的个数,来确定循环的次数,以明确要有多少个输入流对象。
		 * 2,如何知道合并的文件的类型。
		 * 解决方案:应该先读取配置文件。
		 */
		//1,获取配置文件。
		File configFile = getConfigFile(partsDir);
		
		//2,获取配置文件信息容器。获取配置信息的属性集。
		Properties prop = getProperties(configFile);
		
		//3,将属性集对象传递合并方法中。
		merge(partsDir,prop);
	}


	//根据配置文件获取配置信息属性集。
	private static Properties getProperties(File configFile) throws IOException {
		FileInputStream fis = null;
		Properties prop = new Properties();
		try{
			//读取流和配置文件相关联。
			fis = new FileInputStream(configFile);
			//将流中的数据加载的集合中。
			prop.load(fis);
		}finally{
			if(fis!=null){
				try{
				fis.close();
				}catch(IOException e){
					//写日志,记录异常信息。便于维护。
				}
			}
		}
		return prop;
	}


	//根据碎片目录获取配置文件对象。
	private static File getConfigFile(File partsDir) {
		
		if(!(partsDir.exists() &&partsDir.isDirectory())){
			throw new RuntimeException(partsDir.toString()+",不是有效目录");
		}
	
		//1,判断碎片文件目录中是否存在properties文件。使用过滤器完成。
		File[] files = partsDir.listFiles(new FileFilter() {
			@Override
			public boolean accept(File pathname) {
				
				return pathname.getName().endsWith(".properties");
			}
		});
		
		if(files.length!=1){
			throw new RuntimeException("properties扩展名的文件不存在,或不唯一");
		}
		
		
		File configFile = files[0];
		return configFile;
	}



	private static void merge(File partsDir,Properties prop) throws FileNotFoundException,
			IOException {
		
		//获取属性集中的信息。
		 String filename = prop.getProperty("filename");
		 int partCount = Integer.parseInt(prop.getProperty("partcount"));
		
		
		
		//使用io包中的SequenceInputStream,对碎片文件进行合并,将多个读取流合并成一个读取流。
		List<FileInputStream> list = new ArrayList<FileInputStream>();
		
		for (int i = 1; i < partCount; i++) {
			list.add(new FileInputStream(new File(partsDir, i + ".part")));
		}
		
		//怎么获取枚举对象呢?List自身是无法获取枚举Enumeration对象的,考虑到Collections中去找。
		Enumeration<FileInputStream> en = Collections.enumeration(list);
		
		//源。
		SequenceInputStream sis = new SequenceInputStream(en);
		
		//目的。
		FileOutputStream fos = new FileOutputStream(new File(partsDir,filename));
		
		//不断的读写。
		byte[] buf = new byte[4096];
		int len = 0;
		while((len=sis.read(buf))!=-1){
			fos.write(buf,0,len);
		}
		
		fos.close();
		sis.close();
	}

}

10. 对象的序列化和反序列化Serializable

1,对象的序列化,就是将对象持久化存储。

                   目的是将对象的数据持久化,便于下次程序对该对象数据的使用。          

2,写入到文件中使用FileOutputStream,但是无法写入对象,

                   需要额外功能,在OutputStream的子类中找到了ObjectOutputStream。              

3,写入对象时出现了异常NotSerializableException,通过查阅api。

                   发现序列化的类必须实现一个接口Serializable,才可以启动序列化功能。           

4,对象存储后,必须通过OutputInputStream才可以读取出来。

                   readObject读取,编译时会出现ClassNotFoundException,说明读取对象文件必须要有对应的字节码文件。

                   读取对象就是对象的反序列化。               

5,当写入对象的类源码发现变化,在反序列化出现InvalidClassException。

                   通过阅读序列化接口Serializable发现 序列号变化了。

                   根据该接口的文档描述,显示声明序列号 private static final long serialVersionUID = 42L;

                   所以,该接口的作用就是用来启动类的序列化功能,并要求给类加一个序列号。                  

6,注意:静态的数据不会被序列化,非静态的数据想要不被序列化可以用transient(瞬态)关键字修饰。

     

11.总结

字节流:

FileInputStream  FileOutputStream

BufferedInputStream BufferedOutputStream

字符流:

FileReader FileWriter

InputStreamReader OutputStreamWriter

BufferedReader BufferedWriter

所学习的对象都是用于操作文件中数据的对象,可以进行字符编码转换的,有可以提供效率的。

需求:将键盘录入的数据存储到文件中。

System.in.read();//阻塞

IO流中对象很多,解决问题(处理设备上的数据时)到底该用哪个对象呢?★★★★★

把IO流进行了规律的总结(四个明确):

明确一:要操作的数据是数据源还是数据目的。

         源:InputStream    Reader

         目的:OutputStream Writer

先根据需求明确要读,还是要写。

明确二:要操作的设备上的数据是字节还是文本呢?

         源:

                   字节:InputStream

                   文本:Reader

         目的:

                   字节:OutputStream

                   文本:Writer

已经明确到了具体的体系上。

明确三:明确数据所在的具体设备。

         源设备:

                   硬盘:文件  File开头。

                   内存:数组,字符串。

                   键盘:System.in;

                   网络:Socket

         目的设备:

                   硬盘:文件  File开头。

                   内存:数组,字符串。

                   屏幕:System.out

                   网络:Socket

完全可以明确具体要使用哪个流对象。

明确四:是否需要额外功能呢?

         额外功能:

                   转换吗?转换流。InputStreamReader OutputStreamWriter

                   高效吗?缓冲区对象。BufferedXXX

                   有多个源(字节流)吗?序列流。SequenceInputStream

                   对象需要序列化吗?ObjectInputStream,ObjectOutputStream

                   需要保证数据输出的表现形式吗?打印流。PrintStream PrintWriter

                   需要操作基本类型数据保证字节原样性吗?DataOutputStream DataInputStream           

------------------------------

五个需求:★★★★★★★★

需求0:将字符串写入到文件中。

明确一:有源吗?有目的吗?

         源;字符串。String,不用io指定定义String字符串就可以了。

         目的:文件。使用IO技术,输出流  OutputStream Writer。       

明确二:是文本数据吗?

         是!

         目的:Writer。    

明确三:具体设备是?

         目的设备:

                   硬盘:File开头的对象 Writer体系中的。

明确出来,具体要使用的对象是FileWriter。

FileWriter fw = newFileWriter("a.txt");

fw.write(string);

明确四:需要额外功能吗?

                   需要,高效。缓冲区对象。BufferedWriter。

BufferedWriter bufw = new BufferedWriter(newFileWriter("a.txt"));

bufw.write(string);

bufw.newLine();

bufw.flush();

----------------------------------

需求1:复制一个文本文件,有可能对复制过程中的文本进行过滤。

比如将文档中的nba,替换成"美国职业篮球大联盟"写入到目的文件中。

明确一:有源,有目的/

         源:InputStream  Reader

         目的:OutputStream Writer

         强调一点:如果仅做复制动作,不需要考虑数据是字节还是文本。直接使用字节流就哦了。

         FileInputStreamfis = new FileInputStream("a.txt");      

         FileOutputStreamfos = new FileOutputStream("b.txt"); 

         但是如果在复制过程中,需要文本中的字符数据进行操作,必须使用字符流。

明确二:是纯文本数据吗?

         是!

         源:Reader

         目的:Writer

明确三:具体设备:

         源设备:

                   硬盘:File开头对象 FileReader

         目的设备:

                   硬盘:File开头对象 FileWriter

                  

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

FileWriter fw = newFileWriter("b.txt");

明确四:需要额外功能吗?        

         需要,高效。

BufferedReader bufr = newBufferedReader(new FileReader("a.txt"));

BufferedWriter bufw = new BufferedWriter(newFileWriter("b.txt"));

String line = bufr.readLine();

line = line.replace("nba","美国职业篮球大联盟");

bufw.write(line);

bufw.flush();

bufw.close();

----------------------------------

需求2:读取键盘录入将数据存储到文件中。

 

明确一:有源,有目的。

         源:InputStream  Reader

         目的:OutputStream Writer

明确二:是纯文本数据吗?

         是!

         源:Reader

         目的:Writer

明确三:具体设备:

         源设备:键盘。System.in;

         目的设备:硬盘。FileWriter

InputStream in = System.in;

FileWriter fw = newFileWriter("a.txt");

byte[] buf = new byte[1024];

int len = in.read(buf);

String str = new String(buf,0,len);

fw.write(str);

这样虽然可以做,但是很麻烦。 

明确四:需要额外功能吗?

         需要,转换。因为明确源的体系是Reader。可是具体设备System.in 这是字节流,需要字符流,需要转换功能,

                   将字节流转成字符流。字节-->字符   InputStreamReader

         InputStreamReaderisr = new InputStreamReader(System.in);

         FileWriterfw = new FileWriter("a.txt");        

         还需要额外功能吗?需要,高效。

         BufferedReaderbufr = new BufferedReader(new InputStreamReader(System.in));

         BufferedWriterbufw = new BufferedWriter(new FileWriter("a.txt"));

----------------------------------

需求3:读取文本文件打印到屏幕上。

明确一:有源,有目的。

         源:InputStream  Reader

         目的:OutputStream Writer

明确二:是纯文本数据吗?

         是!

         源:Reader

         目的:Writer

明确三:具体设备:

         源设备:硬盘,FileReader

         目的设备:屏幕,System.out

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

PrintStream out = System.out;

out.println();

其实这样就已经完成需求的。

PrintStream对象中有N多的print方法。

fr.read();

System.out.println();

明确四:额外功能。

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

         BufferedWriterbufw = new BufferedWriter(new OutputStreamWriter(System.out));

需求4:读取文件中的文本数据,将数据按照UTF-8的方式存储到文件中。

明确一:有源,有目的。

         源:InputStream  Reader

         目的:OutputStream Writer

明确二:是纯文本数据吗?

         是!

         源:Reader

         目的:Writer

明确三:具体设备:

         源设备:硬盘,FileReader

         目的设备:硬盘,FileWriter

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

FileWriter fw = newFileWriter("b.txt");

但是,不符合题中的要求,对于目的要求必须是UTF-8的编码。

所以必须使用额外功能。

明确四:需要额外功能,转换。

FileReader fr = newFileReader("a.txt");//默认编码。

OutputStreamWriter osw = newOutputStreamWriter(new FileOutputStream("b.txt"),"utf-8");

还需要其他额外功能吗?需要。缓冲区。

BufferedReader bufr = newBufferedReader(new FileReader("a.txt"));

BufferedWriter bufw = newBufferedWriter(new OutputStreamWriter(newFileOutputStream("b.txt"),"utf-8"));



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值