Java I/O流操作(三)File文件操作及打印流和序列流合并流

 
- -------------------------------------------File文件操作---------------------------------------------
 用来将文件或者文件夹封装成对象
方便对文件与文件夹进行操作
File对象可以作为参数传递给流的构造函数.
下面来看一下File类的构造方法:

File file =new File("a.txt");

File file2 =new File("C:\\file\\test\\a.txt");
File d =new File("C:\\file\\test\\");
File file3 =new File(d, "a.txt");
File file4 =new File("C:\\file\\test\\","a.txt");//这和file3是一回事
System.out.println(file +"\r\n" + file2 + "\r\n" + file3 +"\r\n" + file4 + "\r\n");
控制台输出为:
a.txt
C:\file\test\a.txt
C:\file\test\a.txt
C:\file\test\a.txt
但是我们在程序中,文件的路径分隔符只能在windows平台下,在Linux就不能用了,怎么现在跨平台呢?
File file5 = new File("C:"+File.separator+"file"+File.separator+"test"+File.separator+"", "a.txt");
 
接下来就是File类的常用方法了:
创建方法:
 boolean   createNewFile ()在指定位置创建文件,如果文件不存在,则创建成功返回true,如果文件已经存在,则不创建,返回false
这和输出流不同,输出流一旦实例化就会创建文件,文件不存在创建,存在则覆盖.
mkdir ()创建文件夹
mkdirs ()创建文件夹,包括父文件夹
删除方法:
boolean delete()
void deleteOnExit()
这两个方法是有区别的:
delete ()根据抽象的路径名称删除文件,如果程序出现异常没有执行到该行代码,那么这个文件就不会被执行,有人会说放在finally执行,但是如果一个程序正在使用该文件,那么这个文件还是不能被删除.
deleteOnExit()这个方法不会发生这种情况,因为该方法在虚拟机停终止,文件就会被删除.
判断方法:
exists ()  表示抽象的路径名称所指示的文件或文件夹是否存在
canExecute ()应用程序能否执行抽象路径名称所指示的文件
canRead ()应用程序能否读取抽象路径名称所指示的文件
canWrite ()  应用程序能否修改抽象路径名称所指示的文件
isDirectory ()抽象路径名称所指示的是不是文件夹
isFile ()抽象路径名称所指示的是不是文件
isAbsolute ()判断是否是绝对路径
File file =new File("a.txt");
System.out.println("isDirectory:"+file.isDirectory());
System.out.println("isFile:"+file.isFile());
发现控制台输出

isDirectory:falseisFile:false

发现它既不是文件也不是目录,为什么会出现这样的情况呢?
原来用上面的两个方法判断时候,需要先判断该抽象的路径名称所指定的file和directory是否存在.存在才能进行判断.
 
获取信息:
 
File   getAbsoluteFile ()  返回File对象的绝对路径,但是他把这个字符串封装成了File对象返回 这也是和 getAbsolutePath的主要区别
String   getAbsolutePath ()  返回File对象的绝对路径
String   getPath ()  返回File对象的路径,如果File里的抽象路径名称是绝对路径,如果是相对路径,就返回相对路径
String   getName ()如果File对象里的抽象路径名称指定的是目录,那么返回的目录最后的文件夹名称,如果是文件,则返回文件的名称
String   getParent () 如果File对象里的抽象路径名称指定的是目录,那么返回的目录最后的文件夹的上一个文件夹的名称,如果是文件,则返回文件所在目录的名称,如果象路径名称指定的是相对路径,有可能返回null,如:File file = new File("a.txt");那么该file的父目录就是null.
File   getParentFile ()这和 getParent的区别就是 ,这个方法把getParent方法的返回值封装成了File对象
long   length ()  返回文件的大小

renameTo ( File  dest)  重命名:
例如:
File f =new File("C:\\test\\test.txt");
File f2 =new File("C:\\test\\testDemo.txt");
f.renameTo(f2);
如果盘符或者目录不同这就成了剪切了
 
文件列表:
File []   listRoots ()  返回系统盘符的名称 如:C D盘等
String[]   list ()  返回File目录下的所有文件 如果抽象的路径名称指定的是一个文件将会出现空指针异常;并且该目录一定要存在
例如1:
for(File file: File.listRoots()){
System.out.println(file);
}
输出结果为:
C:\
D:\
E:\
F:\
G:\
例如2:
File file =new File("C:\\");
for (String name : file.list()) {
System.out.println(name);
}
输出结果为:C盘目录下的所有文件和目录包含隐藏文件,但是不包含子文件夹里的文件
 
文件过滤:
例如我想输出在某个文件夹下的java文件,那么这时候就要用到过滤文件:
String[]   list ( FilenameFilter  filter)  我们查看API不难发现, FilenameFilter  是一个接口里面只有一个方法booleanaccept(File dir,String name)

publicstatic void method_1() {

    final File file =new File("D:\\");
    String[] names = file.list(new FilenameFilter() {
    publicboolean accept(File dir, String name) {
        return name.endsWith(".java");
        }
    });
 
    for (String name : names)
    System.out.println(name);
 

}

下面来看一下:
File[]   listFiles ()  ;这个方法和 list ()  方法的区别是 list ()  方法是返回一个个字符串的,而 listFiles ()是返回一个个File对象.那么就可以通过File对象获取文件的信息了,所以File[]listFiles()方法更使用一些.例如:
File file =new File("C:\\");
for(File file2 : file.listFiles()){
System.out.println(file2.getName());
}
listFiles ()同样也有自己的过滤功能.通过 listFiles ( FileFilter  filter)  方法可以实现.

通过递归实现列出目录下的所有文件
publicstatic void main(String[] args) {
File file =new File("E:\\MyeclipseWorkbenck\\shop");
method_1(file);
}
 
publicstatic void method_1(File file) {
System.out.println(file.getAbsolutePath());
File[] files = file.listFiles();
for (File file2 : files) {
if(file2.isDirectory()){
method_1(file2);
}else {
System.out.println(file2.getName());
}
 
}
}
 
删除带目录的文件,如果直接使用delete方法,则不能删除成功,因为java删除文件的原理是这样的,把最里面的文件删除,然后删除文件夹
publicstatic void main(String[] args) {
    File file =new File("D:\\java");
    deleteDir(file);
}
publicstatic void deleteDir(File file){
    File[] files = file.listFiles();
    for(File file2 : files){
    //如果是目录
        if(file2.isDirectory()){
    //则使用递归 调用本身
        deleteDir(file2);
    }else {
    //不是目录就直接删除
        file2.delete();
    }
    }
    //然后删除空文件夹
    file.delete();
}
 
怎么把java文件的路径存在一个文件里面,方便以后查找.
publicclass JavaFileItem {
publicstatic void main(String[] args) {
    List<File> list =new ArrayList<File>();
    saveJavaFilePath(new File("E:\\MyeclipseWorkbenck\\HeimaTest"), list);
    writeFilePath(list,"javaItem.txt");
}
//则把java文件放在集合中
publicstatic void saveJavaFilePath(File dir, List<File> list) {
    File[] files = dir.listFiles();
    for (File file : files) {
        if (file.isDirectory()) {
            saveJavaFilePath(file, list);
        }else {
    //如果是java文件,则把该文件放在集合中
        if (file.getAbsolutePath().endsWith(".java"))
            list.add(file);
        }
    }
    }
//把java的路径信息写入文件中
publicstatic void writeFilePath(List<File> files, String targetFile) {
BufferedWriter buffw =null;
try {
        FileWriter fw =new FileWriter(targetFile);
        buffw =new BufferedWriter(fw);
        for (File file : files) {
            //把java文件的绝对路径写入文件
            buffw.write(file.getAbsolutePath());
            //换行
            buffw.newLine();
            //使用字符流注意刷新
            buffw.flush();
        }
    }catch (IOException e) {
        e.printStackTrace();
    }finally {
        try {
            if (buffw !=null)
                buffw.close();
            }catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
这里就是使用递归和IO操作的结合了.
-------------------------关于Properties类:--------------------------------------------
 
关于Properties类:
Properties是Hashtable的子类,也就是说它具备map结婚的特点,而且它里面存储的键值对都是字符串
他是集合和IO相结合的集合容器
该对象的特点是可以用于键值对形式的配置文件
getProperty ( String  key)  根据key从Properties对象中获取值
setProperty ( String  key, String  value)  为Properties对象设置key和value
store ( OutputStream  out, String  comments)  把Properties对象储存到一个文件里面去,底层通过字节输出流处理
store ( Writer  writer, String  comments)  这是JDK1.6后才有的,直接使用字符输出流就可以把Properties对象储存到文件中
load ( InputStream  inStream)  读取属性列表从字节输入流中,加载某个键值对文件,使得Properties与该文件相关联
load ( Reader  reader)  这是JDK1.6后才出现的,读取属性列表从字符输入流中,
模拟限制软件使用次数:
publicstatic void count()throws IOException{
    Properties properties =new Properties();
    File file =new File("count.ini");
    if(!file.exists()){
        file.createNewFile();
    }
    InputStream is =new FileInputStream(file);
    properties.load(is);
    String times = properties.getProperty("times");
    int count = 0;
    if(times!=null){
        count = Integer.parseInt(times);
        if(count>=5)
        System.out.println("你使用的次数已到,请续费!");
    }
    count++;
    OutputStream os =new FileOutputStream(file);
    properties.setProperty("times", count+"");
    properties.store(os,"count times");
    os.close();
    is.close();
}
 
----------------------------------打印流------------------------
下面学习打印流对象PrintStream  PrintWriter
首先 PrintStream

大概的意思是说PrintStream相比其他的输出流增强了许多功能,也就是很方便的打印数据,另外PrintStream还可以自动刷新, 也就是说一个字节数组被写入或者调用println方法或者写入了换行字符或者字节\n 后这个flush方法会被自动调用
PrintStream的常用的构造方法:
PrintStream ( File  file)
PrintStream ( OutputStream  out)
PrintStream ( OutputStream  out, boolean autoFlush)  
PrintStream ( String  fileName)
PrintStream ( OutputStream  out, boolean autoFlush,  String  encoding)
看一下 PrintWriter的API

大概的意思是说,PrintWriter类不像PrintStream类,如果设置了自动刷新(PrintWriter(Writer out, boolean autoFlush) 或者通过PrintWriter(OutputStream out, boolean autoFlush) 设置),只有当调用println()printf() format()方法才会被刷新,而不是无论什么时候只要出现换行字符就会输出,这些方法使用的是平台自己的行分隔符标准,而不是使用换行字符.
接着看一下他的主要构造方法:
PrintWriter ( File  file)
PrintWriter ( String  fileName)
PrintWriter ( OutputStream  out)
PrintWriter ( OutputStream  out, boolean autoFlush)  
PrintWriter ( Writer  out)
PrintWriter ( Writer  out, boolean autoFlush)
比较PrintStream和PrintWriter的构造方法的最大区别PrintWriter可以接受字符输出流Writer而PrintStream不能,所以PrintWriter更常用.
下面通过一个实例来运用它:
publicstatic void print()throws IOException{
    //获取键盘输入
    BufferedReader buffr =new BufferedReader(new InputStreamReader(System.in));
    //目的是控制台
    PrintWriter pw =new PrintWriter(System.out);
    String value =null;
    while((value=buffr.readLine())!=null){
        pw.write(value);
        //注意刷新
        pw.flush();
    }
    pw.close();
    buffr.close();
 
}
控制太输出如下:

发现他没有换行,看上去不美观:怎么实现换行呢?我们来看一下这两个个方法
println ( String  x)
API是这样解释 println ( String  x)方法的Prints a String and then terminates the line. This method behaves as though it invokes   print(String)  and then println().
先打印字符串然后终止这行,也就是先调用print(String str)方法然后在调用println()方法.
 
API是这样解释 println() 方法 的
Terminates the current line by writing the line separator string. The line separator string is defined by the system property line.separator, and is not necessarily a single newline character ( '\n'). 也就是通过行分隔符终止当前行,行分隔符通过系统属性 line.separator设置的而不需要简单的换行符'\n'
是不是我们通过println();方法就可以解决上面的问题呢?
while((value=buffr.readLine())!=null){
    pw.write(value);
    pw.println();//终止一行
    //手动刷新
    pw.flush();
}
还有方法可以解决,因为上面说了可以通过设置是否自动刷新而不是手动刷新,所以可以把  pw.flush();注释掉如:
PrintWriter pw =new PrintWriter(System.out,true);//设置自动刷新
String value =null;
while((value=buffr.readLine())!=null){
    pw.write(value);
    pw.println();//因为println()printf()format()都会触发刷新
    //pw.flush();
}
 
我们还可以不使用write(),使用println(String)就会更加简单
PrintWriter pw =new PrintWriter(System.out,true););//设置自动刷新
String value =null;
while((value=buffr.readLine())!=null){
    pw.println(value);();//因为println()printf()format()都会触发刷新
}
 
我们还可以猜想:
PrintWriter pw =new PrintWriter(System.out,true););//设置自动刷新
String value =null;
while((value=buffr.readLine())!=null){
pw.write(value+"\r\n");//末尾加上换行符是否会触发自动刷新呢?
}
答案是否定的, 在PrintWriter的API有这样一句话These methods use the platform's own notion of line separator rather than the newline character. 也就是说
是否会触发自动刷新,java内部不是使用换行符("\r\n")而是使用平台内部的行分隔符(当然在windows他们是一样的).
那么我们可不可以模拟这个java内部所谓的行分隔符呢?在学习System类的时候我们可以得到系统的环境变量,在里面我们发现了行分隔符 "line.separator"
 
我们来测试一下
PrintWriter pw =new PrintWriter(System.out,true););//设置自动刷新
while((value=buffr.readLine())!=null){
pw.write(value+System.getProperty("line.separator"));//我们使用writer方法,里面的字符串参数加上行分隔符,因为java api里面说println()因为内部加上了行分隔符才会触发刷新的,那么现在我们手工加上行分隔符会触发刷新吗?
}
测试的结果还是不行!
那么为什么?java不是要系统的行分隔符吗?我现在已经加上了行分隔符还不行呢?
我们来看一下PrintWriter的源代码是怎么定义println(String)方法的
println(String)的定义:
    publicvoid println(String x) {
        synchronized (lock) {
            print(x);//先打印数据
            println();//再打印行分隔符,我们在进入println()是怎么定义的,
        }
    }
 
println()的定义:
    publicvoid println() {
        newLine();//打开是怎么样定义的
    }
newLine()方法的定义:
privatevoid newLine() {
    try {
        synchronized (lock) {
        ensureOpen();
        out.write(lineSeparator);//写入行分隔符
        if (autoFlush)//判断是否设置刷新
            out.flush();//如果设置为true,那么调用flush()方法
        }
    } catch (InterruptedIOException x) {
        Thread.currentThread().interrupt();
    } catch (IOException x) {
        trouble =true;
    }
}
经过我查看writer()方法的源代码,没有发现他去判断是否刷新,
结论:尽管我们在使用writer(String)方法的String参数后面加上了行分隔符,但是在writer方法里面根本不去判断是否设置了autoFlush=true
所以我们在writer(String)方法的String参数后面加上了行分隔符也不能达到预期的效果!
如果我们想更加了解PrintStream和PrintWriter的区别,查看源代码是最好的帮手.PrintStream是字节输出流的子类,PrintWriter是字符输出流的子类.
 
 
----------------------------------------- 序列流-合并流---------------------------------------------------------------------
简单的说就是把多个字节输入流合并成一个字节输入流,
SequenceInputStream ( Enumeration <? extends  InputStream > e)  里面传一个 Enumeration里面的元素使用了泛型限定.
SequenceInputStream ( InputStream  s1, InputStream  s2)  里面传入两个字节输入流对象
实例:怎么把3个文件的内容输入到一个文件中:
//通过Vector可以获取Enumeration
Vector<InputStream> vector =new Vector<InputStream>();
vector.add(new FileInputStream("C:\\1.txt"));
vector.add(new FileInputStream("C:\\2.txt"));
vector.add(new FileInputStream("C:\\3.txt"));
//实例化合并流
SequenceInputStream sequence =new SequenceInputStream(vector.elements());
InputStreamReader isr =new InputStreamReader(sequence);
BufferedReader fr=new BufferedReader(isr);
BufferedWriter bw =new BufferedWriter(new FileWriter("C:\\123.txt"));
String value =null;
while((value=fr.readLine())!=null){
bw.write(value);
bw.newLine();
}
bw.close();
sequence.close();
 
-----------------------------------------切割流------------------------------------------------
需求  :一个MP3文件切割成几个文件:
 
publicstatic void cut()throws Exception {
    InputStream is =new FileInputStream("C:\\mp3\\卓依婷- 好人好梦.mp3");
    OutputStream os =null;
    byte[] buffer =new byte[1024*1024];
    int len=0;
    int name = 1;
    while ((len=is.read(buffer))!=-1) {
        //每一次循环都会生成一个文件,这样就实现了切割
        os =new FileOutputStream("C:\\mp3\\part\\"+(name++)+".part");
        //每个文件写1M,最后一个文件可能没有1M
        os.write(buffer,0,len);
        os.close();
    }
    is.close();
}
发现part目录多了4个文件,并且大小和以前的文件一样:

从而也验证了前面4个文件都是1M最后一个没有1M的结论
现在怎么把上面的4个文件重新合并成一个文件
publicstatic void merger()throws Exception {
    List<InputStream> list =new ArrayList<InputStream>();
    for (int i = 1; i <= 4; i++) {
        list.add(new FileInputStream("C:\\mp3\\part\\" + (i) +".part"));
    }
    final Iterator<InputStream> iterator = list.iterator();
    Enumeration<InputStream> enumeration =new Enumeration<InputStream>(){
    publicboolean hasMoreElements() {
    return iterator.hasNext();
    }
    public InputStream nextElement() {
    return iterator.next();
}
};
    SequenceInputStream sequence =new SequenceInputStream(enumeration);
    OutputStream os =new FileOutputStream("C:\\mp3\\part\\卓依婷- 好人好梦.mp3");
    byte[] buffer =new byte[1024];
    int len = 0;
    while((len=sequence.read(buffer))!=-1){
        os.write(buffer,0,len);
    }
    os.close();
    sequence.close();
}

转载请注明出处 : http://blog.csdn.net/johnny901114/article/details/8710433

原文地址:https://www.cnblogs.com/xinyuyuanm/p/2979606.html