------- android培训、java培训、期待与您交流! ----------
1)字节流和字符流的由来:
在编程语言发展初期,ASCII码表中的数据的输入输出使用的是字节流,由于后期的发展需要,我们需要统一所有国家的文字,创造国际标准的编码表-UNICODE码表,在发展的过程中,我们用字节流读取文字,但不直接操作,而是先查指定的编码表,获取对应的文字,再进行操作。此时,字节流与码表一起就形成了字符流。简而言之:字符流=字节流+码表
输入流和输出流相对于内存设备而言,将外设中的数据读取到内存中,我们称之为输入流;将内存中的数据读出到外部设备中,我们称之为输出流。
字节流的顶层父类:1.InputStream 2.OutputStream
字符流的顶层父类:1.Writer 2.Reader
2) 字符流:
FileWriter类(写):是Writer的子类。使用的过程中有以下几点需要注意:
①构造方法中都是有参数的构造方法,一般而言传入的参数是文件的绝对路径。创建对象的过程中,如果文件存在,则会被覆盖;文件不存在,程序会自动创建文件。
②写文件时,其实是将写入的内容暂时存储在输出流(存储区域)中,当使用该类的flush方法进行刷新后,才会将输出流的内容存入到文件中去。如果构造方法的参数只有文件的路径,当不断的writer方法写文件时,此时文件中的内容其实是在不断的被覆盖。而当构造方法中传入的参数不仅有路径还有追加的模式(将该模式置为null即可,如FileWriter f=new FileWriter(“d:\\123.txt”,true))的时候,此时写入的东西不是以一种覆盖的方式,而是以一种追加的方式,即写入的东西都会存在在一个文件中。
③对于flush与close而言,flush方法是将写入到写入流的内容保存到文件中,又叫做刷新,就相当于在文件中写入东西按下保存键的原理类似;close方法是将写入流的资源释放,此时就不能再往文件中写入内容了,就相当于将保存好的文件关闭一样。所以在写入文件的时候,flush可以用多次,而close只可以用一次,一旦关闭就不能再进行任何操作。
④记事本程序是微软的产品,记事本中的换行在java中的表示时\r\n,而java虚拟机中的换行符就是\n。一般我们可以直接调用line.separator(移植性非常好)获取系统的换行符,此时就不用纠结这一个问题了。
FileReader类(读):是Reader的子类。使用的过程中有以下几点需要注意:
①在读文件的时候,必须指定文件的路径,并且该文件必须存在,否则或抛出FileNotFindException的异常。同理,读文件时,也是将文件内容从文件中读取到读取流中,所以在读完文件的结尾处,也必须进行关闭资源(读取流)的操作。
②在用该类的Read()方法读取文件的时候,返回值是0-65535之间的数,所以在读取文件后要进行转换的操作;其次文件内容的开始和结尾处其实都有始标识和尾标识,当文件读取到最后的时候,已经没有可读的内容时,此时会读取文件的尾标识(具体都不太一样),然后返回-1来表示文件已经读取结束。
③用该类的Read(char[] c)读取文件的时候,传入的参数是一个字符数组,返回值是读到了该数组中的所有字符的长度。即:
FileReader f=new FileReader(“d:\\123.txt”);
char[] c= new char[3];
int num=0;;
While((num=f.read(c))!=-1)
{
System.out.println(new String(buf,0,num));//此处是读取从0到该数组中拥有的字符的长度之间读取该数组
}
④对于②和③这两种读取方式的区别:②是一个一个读取的而③是按照数组长度一部分一部分读取。③的效率更高。
对于FileWriter和FileReader的使用,分别如以下所示:
①FileWriterDemo.java
import java.io.FileWriter;
import java.io.IOException;
public class FileWriterDemo {
private static final String LINE_SEPARATOR = System.getProperty("line.separator");//获取系统的换行符
public static void main(String[] args) {
FileWriter fw=null;//这里必须给fw赋初值为null,因为流对象的创建一般是在外部声明并且初始化,在try内部进行创建实例。
try {
fw = new FileWriter("d:/file.txt",true);//追加的模式写入文件
int line=0;
String[] f=new String[]{"你好","世界","我叫","小明"};
while(line<4)
{
fw.write(f[line]+LINE_SEPARATOR);
line++;
}
fw.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
System.out.println(e+"文件写入失败");
} finally
{
try {
if(fr!=null)
fw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
throw new RuntimeException("文件关闭失败");
}
}
}
}
②FileReaderDemo.java
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class FileReaderDemo {
public static void main(String[] args) {
FileReader fr=null;
try {
fr = new FileReader("d:/file.txt");
char[] buf=new char[1024];//一般而言从硬盘中读文件是按照KB为单位读取的,所以建议使用1024作为数组长度。
int len=0;
try {
while((len=fr.read(buf))!=-1)
{
System.out.println(new String(buf,0,len));
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally
{
try {
if(fr!=null)
fr.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
BufferedWriter类(写入流缓冲区):本身就是一个封装了数组的对象,为了提高写入的效率。
BufferedReader类(读取流缓冲区):本身就是一个封装了数组的对象,为了提高写入的效率。
以写入流为例,带有缓冲区的写入流的步骤过程如下:
将写入的内容(包括换行符即readline方法)通过写入流传入到内存的缓冲区中,此时缓冲区会按行读取内容,然后将读到的每一行内容都变成字符串按行写入到文件中去。
对于这两个对象,必须与FileWriter和FileReader配合使用。
装饰设计模式:对一组对象的功能进行增强时,就可以使用该模式。字符流的缓冲区的设计原理就是这个。
特点:装饰类与被装饰类都必须所属同一个接口或者父类。
装饰设计模式与继承的区别:
装饰设计模式与继承都能对一组对象进行功能的扩展,但是装饰设计模式比继承更为灵活。
3) 字节流:除了能够存储字符,文字等等外,还能存储音频,视频等媒体文件,这一点是字符流无法比拟的。
它涉及的类的使用方式与字符流基本一致,具体参考API文档
以下字节流的应用范例:这两种应用,传递数据的效率都比较高,比较好。
StreamDemo.java
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class StreamDemo {
public static void main(String[] args) {
//IOmethod_1();
IOmethod_2();
}
public static void IOmethod_2() {
// 字节流的第一种表现方式
FileInputStream fis=null;
FileOutputStream fos=null;
try {
fis = new FileInputStream("d:/樊凡 - 回味.mp3");
fos=new FileOutputStream("d:/回味2.mp3");
byte[] buf=new byte[1024];
int leng=0;
try {
while((leng=fis.read(buf))!=-1)
{
fos.write(buf,0,leng);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
fis.close();
fos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void IOmethod_1() {
//字节流的第二种表现方式
FileInputStream fis;
BufferedInputStream bis=null;
FileOutputStream fos;
BufferedOutputStream bos=null;
try {
fis = new FileInputStream("d:/樊凡 - 回味.mp3");
bis=new BufferedInputStream(fis);
fos=new FileOutputStream("d:/回味.mp3");
bos=new BufferedOutputStream(fos);
int ch=0;
try {
while((ch=bis.read())!=-1)
{
bos.write(ch);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
if(bis!=null)
bis.close();
bos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
4) 转换流:InputStreamReader是字节流转换为字符流的桥梁;OutputStreamWriter是字符流转换为字节流的桥梁。当想要指定编码表时,只能用转换流来做。
总结:IO流操作的基本规律:即“四个明确”
①明确源和目的:源(读取流):InputStream Reader
目的(输出流):OutputStream Writer
②明确数据是否是纯文本数据:源:是:Reader
否:InputStream
目的:是:Writer
否:OutputStream
③明确具体的设备:源设备:硬盘:File
键盘:System.in
内存:数组
网络:Socket流
目的设备:硬盘:File
控制台:System.out
内存:数组
网络:Socket流
④明确是否需要其他额外功能:
①是否需要高效:缓冲区
②是否需要转换:转换流
5) File类:IO流中的类只能操作文件中的数据,而不能操作文件,而File是操作文件以及文件夹的类。
案例1:文件夹的深度遍历
①FileDemo.java
import java.io.File;
import com.io.filesplit.SufixFilter;
/*需求:1.删除一个拥有文件以及文件文件夹的文件夹
2.深度遍历一个文件夹,包括其内部的所有文件与文件夹
*/
public class FileDemo {
public static void main(String[] args) {
File f=new File("d:/file");
DeepErgodicFile(f,1);
//removeFiles(f);
}
public static void removeFiles(File f) {
// 删除一个文件夹下所有的文件以及文件夹
//File f2=f;
File[] files=f.listFiles();
for(File file:files)
{
if(file.isDirectory())
{
removeFiles(file);
}
else
{
System.out.println(file.getName()+file.delete());
}
}
System.out.println(f.getName()+f.delete());
}
public static void DeepErgodicFile(File f,int count) {
// 深度遍历一个文件夹
//System.out.println(getSpace(count)+f.getName());
File[] files=f.listFiles();
count++;
for(File file:files)
{
if(file.isDirectory())
{
DeepErgodicFile(file,count);
}
else
{
System.out.println(getSpace(count)+file.getName());
}
}
}
public static String getSpace(int count) {
//文件夹与文件缩进
StringBuilder sb=new StringBuilder();
for(int i=0;i<count;i++)
{
sb.append("--");
}
return sb.toString();
}
}
案例2:filter过滤器的使用:包括过滤文件和过滤文件名
①FilterDemo.java
import java.io.File;
/*需求:1.在一个文件夹中或者硬盘中寻找到所有以任意文件后缀结尾的文件(只在本目录下,不进行深度遍历)
* 2.在一个文件夹或者硬盘中寻找到所有的不包括隐藏文件文件(只在本目录下,不进行深度遍历)
*/
public class FilterDemo {
public static void main(String[] args) {
File f=new File("d:/file");
file_search(f);
//file_search2(f);
//fileErgodic(f);
}
public static void fileErgodic(File f) {
// 遍历当前文件夹中的内容,隐藏文件也能够遍历出来
String[] names=f.list();
for(String name:names)
{
System.out.println(name);
}
}
public static void file_search2(File f) {
// 寻找到所有的以任意文件格式结尾的文件
String[] names=f.list(new fileFilter2(".txt"));
for(String name:names)
{
System.out.println(name);
}
}
public static void file_search(File f) {
// 寻找到所有的不隐藏文件
File[] names=f.listFiles(new fileFilter());
for(File name:names)
{
System.out.println(name);
}
}
}
②文件名过滤器 fileFilter2.java
import java.io.File;
import java.io.FilenameFilter;
public class fileFilter2 implements FilenameFilter{
private String suffix;
public fileFilter2(String suffix)
{
this.suffix=suffix;
}
public boolean accept(File dir, String name) {
//
return (name.endsWith(suffix) && !dir.isHidden());
}
}
③文件过滤器 fileFilter.java
import java.io.File;
import java.io.FileFilter;
public class fileFilter implements FileFilter{
public boolean accept(File pathname) {
//
return !pathname.isHidden();
}
}
6) Properties类:
案例:获取指定目录下,将指定扩展名的所有文件(深度遍历)的绝对路径写入一个文本文件中。
①程序:FileListDemo.java
import java.io.File;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import com.io.filesplit.SufixFilter;
//需求:获取指定目录下,将指定扩展名的所有文件(深度遍历)的绝对路径写入一个文本文件中。
public class FileListDemo {
public static void main(String[] args) {
File dir=new File("d:/file");
File file=new File("d:/file/file.txt");
String suffix=".txt";
ArrayList<File> list=new ArrayList<File>();
SufixFilter sf=new SufixFilter(suffix);
getFileList(dir,sf,list);
writeTofile(file,list);
}
public static void writeTofile(File file, ArrayList<File> list) {
// 将集合中的文件的文件名称写入同一个文本文件中
BufferedWriter bw=null;
try {
bw=new BufferedWriter(new FileWriter(file));
for(int i=0;i<list.size();i++)
{
bw.write(list.get(i).getName()+"="+list.get(i).getAbsolutePath());
bw.newLine();
bw.flush();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally
{
try {
bw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void getFileList(File dir, SufixFilter sf,
ArrayList<File> list) {
// 获取指定目录下的所有指定扩展名的文件,将这些文件存储于集合中
File[] files=dir.listFiles();
for(File f:files)
{
if(f.isDirectory())
{
getFileList(f, sf, list);
}
else
{
if(sf.accept(f, f.getName()))
list.add(f);
}
}
}
}
7) 打印流:PrintStream与PrintWriter
8) 序列流(SequenceInputStream):将多个流与集合封装成了对象,可以用这一个流对象来将多个文件合并到一个文件下,方便用户使用。
9) 文件切割机与文件合并:
案例:1.将一个任意文件切割成n份。分别存在一个文件夹中,同时将该文件的配置信息存储下来
2.将切割了的文件,根据配置信息合并成一个文件。
①SplitDemo.java
import java.io.File;
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.Properties;
//需求:1.将一个任意文件切割成n份。分别存在一个文件夹中,同时将该文件的配置信息存储下来
//2.将切割了的文件,根据配置信息合并成一个文件。
public class SplitDemo {
private static final int size = 1024*1024;
public static void main(String[] args) {
//File file=new File("d:/music/aa.mp3");
//System.out.println(file.length()+"..."+size);
//System.out.println(file.getParent()+"\\split");
//splitFile(file);
File dir=new File("d:/music/split/");
mergeFile(dir);
}
public static void mergeFile(File dir) {
// 将一些碎片文件合并成一个完整的文件
SequenceInputStream sis=null;
FileOutputStream fos=null;
Properties prop=new Properties();
File[] Confiles=dir.listFiles(new SufixFilter(".properties"));
//System.out.println(Confiles.length);
if(Confiles.length!=1)
{
throw new RuntimeException("该目录下的配置文件不唯一或者没有配置文件");
}
try {
prop.load(new FileInputStream(Confiles[0]));
String filename=prop.getProperty("filename");
int num=Integer.parseInt(prop.getProperty("splitnum"));
ArrayList<FileInputStream> al=new ArrayList<FileInputStream>();
File[] files=dir.listFiles(new SufixFilter(".part"));
for(int i=0;i<num-1;i++)
{
al.add(new FileInputStream(files[i]));
}
Enumeration<FileInputStream> en=Collections.enumeration(al);
sis=new SequenceInputStream(en);
fos=new FileOutputStream(new File("d:/music/merge",filename));
byte[] buff=new byte[size];
int len=0;
while((len=sis.read(buff))!=-1)
{
fos.write(buff, 0, len);
fos.flush();
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
if(sis!=null)
sis.close();
if(fos!=null)
fos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void splitFile(File file) {
// 将一个文件切割成n个碎片分别存储在不同的文件中。
FileInputStream fis=null;
FileOutputStream fos;
try {
fis=new FileInputStream(file);
byte[] buff=new byte[size];
int count=1;
int len=0;
String dir=file.getParent()+"\\split";//创建需要文件被切割后碎片存放的主目录
try {
while((len=fis.read(buff))!=-1)
{
fos=new FileOutputStream(new File(dir,(count++)+".part"));
fos.write(buff, 0, len);
fos.close();
}
Properties prop=new Properties();
prop.setProperty("splitnum", count+"");
prop.setProperty("filename",file.getName());
prop.store(new FileOutputStream(new File(dir,count+".properties")),"filesplit");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
fis.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
②后缀名过滤器:SufixFilter.java
import java.io.File;
import java.io.FilenameFilter;
public class SufixFilter implements FilenameFilter{
private String suffix;
public SufixFilter(String suffix) {
super();
this.suffix = suffix;
}
public boolean accept(File dir, String name) {
//
return name.endsWith(suffix);
}
}
10) 操作对象流:ObjectInputStream和ObjectOutputStream
11) 随即访问文件(RandomAccessFile类):支持对文件的随即读取和写入,自身具备读写的方法。通过skipByte(int x),seek(int x)来达到随机访问。
特点:
①它不是IO体系中的子类
②既能读,又能写
③该对象内部维护了一个byte数组,并通过指针获取该数字中的元素
④可以通过getFilePointer方法获取该指针的位置,和通过seek方法设置指针的位置。
⑤其实该对象就是将字节输入流和字节输出流进行了封装
⑥该对象的源和目的只能是文件,通过构造函数就可以看出来。
⑦写文件的过程中,如果文件不存在,则自动创建,如果存在,则不创建。这一点与一般的流不一样。
12) 管道流:PipedInputStream和PipedOutputStream 输入输出可以直接进行连接,一般结合线程使用。
13) 操作基本数据类型的的流:DataInputStream和DataOutputStream
14) 操作字节数组的流:ByteArrayInputStream和ByteArrayOutputStream 源和目的都是内存,不操作底层数据,没有将数据与硬盘设备等设备关联起来,维护的仅仅是内存中的数据(存储在数组中),所以这类流不需要关闭,关闭也无效。同时该流不会抛出IO异常。
类似的还有操作字符数组的:CharArrayReader和CharArrayWriter以及操作字符串数组的:StringReader和StringWriter
15) IO流之编码表:
编码表的由来:计算机只能识别二进制数据,早期由来是电信号。为了方便应用计算机,让它可以识别各个国家的文字,就将各个国家的文字用数字来表示的,并一一对应,这就形成了一张表,即编码表。
常见的编码表:
ASCII:美国标准信息交换码,用一个字节的7位来表示。
ISO8859-1:拉丁码表,欧洲码表。用一个字节的8位来表示。
GB2312:中国的中文编码表
GBK:中国的中文编码表的升级,融合了更多的中文文字符号。
Unicode:国际标准编码表。融合多种文字,所有文字都用两个字节来表示,是为了统一所有国家的编码表而出现的。
UTF-8:国际标准编码表的改进,所有文字最多用三个字节来表示,即一个字节能表示的文字用一个字节,两个字节能表示的用两个字节,三个的用三个的。
目前中国最常用的是GBK和UTF-8码
Java中编解码的表现:
编码:字符串->字节数组
解码:字节数组->字符串