Java里的文件与IO--字节流和字符流

1,414 阅读7分钟

由于IO操作不像findViewById一样那么频繁,所以每当操作文件的时候,都要去百度一下,然后看看人家怎么写的,再把它copy过来,导致现在遇到IO操作的逻辑,就感觉有点怵,所以现在总结一下

1、File类的使用

File类的基本概念:表示文件和目录路径名的抽象表现形式
File类可以实现文件的创建、删除、重命名,获取路径修改时间等等
File类的构造方法传入的路径,可以存在,也可以不存在,都能构建出来一个file的对象

1.1、文件的创建、删除,移动和重命名

File f1=new File("D:\test\xxy.txt");
if(!f1.exists()){//判断xxy.txt是否存在 这个File对象是肯定存在的,但是file对象指向的file文件存不存在是未知的
    try {
        f1.createNewFile();//创建文件
    } catch (IOException e) {
        e.printStackTrace();
    }
    f1.isDirectory();//判断这个File指向的File是否是目录
    f1.isFile();//判断这个File指向的File是否为文件
    f1.delete();//只能删除没有子目录或没有文件的目录和文件
    f1.mkdir();//如果test目录已经存在,用着个方法创建
    f1.mkdirs();//如果test目录不存在,用这个方法创建会创建test和xxy.txt
}

1.2、获取当前目录所有文件,及获取文件常用的属性

File f2=new File("D:\test");
String[] fileNames=f2.list();//获取当前目录下的 所有文件名 只能查看当前目录,不能看到子目录的子目录
System.out.println(Arrays.toString(fileNames));
File[] files=f2.listFiles();//获取当前目录下所有文件的File对象
for (File file: files) {
    file.length();//获取文件的长度
    file.getName();//获取文件名
    file.getPath();//获取文件相对路径
    file.getAbsolutePath();//获取文件的绝对路径
    file.lastModified();//获取最后一次文件修改时间
    file.isHidden();//是否是隐藏文件
}
//过滤器  获取当前目录下后缀为txt的所有文件,
File[] files1=f2.listFiles(new FileFilter() {
    @Override
    public boolean accept(File pathname) {
        return pathname.getName().endsWith(".txt");
    }
});
//过滤器  获取当前目录下后缀为txt的所有文件,Stream表达式儿
File[] files2=f2.listFiles((file)->file.getName().endsWith(".txt"));

1.3、指定目录查找文件

找出指定目录下的某种类型文件

/***
 *
 * @param targetFile 指定的目录
 * @param ext  需要查找的文件后缀名
 */
public static void findFile(File targetFile,String ext){
    if(targetFile==null)
        return;
    if(targetFile.isDirectory()){
        File[] files=targetFile.listFiles();
        for (File f:
             files) {//递归调用,层数过多容易OOM
                findFile(f,ext);
            }

    }else{//file是一个非目录的文件儿
        String name=targetFile.getName();
        if(name.endsWith(ext)){
            System.out.println(targetFile.getAbsolutePath());
        }
    }

}

2、字节输入输出流

IO流:输入输出流(Input,Output)
流是一组有顺序,有起点和终点的字节集合,是对数据传输的总称和抽象。即数据在两设备间的传输称为流
流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作
IO流的分类
根据数据类型的不同分为:字符流和字节流 (最终传输的时候还是会转换为字节流) 根据数据流向不同分为:输入流(把数据从文件输出到程序里)和输出流(把数据从程序写到文件里)以程序为参照物

2.1字节输出输入流

字节输出流:
OutputStream类定义

public abstract class OutputStream implements Closeable, Flushable

这个抽象类是表示输出字节流的所有类的超类,输出流接收输出字节,并将这些字节发送到InputStream类某个接收器 要向文件中输出,使用FileOutputStream
字节输入流:
InputStream类 定义

public abstract class InputStream implements Closeable 

这个抽象类是便是字节输入流的所有类的超类
FileInputStream从文件系统中的某个文件获得输入字节 往文件里输出内容

public static  void  testOut(){
    //1、确定目标文件
    File file=new File("D:\test\a.txt");

    try {
        //2、构建输出流对象
        OutputStream fileOutputStream=new FileOutputStream(file,true);//append是否追加输出,true 不删除原来的内容在后边追加,false从头开始
        //3、往文件里输出内容
        String info="One,two,three,four,let,s go";
        //4、写到文件里
        fileOutputStream.write(info.getBytes());
        //5、关闭流
        fileOutputStream.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }

}

从文件里输入内容

public static void testIn(){
    //1、确定目标文件
    File file=new File("D:\test\a.txt");

    try {
        //2构建文件输入流对象
       InputStream fileInoutStream=new FileInputStream(file);
       //3、构建读取的容器
       byte[] bytes=new byte[1024];
       StringBuilder stringBuilder=new StringBuilder();
       int len=-1;//表示每次读取的字节长度
        //read 传入数组 把数据读入到数组中,并返回读取的字节数,当不等于-1时,表示读取到数据,等于-1,表示读完了
        while ((len=fileInoutStream.read(bytes))!=-1){
            //stringBuilder.append(new String(bytes));
            stringBuilder.append(new String(bytes,0,len));
        }
        System.out.println(stringBuilder.toString());
        //关闭输入流
        fileInoutStream.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

输入输出流的操作原理,每次只会操作一个字节(从文件中读取或写入)
在上边读文件的过程中,byte数组转字符串的时候,一定加上起始位置和读取的长度,不然读文件的时候,输出来的字符串很有可能会变多
如果循环次数就一次的话不会出现这种情况
你文件数据的长度正好是字节容器的倍数的时候也不会出现这种情况
比如文件字符串长度是5个,以‘我是凉白开’为例一共十个字节,你的字节容器的大小是4,循环到第三次的时候,他把读取到的两个字节填充到数组里,后两个数组的内容还是第二次循环读出来的内容,并没有清除掉,所以读出来的内容是‘我是凉白开开’
除了上面的问题外,还会出现一个问题,你容器大小是奇数时,或者是1的时候,读出来的东西是乱码都是问号,因为一个字符是两个字节,一个字节转字符串的时候它就会乱码,如何解决,这样当当当字符输入输出流来了

image.png

3、字符输入输出流

字符输出流
Writer: 写入字符流的抽象类,与OutputStream一样,对文件的操作使用FileWriter
字符输入流
Reader: 读取字符流的抽象类,使用FileReader类进行实例化
往文件写入数据

public static void testOut(){
   File file=new File("D:\test\a.txt");
   try {
       Writer writer=new FileWriter(file,false);//是否追加
       writer.write("我是凉白开 ");
       writer.close();
   } catch (IOException e) {
       e.printStackTrace();
   }
}

从文件读取数据

public static  void testIn(){
    File file=new File("D:\test\a.txt");
    try {
        Reader reader=new FileReader(file);
        char[] cs=new char[1];
        int len=-1;
        StringBuilder stringBuilder=new StringBuilder();
        while ((len=reader.read(cs))!=-1){
            stringBuilder.append(new String(cs,0,len));
        }
        System.out.println(stringBuilder);
        reader.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
  • 每次操作单位是一个字符
  • 文件字符操作流,会自带缓存,默认大小1024字节,在缓存满后或手动刷新缓存或=关闭流使,会把数据写入文件
  • 字符流的实现也是用字节流实现的 而字节输入输出流,不会有缓存,写一次,文件就更新一次
    在所有流操作中,字节永远是最基础的,任何基于字节的操作都是正确的,无论是文本文件还是二进制文件。如果确认流里面有可打印的字符,考虑使用字符流,由于编码不同,有的字符可能不占两个字节,可能占三个字节,可能占一个字节,比如GBK汉字占两个字节,utf-8汉字占三个字节,字符流就是根据编码,将一个或多个字节转为java里的Unicode字符,然后进行操作
    如果确认处理的流是可打印字符就用字符流,不确认就用字节流

4、用Java实现文件的简单复制

public class CopyFileDemo {
    public static void copy(String srcPath,String targetPath){
        File srcFile=new File(srcPath);
        File targetFile=new File(targetPath);
        InputStream inputStream = null;
        OutputStream outputStream = null;
        try {
             inputStream=new FileInputStream(srcFile);
             outputStream=new FileOutputStream(targetFile);
            byte[] bytes=new byte[1024];
            int len=-1;
            while ((len=inputStream.read(bytes))!=-1){
                outputStream.write(bytes,0,len);
            }
            inputStream.close();
            outputStream.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {

            if(inputStream!=null) inputStream.close();
            if(outputStream!=null) outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    public static void main(String[] args) {
            copy("D:\test\aa.jpg","D:\test\bb\aa.jpg");
    }
}

5、字节字符转换流

转换流,可以将一个字节流转换为字符流,也可以将一个字节流转换为字符流
OutoutStreamWriter:可以将输出的字符流转换为字节流的输出形式
InputStreamReader:将输入的字节流转换为字符流输入形式

image.png

public  static  void read(InputStream in){
    Reader reader=new InputStreamReader(in,Charset.forName("gbk"));
    char[] cs=new char[1024];
    int len=-1;
    try {
    while (true){
            if (!((len=reader.read(cs))!=-1)) break;

        System.out.println(new String(cs,0,len));
    }
    reader.close();

    } catch (IOException e) {
        e.printStackTrace();
    }
}
public static void write(OutputStream out) throws IOException {
    Writer write=new OutputStreamWriter(out,Charset.forName("gbk"));
    write.write("凉白开真好喝,有点甜!");
    write.close();
}
public static void main(String[] args) throws IOException {
            read(new FileInputStream(new File("D:\test\a.txt")));
            write(new FileOutputStream(new File("D:\test\a.txt") ));
}

6、总结

字节流和字符流和字节字符转换流在开发中还是用的挺多的,分别介绍了他们的使用,还是一句话还得多写才能记住,不然又给忘了!如果能帮到兄弟们,一键三连哦!