黑马程序员-IO流的小总结(下)

---------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流! ----------------------

File类:用来将文件或者文件夹封装成对象。方便对文件与文件夹的属性信息进行操作。
File对象可以作为参数传递给流的构造函数。
流只能操作数据,File对象操作数据封装成的文件信息。
File f1=new File("路径\\文件");//把文件做成File对象
File f2=new File("路径","文件");//和上面一样,后面参数可以传进来String参数变量
File d=new File("路径");
File f3=new File(d,"文件");//和上面二者一样
File类中有一个seperator静态变量,是File的目录分隔符,可以跨平台
 File类常见方法
1创建
boolean createNewFile():指定位置创建文件,如果已存在,则返回false。和输出流不一样
File dir=new File("路径");dir.mkdir();//创建一层目录,返回true false
dir.mkdirs()//创建路径中的多层目录 返回true false

2删除
boolean delete();删除此对象文件,返回是否删成功
void deleteOnExit();退出java虚拟机时候删除此文件。这是为了防止,如果流正在对文件读写,删除文件是不会被允许的。本命令就是告诉程序,一会儿虚拟机停了,就把 这个文件删了。
3判断
boolean canExecute()判断文件是否能执行
boolean exists()判断文件是否存在
boolean isFile()
boolean isDirectory()是否文件夹
boolean isHidden()是否是隐藏文件
boolean isAbsolute()判断对象是否是绝对路径的,即使对象封装内容不存在也能够判断
在判断文件对象是否是文件或者目录时,必须先要判断该文件对象的封装的内容是否存在
4获取信息
getName()
getPath()
getParent()
getAbsolutePath()
long lastModified()
long length()
list();//获取此文件夹对象中的所有一级文件和文件夹名称,放在一个String类型数组中。如果被一个文件
File f=new File("c:\\");//对象调用,返回空。
String[] names=f.list();


f.listRoots()//获取系统所有的根目录列表,即所有磁盘盘符列表。返回一个File数组。
String[] list()//把文件夹对象中的一级文件文件夹名称都返回给一个String数组
String[] list(FilenameFilter filter)//把文件夹对象中的满足filter的一级文件文件夹名称都返回给一个String数组
File[] listFiles()返回一个文件对象数组,数组中是文件夹中所有的一级文件和目录的File对象。
File[] listFiles(FilenameFilter filter)把文件夹对象中的满足filter的一级文件文件夹对象都返回给一个File数组

public class FileDemo2 {
	public static void main(String[]args){
		//listDemo();
		listDemo2();
	}
	public static void listDemo(){
		File f=new File("c:\\");
		//String[] list(FilenameFilter filter)把文件夹对象中的满足filter的一级文件文件夹名称都返回给一个	
		//String数组。
		//过滤器要重写accept方法。
		String[] names=f.list(new FilenameFilter(){
			public boolean accept(File dir,String name){
				return name.endsWith(".x");
			}
		});
		for(String name:names){
			System.out.println(name );
		}
	}
	
	public static void listDemo2(){
		File f=new File("c:\\");
		
		//File[] list(FilenameFilter filter)把文件夹对象中的满足filter的一级文件文件夹名称都返回给一个	
		//File数组。
		//过滤器要重写accept方法。
		File[] files=f.listFiles(new FilenameFilter(){
			public boolean accept(File dir,String name){
				return name.endsWith(".x");
			}
		});
		for(File file:files){
			System.out.println("name:"+file.getName() );
			System.out.println("path:"+file.getPath());
			System.out.println("length:"+file.length());
		}
	}
	
	public static void listRootsDemo(){
		File[] files=File.listRoots();
		for(File f:files){
			System.out.println(f);
		}
	}
}

     删除一个文件夹内所有文件

/*删除一个文件及其所有内容。从里往外删除,用递归*/
public class RemoveDir {
	public static void main(String[]args){
		removeDir(new File("e:\\111 test"));
	}
	
	public static void removeDir(File dir){
		File[]files=dir.listFiles();
		
		for(File f:files){
			
			if(f.isDirectory()){
				removeDir(f);
			}
			else System.out.println(f.toString()+"::"+f.delete());
		}
		 System.out.println(dir.toString()+"--DIR::"+dir.delete());
	}
}

/*将一个指定目录下的java文件的绝对路径,存储到一个文本文件中。建立一个java文件列表文件
 * 思路:1对指定目录进行递归
 * 2获取递归过程中所有的java文件的路径
 * 3将这些路径存储到集合中
 * 4将集合中的数据写入到文件中
  */
public class JavaFileList {
	public static void main(String[]args) throws IOException{
		
		List<File> list=new ArrayList<File>();
		fileToList(new File("E:\\test"),list);
		if(list.isEmpty()) System.out.println("没内容");
		else{
			for(File f:list){
				System.out.println(f);
			}
		}
		writeTofile(list);
	}
	
	public static void fileToList(File dir,List<File> list){
		File[] files=dir.listFiles();
		for(File f:files){
			if(f.isDirectory()){
				fileToList(f,list);
			}else if(f.getName().endsWith(".java")){
				list.add(f);
			}
		}
		
	}
	
	public static void writeTofile(List<File> list) throws IOException{
		BufferedWriter buw=new BufferedWriter(new FileWriter("E:\\testto.txt"));
		for(File f:list){
			buw.write(f.getAbsolutePath());
			buw.newLine();
			buw.flush();
		}
		buw.close();
	}
}


打印流:该流提供了打印方法,可以将各种数据类型的数据都原样打印。
字节打印流PrintStream:构造函数可以接收的参数类型: 
1 File对象:File
2 字符串路径:String
3 字节输出流:OutputStream

字符输出流PrintWriter:
1 File对象:File
2 字符串路径:String
3 字节输出流:OutputStream
4 字符输出流:Writer

合并流:SequenceInputStream。可以将多个字节输入流按顺序汇总到一起。需要用Vector的Enumeration对象,这是个有序集合对象,来有序装入各输入流
Vector<FileInputStream>v=new Vector<FileInputStream>();
v.add(new FileInputStream("E:\\1.txt"));
v.add(new FileInputStream("E:\\2.txt"));
v.add(new FileInputStream("E:\\3.txt"));

Enumeration<FileInputStream> en=v.elements();//Enumeration是一个枚举集合,通常用于接收vector的elements()方法得到的所有元素
SequenceInputStream sis=new SequenceInputStream(en);
现在sis就是一个字节输入流了,可以配一个字节输出流进行操作了。

切割文件:
一个输入流,一个固定大小字节数组,每次read一次填写入数组,然后new一个输出流到一个新文件。直到read值为-1.
public class SplitFile {
	static int i=1;
	public static void main(String[]args) throws IOException{
		SplitFile();
		HebingFile();
	}
	
	public static void SplitFile() throws IOException{
		FileInputStream fis=new FileInputStream("E:\\Again4.mp3");
		
		FileOutputStream fos=null;
		byte[]b=new byte[1024*1024];
		int len=0;
		
		while((len=fis.read(b))!=-1){
			fos=new FileOutputStream("E:\\SplitFiles\\"+(i++)+".part");
			fos.write(b, 0, len);
			fos.close();
		}
		fis.close();
	}
	
	public static void HebingFile() throws IOException{
		Vector<FileInputStream> v=new Vector<FileInputStream>();
		while(i>1){
			v.add(new FileInputStream("E:\\SplitFiles\\"+(--i)+".part"));
		}
		
		Enumeration en=v.elements();
		SequenceInputStream sis=new SequenceInputStream(en);
		
		FileOutputStream fos=new FileOutputStream("E:\\Again5.mp3");
		
		byte[] a=new byte[1024*1024];
		int len;
		
		while((len=sis.read(a))!=-1){
			fos.write(a,0,len);
			fos.flush();
		}
		fos.close();
		sis.close();
	}
}

 对象的序列化:
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("文件"));
oos.writeObject(new Object());
实例化一个对象,并把此对象放入文件中,以便长期存储。
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("文件"));
Object obj=ois.readObject();
把对象从文件中读出来。
注意,对象序列化时候是把堆内存中的对象写入文件,类中如果有静态变量,因为是存在共享区,所以没有静态变量不能被序列化存储。每一个类都有一个序列号,这个序列号是通过其中的所有成员计算出来的。将一个类的对象序列化之后,对象同样也有序列号,用来和它的类对应。如果在读取对象的时候,这个类变了,那么读取对象就会失败,因为找不到对应的序列号来关联加载自己的类了。

 管道流:
输入流和输出流成对儿绑定,二者分别写在两个线程中,以免死锁。因为二者绑定,所以一个写一个读都是同时操作的同一临界区。

public class PipeStreamDemo {
	public static void main(String []args) throws IOException{
		PipedInputStream in=new PipedInputStream();
		PipedOutputStream out=new PipedOutputStream();
		
		in.connect(out);//将输入输出管道流绑定对接
		
		Read r=new Read(in);
		Write w=new Write(out);
		
		new Thread(r).start();
		new Thread(w).start();
	}
}


class Read implements Runnable{
	
		private PipedInputStream in;
		Read(PipedInputStream in){
			this.in=in;
		}
			
		public void run(){
			
			try {
				
				byte[]buf=new byte[1024];
				System.out.println("读取前,没有数据阻塞");
				int len=in.read(buf);//从管道流默认的缓存区里读出数据,输出流默认向这里面写数据

				System.out.println("读取数据,阻塞结束");
				String s=new String(buf,0,len);
				System.out.println(s);
				in.close();
			} catch (IOException e) {
				throw(new RuntimeException("管道读取流失败"));
			}
		}
}

class Write implements Runnable{
	private PipedOutputStream out;
	Write(PipedOutputStream out){
		this.out=out;
	}
	public void run(){
		try {
			System.out.println("我要输入了,但是先休息6秒");
			Thread.sleep(6000);
			System.out.println("休息结束,开始输入");
			out.write("pipe lai la".getBytes());//把内容写到管道流中默认的缓存区里面,输入流默认从这里读取数据
			out.close();
		} catch (Exception e) {
			throw new RuntimeException("管道输出流失败");
		}
		
	}
}

 RandomAccessFile类

该类不算是IO体系中的子类。而是直接继承Object。但是它是IO包中的成员。因为具备读写功能。内部封装了一个巨大数组,而且通过指针对数组的元素进行操作。可以通过getFilePointer获得指针位置,同时可以通过seek改变指针的位置。其实完成读写的原理就是内部封装了字节输入流和字节输出流。通过构造函数可以看出,该类只能操作文件。而且操作文件还有固定模式:r,rw,rws,rwd。
new此类新对象时,如果是读写rw模式,指定文件不存在,那么新建。如果指定文件已经存在,则不再新建,直接在原文件上读写。(不同于一般的流)
如果模式为只读r,不会创建文件,指定文件不存在,则异常。
通过这个类的特点可以实现多线程下载。

public class RandomAccessFileDemo {
	public static void main(String[]args) throws IOException{
		writeFile();
		readFile();
	}
	
	public static void writeFile() throws IOException{
		RandomAccessFile raf=new RandomAccessFile("E:\\ran.txt","rw");
		
		raf.write("李四".getBytes());//写入两个汉字,是四个字节
		raf.writeInt(258);//把输入内容补充到4个字节存储。
		raf.write("张三".getBytes());
		raf.writeInt(97);
		raf.write("王五".getBytes());
		raf.writeInt(99);
		raf.close();
	}
	
	public static void writeFile2() throws IOException{
		RandomAccessFile raf=new RandomAccessFile("E:\\ran.txt","rw");
		
		raf.seek(40);
		raf.write("周七".getBytes());//通过seek方法,可以在数组中任意位置进行写
	}
	
	public static void readFile()throws IOException{
		RandomAccessFile raf=new RandomAccessFile("E:\\ran.txt","r");
		
		//调整对象中的指针
		raf.seek(8);//指针指向缓存数组中角标为8的位置,之后read就从这里开始向后推进
		//跳过指定的字节数
		raf.skipBytes(8);//指针向后跳过8个字节。
		byte[] buf=new byte[4];
		raf.read(buf);//从缓存数组中读取buf长度的数据
		String name=new String(buf);
		int age=raf.readInt();//直接读取缓存中4个字节
		System.out.print(name);
		
		raf.close();
	}
}

DataInputStream与DataOutputStream:可用于操作基本数据类型的数据流对象。
凡是操作基本数据类型就用这里两个流。

ublic class DataStreamDemo {
	static OutputStreamWriter osw=null;
	public static void main(String[]args) throws IOException{
		//writeData();
		//readData();
		writeUTFDemo();//用UTF-8修改版写的数据只有相应的流readUTF才能读出来
		
		osw=new OutputStreamWriter(new FileOutputStream("E:\\utf-8.txt"),"utf-8");
		osw.write("你好");//标准utf-8写出的两个汉字是6字节
		osw.close();
		
		osw=osw=new OutputStreamWriter(new FileOutputStream("E:\\gbk.txt"),"gbk");
		osw.write("你好");//gbk写出的两个汉字是4字节
		osw.close();
	}
	
	public static void writeData() throws IOException{
		DataOutputStream dos=new DataOutputStream(new FileOutputStream("E:\\data.txt"));
		
		dos.writeInt(234);//4个字节
		dos.writeBoolean(true);//1个字节
		dos.writeDouble(9887.543);//8个字节
		
		dos.close();
	}
	
	public static void readData() throws IOException{
		DataInputStream dis=new DataInputStream(new FileInputStream("E:\\data.txt"));
		//读取时一定要按照顺序和类型读,因为存储的不同类型字节数不一样,读错了就乱码了
		int num=dis.readInt();
		boolean b=dis.readBoolean();
		double d=dis.readDouble();
		
		System.out.println(num);
		System.out.println(b);
		System.out.println(d);
		
		dis.close();
	}
	
	//
	public static void writeUTFDemo() throws IOException{
		DataOutputStream dos=new DataOutputStream(new FileOutputStream("E:\\utfdata.txt"));
		
		dos.writeUTF("你好");//用UTF-8修改版编码来写入你好,这个修改版两个字占8个字节
		
		dos.close();
	}
}

 ByteArrayInputStream和ByteArrayOutputStream
因为这两个流对象操作的都是数组,没有使用系统资源,所以关闭这个流无效。因为。这两个流都不调用底层资源。此类中的方法在关闭流后仍可被调用,而且不会产生任何IO异常。
ByteArrayInputStream在构造的时候,需要接受数据源,而且数据源是一个字节数组。
ByteArrayOutputStream的缓冲区会随着数据的不断写入而自动增长。 这个输出流的目的就是自带的被封装的这个缓冲数组。
流操作:
源设备:
键盘System.in,硬盘FileStream 内存ArrayStream
目的设备:
控制台System.out 硬盘FileStream内存ArrayStream

编码问题:
String Arrays.toString(Byte[]a)返回字节数组a的字符串表示形式,没有编码解码过程,仅仅是把数组内容以字符串形式输出。
编码:将字符串s中的字符都转换成字节的方法:s.getBytes();返回一个数组,数组中Byte元素对应字符串中每个字符
getBytes("码表");可以用指定码表编码,默认的是GBK。
解码:String s=new String(字节数组,"码表");返回s字符串是字节数组按照指定码表解码得到的字符串。码表默认是GBK码表。
服务器端一般用的是ISO8859-1码表,不支持中文。客户端发去的中文字符在服务器端无法正常解码。这个时候需要在服务器端对解码后的乱码重新用ISO8859-1编码,然后再用中文码表解码即可。这种重新编码再解码的方式只是用于中英文不兼容码表,如果是GBK和utf-8码表之间这样做就不行了。

联通问题:
现象:新建记事本里面输入联通两个字,保存退出,再打开,乱码。
解释:编码问题。utf-8的编码规则是,有时候一个字节读出一字符,有时候二字节,有时候三字节。怎样判断是该几个字节读取一字符?utf-8的字节都有标识位,字节第一位是0表示一个字节读一字符;第一字节前面是110,第二字节前面是10,表示这两个字节读一个字符;第一个字节前面是1110,第二个字节前面是10,第三个字节前面是10,表示这三个字节读一个字符。记事本在被输入联通两个字并保存时,也对联通进行了编码(默认GBK编码)。很偶然的情况是,GBK编码后得到的4个字节,恰好符合utf-8编码两字节读取一字符的标识位特点!然后解码的时候,记事本认出这些标志位,就错误的以为这是utf-8的编码并用utf-8进行解码,当然出来的是乱码。
解决方法:在联通前面加一个中文汉字,让记事本从第一个字节开始判断的时候不被骗就行。不能加英文,因为英文都一字节的,同样符合utf-8规则,依然会骗解码器。

键盘输入流+输出流+集合 示例

/*有五个学生,每个学生3门课成绩
 * 键盘输入以上数据(姓名,三科成绩)
 * 输入格式:如 zhangsan, 30, 40, 60 计算出总成绩
 * 并把学生信息和总分数高低顺序存放在磁盘文件“stud.txt”中*/
public class StudentInfoTest {
	static Reader r=null;
	static Writer w=null;
	static BufferedReader br=null;
	static BufferedWriter bw=null;
	static TreeSet<Studentw> ts=new TreeSet<Studentw>();
	public static void main(String[]args) throws IOException{
		shuru();
		Iterator<Studentw> it=ts.iterator();
		bw=new BufferedWriter(new OutputStreamWriter(new FileOutputStream("E:\\stud.txt")));
		while(it.hasNext()){
			Studentw st=it.next();
			String s=st.name+":语文"+st.yuwen+" 数学"+st.shuxue+" 英语"+st.yingyu+" 总分"+st.totle();
			bw.write(s);
			bw.newLine();
			bw.flush();
		}
		bw.close();
	}
	
	public static void shuru() throws IOException{
		for (int i=1;i<=5;i++){
			System.out.print("请输入第"+i+"个学生的信息:");
			br=new BufferedReader(new InputStreamReader(System.in));
			String s=br.readLine();
			String[] sp=s.split(", ");
			ts.add(new Studentw(sp[0],Integer.parseInt(sp[1]),Integer.parseInt(sp[2]),Integer.parseInt(sp[3])));
			
		}
		br.close();
	}
}

class Studentw implements Comparable<Studentw>{
	String name;
	int yuwen;
	int shuxue;
	int yingyu;
	Studentw(String name,int yuwen,int shuxue,int yingyu){
		this.name=name;
		this.yuwen=yuwen;
		this.shuxue=shuxue;
		this.yingyu=yingyu;
	}
	
	public int totle(){
		return yuwen+shuxue+yingyu;
	}
	
	public int hashcode(){
		return name.hashCode()*77;
	}
	
	public boolean equeals(Object obj){
		if(!(obj instanceof Studentw))
			throw new ClassCastException("类型不匹配");
		Studentw s=(Studentw)obj;
		
		return this.name.equals(s.name) && this.totle()==s.totle();
	}
	
	public int compareTo(Studentw st1){
		if(this.totle()==st1.totle()){
			return this.name.compareTo(st1.name);
		}else return this.totle()-st1.totle();
		
	}
}

---------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流! ----------------------

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值