IO 【vaynexiao】

https://edu.51cto.com/course/17347.html 湯小洋

基礎

1. 簡介

IO:Input Output 輸入和輸出流

  • 通過IO流實現文件的輸入和輸出功能
  • 用於對文件進行讀寫的操作
    流stream:可以理解爲一組有順序的、有起點和終點的動態數據集合
  • 文件是數據在硬盤上的靜態存儲
  • 流是數據在傳輸時的動態形態

2. 文件的分類(兩類)

  • 文本文件
    可以使用記事本編輯的文件, .txt .java .properties
  • 二進制文件
    除了文本文件,其他所有文件都是二進制文件

InputStream是字節輸入流的頂層父類,常用子類:

  • FileInputStream
  • ByteArrayInputStream
  • ObjectInputStream
    OutputStream是字節輸出流的頂層父類,常用子類:
  • FileOutputStream
  • ByteArrayOutputStream
  • ObjectOutputStream

3. 流的分類(三類)

按流的方向(站在Java程序的角度)

  • 輸入流:用於讀取數據,比如從文件中讀取數據到程序中,由InputStream和Reader作爲父類
  • 輸出流:用於寫出數據,比如將程序中的數據寫出到文件中,由OutputStream和Writer作爲父類
    按流中數據的單位
  • 字節流byte:所操作的最小數據單元爲字節,由InputStream和OutputStream作爲父類
  • 字符流char:所操作的最小數據單元爲字符,由Reader和Writer作爲父類,
    【一個英文字符佔1個字節,一個漢字佔2個字節(GBK) 或 3個字節(UTF8)】
    按數據的來源
  • 節點流:直接對數據源進行操作,如操作文件
  • 包裝流:對一個節點流進行操作(包裝)
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述

基礎API

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.Date;

public class Test01 {
    public static void main(String[] args) throws URISyntaxException, IOException {
        //java.io.File

        /*
        * 創建一個File對象
        */

        // 方式1:指定文件的全路徑
        // File file = new File("D:\\resource\\a.txt"); // 絕對路徑
        // File file = new File("D:/resource/a.txt");
        // File file = new File("/home/soft01/a.txt");
        // File file = new File("a.txt"); //相對路徑

        // 方式2:指定父目錄的路徑和文件名
        // File file = new File("C:/resource", "code/a.txt");

        // 方式3:指定父目錄的File對象和文件名
        File file1 = new File(new File("C:/resource"), "code/a.txt");
        System.out.println("file1=:"+file1);//C:\resource\code\a.txt

        // 方式4:指定URI統一資源標識符
        File file2 = new File( Test01.class .getClassLoader() // 獲取類加載器
           .getResource("data.properties") // 加載類路徑下的文件(當然文件得存在),返回URL(UniformResource Locator統一資源定位符)
             .toURI() // 轉換爲URI(Uniform Resource Identifier統一資源標識符)
        );
        System.out.println("file2=:"+file2);//C:\idea_pro\io\target\classes\data.properties

        File file = new File("c.txt"); // 放在項目根目錄下才能找到,java默認是相對於項目的根目錄
        System.out.println("文件名:"+file.getName());//文件名:c.txt
        System.out.println("路徑名:"+file.getPath());//路徑名:c.txt
        System.out.println("絕對路徑名:"+file.getAbsolutePath());//絕對路徑名:C:\idea_pro\io\c.txt
        System.out.println("父目錄:"+file.getParent());//null
        System.out.println("父目錄文件對象:"+file.getParentFile()); //null 返回的是File對象
        System.out.println("文件長度:"+file.length()+"字節");//文件長度:10字節
        System.out.println("最後一次修改時間:"+new Date(file.lastModified()));//Sun May 10 16:59:38 CST 2020 lastModified()返回的是long值
        System.out.println("是否可讀:"+file.canRead());//是否可讀:true
        System.out.println("是否可寫:"+file.canWrite());//是否可寫:true
        System.out.println("是否爲普通文件:"+file.isFile());//是否爲普通文件:true
        System.out.println("是否爲目錄:"+file.isDirectory());//是否爲目錄:false
        System.out.println("是否爲隱藏文件:"+file.isHidden());//是否爲隱藏文件:false
        System.out.println(file); //c.txt 直接輸出File對象,本質上就是調用getPath()
        System.out.println(file.exists()); //true 判斷指定路徑的文件是否存在

        /*
        * 文件目錄的操作
        */
        File file3 = new File("C:/resource/c.txt");
        boolean isSuccess = file3.createNewFile();  // 創建一個空文件,返回一個boolean,表示成功或失敗
        System.out.println("是否創建成功"+isSuccess);
        file3.renameTo(new File("c2.txt")); //重命名 這裏是相對路徑會在項目根目錄下新建c2.txt文件
        boolean isSuccess2 = file3.delete(); //刪除文件
        System.out.println("刪除是否成功"+isSuccess2);

        File f = new File("D:/software");
        // boolean isSuccess = f.mkdir(); // 創建目錄,如果父目錄不存在 ,會導致創建失敗
        // boolean isSuccess = f.mkdirs(); // 創建包括父目錄的目錄,即級聯創建
        // System.out.println(isSuccess);
        String[] names = f.list(); // 獲取目錄下的所有文件和目錄的名稱
        System.out.println(Arrays.toString(names));
        File[] files = f.listFiles(); // 獲取目錄下的所有文件和目錄的對象
        System.out.println(Arrays.toString(files));

        // 常量
        System.out.println(File.separator);//當前系統分隔符

    }
}

案例

遞歸獲取指定目錄下所有文件

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;

public class Test01 {
    public static void main(String[] args) throws URISyntaxException, IOException {
        File file = new File("c:/idea_pro");
        display(file);

    }
    public  static  void  display(File  file)  {
                //  判斷文件是否存在
                if  (!file.exists())  {
                        return;
                }
                //  判斷是否爲目錄
                if  (file.isDirectory())  {
                        File[]  files  =  file.listFiles();
                        for  (File  f  :  files)  {
                                if(f.isFile()){
                                        System.out.println(f.getAbsolutePath());
                                }else{
                                        display(f);
                                }
                        }
                }else{
                        System.out.println(file.getAbsolutePath());
                }
        }
}

遞歸刪除全部空目錄

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;

public class Test01 {
    public static void main(String[] args) throws URISyntaxException, IOException {
        File file = new File("d:/resource");
        remove(file);

    }
    public static void remove(File file) {
        // 判斷是否爲空目錄
        if (file.isDirectory() && file.listFiles().length == 0) {
            file.delete();
            System.out.println("刪除空目錄:" + file.getAbsolutePath());
            // 刪除當前目錄後可能導致父目錄也爲空,所以需要對父目錄進行處理
            remove(file.getParentFile());
        } else if (file.isDirectory()) {
            // 如果當前目錄不爲空目錄,則獲取目錄下所有文件進行再處理
            File[] files = file.listFiles();
            for (File f : files) {
                remove(f); // 遞歸刪除
            }
        }
    }
}

FileInputStream 文件字節輸入流

import java.io.*;
import java.net.URISyntaxException;

public class Test01 {
    public static void main(String[] args) throws URISyntaxException, IOException {
        test01();
//        test02();
//        test03();
    }
    // 基本用法
    public static void test01(){
        FileInputStream  fis = null;//不初始化時,close方法報錯,懷疑可能未初始化
        try {
            //fis = new FileInputStream("a.txt");
            fis = new FileInputStream(new File("a.txt"));// 讀取項目根目錄下的文件
            /*
            int data = fis.read(); //處於阻塞狀態,讀取一個字節返回int類型的字節值,a就是97,讀取到末尾返回-1
            while(data!=-1){
                System.out.println((char)data);
                data=fis.read();
            }
            */
            int data = -1;//初始值-1
            while((data=fis.read()) != -1){
                System.out.println(data+" : "+(char)data);  // 字節流讀取中文時可能會出現亂碼(中文utf-8時是3個字節)
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally{
            if(fis != null){ // 需要判斷是否爲null,防止出現NullPointerException
                try {
                    fis.close(); // 關閉輸入流:只要是打開了外部的資源(文件\數據庫連接\網絡連接),在使用後都需要關閉,釋放資源
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            
        }
    }
    
    // 其他用法
    public static void test02(){
        // 在JDK7中,提供了一種新語法,叫做try-with-resource,能夠自動關閉外部資源,不需要寫finally,簡化代碼
        try(
                // 此處只能創建實現了AutoClosable接口的對象
                FileInputStream fis=new FileInputStream("a.txt");
        ){
            //System.out.println((char)fis.read());
            //System.out.println(fis.available()); // 流中可讀取的字節數
            
            byte[] buffer=new byte[1024*1024]; // 減少對硬盤的讀取次數,提高效率
//          int num = fis.read(buffer); // 一次性讀取buffer.length個字節到buffer中,返回實際讀取到的字節數,如果讀取到末尾,則返回-1
//          System.out.println(num);
//          String str = new String(buffer);
//          System.out.println(str);
//          
//          num = fis.read(buffer);
//          System.out.println(num);
            //System.out.println(new String(buffer, 0, num));  // 將指定長度的字節數組轉換爲字符串
            
            int num=-1;
            while((num=fis.read(buffer)) != -1){
                System.out.println(new String(buffer,0,num));
            }
        }catch(IOException e){
            e.printStackTrace();
        }
    }
    // 加載不同路徑下的文件
            public static void test03(){
        try(
                // 默認加載項目根路徑下
                //InputStream is=new FileInputStream("a.txt");

                // 加載classpath類路徑,即src目錄
                //InputStream is=Test01_FileInputStream.class.getClassLoader().getResourceAsStream("b.txt");

                // 加載當前類所在目錄
                InputStream is=Test01.class.getResourceAsStream("c.txt");
        ){
            System.out.println((char)is.read());
            System.out.println("加載文件成功!");
        }catch(IOException e){
            e.printStackTrace();
        }
    }
}

FileOutputStream 文件字節輸入流

在這裏插入圖片描述

import java.io.*;

public class Test01 {
    public static void main(String[] args) {
        FileOutputStream fos = null;
        try {
            // 如果文件不存在,會自動創建文件,如果文件存在,默認會覆蓋原文件的內容
            // fos=new FileOutputStream("a.txt");
            fos = new FileOutputStream("a.txt", true); // true表示以追加的形式寫數據
            byte[] data = "hello world".getBytes();
            fos.write(data); // 寫入數據,只是將數據寫入到內存的緩衝區中,並沒有真正寫入到文件中
            fos.flush(); // 刷新輸出流,完成數據的輸出,當關閉流時會自動調用該方法
            System.out.println("寫入數據成功");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

單個文件複製

import java.io.*;

public class Test01 {
    public static void main(String[] args) {
        // 如果文件不存在,會自動創建文件,如果文件存在,默認會覆蓋原文件的內容
//        test01();
        test02();
    }
    /*
     * 每次複製一個字節 read()方法默認讀取一個字節
     */
    public static void test01(){
        InputStream is = null;
        OutputStream  os = null;
        
        try {
            is = new FileInputStream("a.txt");
            os = new FileOutputStream("aa.txt");
            
            int data=-1;
            while((data=is.read())!=-1){
                os.write(data); // 讀一個字節,寫一個字節
            }
            System.out.println("文件複製成功");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            if(os!=null){
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(is!=null){
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    /*
     * 每次複製多個字節 read(arg)方法默認讀取指定參數個字節
     */
    public static void test02(){
        try(
            InputStream is = new FileInputStream("C:\\software\\oss-browser-win32-x64\\oss-browser.exe");
            OutputStream os = new FileOutputStream("oss.exe");
        ){
            byte[] buffer = new byte[1024*1024];
            int num = -1;
            while((num=is.read(buffer)) != -1){
                os.write(buffer, 0, num);
            }
            System.out.println("文件複製成功");
        }catch(IOException e){
            e.printStackTrace();
        }
    }
}

複製目錄下所有文件到目標目錄

import java.io.*;

public class Test01 {
    public static void main(String[] args) {
        copyFile("C:/software","c:/backup");
    }
    public static void copyFile(String srcPath, String destPath) {
        File srcFile = new File(srcPath);
        File destFile = new File(destPath);
        
        if(srcFile.isDirectory()){
            destFile=new File(destPath+File.separator+srcFile.getName());
            destFile.mkdirs();
            
            File[] files = srcFile.listFiles();
            for (File file : files) {
                copyFile(file.getAbsolutePath(), destFile.getAbsolutePath());
            }
        }else if(srcFile.isFile()){
            try(
                InputStream is=new FileInputStream(srcFile);
                OutputStream os=new FileOutputStream(destPath+File.separator+srcFile.getName());
            ){
                byte[] buffer=new byte[1024*1024];
                int num=-1;
                while((num=is.read(buffer))!=-1){
                    os.write(buffer, 0, num);
                }
                System.out.println("複製"+srcPath);
            }catch(IOException e){
                e.printStackTrace();
            }
        }
    }
}

字節數組輸入輸出流

import java.io.*;

public class Test01 {
    public static void main(String[] args) {
//    流(數據)的來源或目的地並不一定是文件,也可以是內存中的一塊空間,例如一個字節數組
//    ByteArrayInputStream 字節數組輸入流:從字節數組中讀取數據,即將字節數組當作流輸入的來源
//    ByteArrayOutputStream 字節數組輸出流:將數據寫出到內置的字節數組中,即將字節數組當作流輸出的目的地
        test01();
        test02();
    }
    // ByteArrayInputStream
    public static void test01(){
        byte[] data="welcome to java".getBytes();
        try {
            // 定義字節數組輸入流,數據來源爲字節數組
            InputStream is = new ByteArrayInputStream(data);
            
            int i=-1;
            while((i=is.read())!=-1){
                System.out.print((char)i);
            }
            //輸出welcome to java
        } catch (IOException e) {
            e.printStackTrace();
        }
        /*
         * ByteArrayInputStream和ByteArrayOutputStream不需要關閉
         * 因爲其操作的是內存中的字節數組,屬於內存讀寫流,並非操作的外部資源
         */
    }
    // ByteArrayOutputStream
            public static void test02(){
        try {
            ByteArrayOutputStream os = new ByteArrayOutputStream(); 
            // 將數據寫出到內置的字節數組中
            os.write("hello".getBytes());
            os.flush();
            // 獲取內置的字節數組中的數據
            byte[] buffer = os.toByteArray();
            System.out.println(new String(buffer));//hello
            System.out.println(os.toString());//hello
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

對象輸入輸出流

如果希望將Java對象寫入到IO流中,或從IO流中讀取Java對象,則要使用對象輸入輸出流,稱爲對象的序列化和反序列化

4.1 序列化和反序列化
序列化:將Java對象寫入IO流中,實現將對象保存在磁盤上或在網絡中傳遞對象
反序列化:從IO流中讀取Java對象,實現從磁盤上或網絡中恢復對象
    要求:
    對象必須實現Serializable接口,才能被序列化,轉換爲二進制流,通過網絡進行傳輸
    通過 serialVersionUID 判斷對象的序列化版本的一致性:
        在反序列時,會將流中的serialVersionUID與本地相應實體對象/類的serialVersionUID進行比較
            如果相同就認爲版本一致,則可以進行反序列化
            如果不相同,則會出現序列化版本不一致的異常InvalidClassException

4.2 ObjectInputStream
對象輸入流:用來讀取對象,即反序列化
ObjectInputStream 和 ObjectOutputStream 屬於包裝流(用於對節點流進行功能擴展 / 包裝)
在創建包裝流,需要傳入要操作的節點流對象
當關閉流時,只需要關閉包裝流,被包裝的節點流也會被關閉

4.3 ObjectOutputStream
對象輸出流:用來寫入對象,即序列化

User.java

import java.io.Serializable;

public class User implements Serializable {
    // static final long serialVersionUID
    // 根據類名、接口名、成員方法和屬性等生成的一個64位的哈希值
    // 表示對象類型的的唯一標識
    private static final long serialVersionUID = 705661196096434175L;
    private Integer id; // 都使用包裝類型,默認爲null
    private String name;
    private transient Integer age;  // transient修飾的屬性不會被序列化(static變量也不會被序列化)
    private Address address; // 對象屬性也必須實現Serializable接口
    public User(Integer id, String name, Integer age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public Address getAddress() {
        return address;
    }
    public void setAddress(Address address) {
        this.address = address;
    }
}

Address.java

import java.io.Serializable;

public class Address implements Serializable {
    private String province;
    private String city;
    public Address() {
        super();
    }
    public Address(String province, String city) {
        super();
        this.province = province;
        this.city = city;
    }
    public String getProvince() {
        return province;
    }
    public void setProvince(String province) {
        this.province = province;
    }
    public String getCity() {
        return city;
    }
    public void setCity(String city) {
        this.city = city;
    }
    @Override
    public String toString() {
        return "Address [province=" + province + ", city=" + city + "]";
    }
}

Test.java

import java.io.*;
import java.util.Arrays;
import java.util.List;

public class Test01 {
    public static void main(String[] args) {
        test01();
        test02();
    }
    // 序列化,寫入對象
    public static void test01() {
        User u1 = new User(1001, "tom", 18);
        u1.setAddress(new Address("江蘇", "南京"));

        User u2 = new User(1002, "jack", 21);
        u1.setAddress(new Address("江蘇", "揚州"));

        List<User> users = Arrays.asList(u1, u2);
        ObjectOutputStream oos = null;
        try {
            // FileOutputStream fos = new FileOutputStream("user.data");
            // oos = new ObjectOutputStream(fos);
            //輸出到user.data文件中,是二進制文件,文本編輯器打開不能正常顯示
            oos = new ObjectOutputStream(new FileOutputStream("user.data"));
            
            // 寫入對象
            // oos.writeObject(u1);
            // oos.writeObject(u2);
            oos.writeObject(users);
            oos.flush();//這裏不flush也行,後面有close
            System.out.println("寫入對象成功");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (oos != null) {
                try {
                    oos.close(); // 只需要關閉包裝流
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    // 反序列化,讀取對象
    public static void test02() {
        ObjectInputStream ois = null;
        try {
            ois = new ObjectInputStream(new FileInputStream("user.data"));
            // 讀取順序和寫入順序一致
            // User u1 = (User) ois.readObject();
            // User u2 = (User) ois.readObject();
            // System.out.println(u1);
            // System.out.println(u2);
            List<User> list = (List<User>) ois.readObject();
            System.out.println(list);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (ois != null) {
                try {
                    ois.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

文件的拆分和合並

import java.io.*;

public class Test01 {
    public static void main(String[] args) {
        // 路徑不包含bak的話報錯,不知爲何,總之不能在根目錄下操作
        splitFile("C:\\bak\\oss.zip");
        mergeFile("C:\\bak\\oss.zip_1");
    }
    // 拆分文件:一個輸入流,多個輸出流
    public static void splitFile(String filePath) {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            fis = new FileInputStream(filePath);
            byte[] buffer = new byte[1024 * 1024 * 10]; // 每次讀取10M
            int num = -1;
            int index = 0;
            while ((num = fis.read(buffer)) != -1) {
                fos = new FileOutputStream(filePath + "_" + (++index));
                fos.write(buffer, 0, num);
                fos.flush();
                fos.close();
            }
            System.out.println("拆分成功,共拆分爲:" + index + "個");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    // 合併文件:一個輸出流,多個輸入流
    public static void mergeFile(String filePath) { //filePath只需要一個文件名,通過截取即可
        String basePath = filePath.substring(0, filePath.lastIndexOf("_"));
        FileOutputStream fos = null;
        FileInputStream fis = null;
        try {
            fos = new FileOutputStream(basePath);
            int index = 1;
            File f = null;
            while ((f = new File(basePath + "_" + index++)).exists()) {
                fis = new FileInputStream(f);
                byte[] buffer = new byte[fis.available()];
                fis.read(buffer);
                fos.write(buffer);
                fos.flush();
                fis.close();
            }
            System.out.println("合併成功:"+basePath);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

文件字符流

import java.io.*;

public class Test01 {
    public static void main(String[] args) {
        /*
        * FileReader、FileWriter的用法與FileInputStream、FileOutputStream類似
        * 前者是以字符爲單位,後者是以字節爲單位
        */
        try(
            Reader reader=new FileReader("User.java"); // 字符流無法讀寫二進制文件,
            Writer writer = new FileWriter("User2.java")
        ){
            //int data = reader.read(); // 每次讀取一個字符,返回int類型的字符值
            //System.out.println((char)data);
            
            char[] buffer=new char[5];
            int num = -1;
            while((num=reader.read(buffer))!=-1){
                writer.write(buffer, 0, num);
            }
            System.out.println("複製文件成功");
        }catch(IOException e){
            e.printStackTrace();
        }
    }
}

字符緩衝輸入輸出流 打印流

包裝流 BufferedReader PrintWriter

import java.io.*;

public class Test01 {
    public static void main(String[] args) {
        try(
            BufferedReader reader = new BufferedReader( new FileReader("a.txt") );
            //BufferedWriter writer = new BufferedWriter(new FileWriter("aaaa.txt"));
            PrintWriter writer = new PrintWriter("aaaa.txt");   
        ){
            //String data = reader.readLine(); // 每次讀取一次,讀不到數據時返回null
            //System.out.println(data);
            String data = null;
            while((data = reader.readLine()) != null){
                //writer.write(data);
                //writer.newLine(); //插入換行符
                //writer.write("\r\n"); // 也可以插入\r\n進行換行
                writer.println(data); // 寫入並換行,更方便
            }
            System.out.println("複製文件成功");
        }catch(IOException e){
            e.printStackTrace();
        }
    }
}

轉換流

  • 用於將字節流轉換爲字符流,同時可以實現編碼的轉換
  • 在轉換時需要指定使用的字符集,如果不指定默認使用JVM的字符集
  • 在Java中沒有提供將字符流轉換爲字節流的方法,不支持該操作

InputStreamReader
將字節輸入流轉換爲字符輸入流(InputStream - Reader)
OutputStreamWriter
將字節輸出流轉換字符輸出流(OutputStream - Writer)

public class Test03_轉換流 {
    public static void main(String[] args) {
        try(
//          FileInputStream fis = new FileInputStream("a.txt");
//          InputStreamReader isr = new InputStreamReader(fis, "gbk");
//          BufferedReader reader=new BufferedReader(isr);  
            
            BufferedReader reader=new BufferedReader(
                    new InputStreamReader( new FileInputStream("b.txt") ,"gbk") // 以gbk編碼讀取文件
            );  
                
            // BufferedReader reader = new BufferedReader(new InputStreamReader
            // (Test03_轉換流.class.getClassLoader().getResourceAsStream("b.txt")));
                
//          BufferedWriter writer=new BufferedWriter(
//                  new OutputStreamWriter(
//                          new FileOutputStream("c.txt")
//                  ,"utf-8")  // 以utf-8編碼寫入文件
//          );  
            PrintWriter writer = new PrintWriter("c.txt", "utf-8"); // 這個最強大
        ){
//          System.out.println(reader.readLine());
            writer.write(reader.readLine());
            System.out.println("複製文件成功");
        }catch(IOException e){
            e.printStackTrace();
            
        }
    }
}

RandomAccessFile

隨機讀寫流,是一個字節流,可以對文件進行隨機讀寫

  • 隨機:可以定位到文件的任意位置進行讀寫操作,通過移動指針(Pointer)來實現
  • 讀寫:使用該流既能讀取文件,也能寫入文件
public class Test {
    public static void main(String[] args) {
        try(
            /*
             * 當文件不存在時:
             *      如果模式爲r,會報異常FileNotFoundException
             *      如果模式爲rw,會自動創建文件
             */
            RandomAccessFile raf=new RandomAccessFile("x.txt", "rw"); //模式:r只讀、rw讀寫
        ){
            System.out.println(raf.getFilePointer()); // 獲取當前指針的位置,從0開始
            
            raf.write("張三".getBytes()); //對於utf-8,一個漢字佔3個字節
            raf.write("hello".getBytes());
            System.out.println(raf.getFilePointer()); // 11
            
            System.out.println("寫入成功");
            
            raf.seek(8); // 將指針移動到指定的位置
            raf.write("李四".getBytes());
            System.out.println(raf.getFilePointer()); // 14
            
            raf.seek(6);
            byte[] buffer = new byte[2];
            raf.read(buffer);
            System.out.println(new String(buffer));
            System.out.println(raf.getFilePointer()); // 8
            
            raf.skipBytes(3); // 將指針向後跳過指定的字節,只能往前,不能倒退 ——>
            buffer = new byte[1024*1024];
            int num = -1;
            while((num = raf.read(buffer)) != -1){
                System.out.println(new String(buffer,0,num));
            }
            
            // 修改數據
            raf.seek(8);
            raf.write("趙".getBytes());
            System.out.println("修改成功");
        }catch(IOException e){
            e.printStackTrace();
        }
    }
}

文件的加密

* 實現文件的加密
 *  a.txt ——> a.txt.sec
 * 
 * 思路:讀取文件中的每個字節,與pwd進行異或的操作
 */

import java.io.*;
import java.util.Scanner;

public class Test01 {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        System.out.print("請輸入文件路徑:");
        String filePath=input.nextLine();
        System.out.print("請輸入密碼:");
        int pwd = input.nextInt();
        secret(filePath, pwd);
        System.out.println("加密成功");
    }
    public static void secret(String filePath, int pwd){
        try(
            FileInputStream fis=new FileInputStream(filePath);
            FileOutputStream fos = new FileOutputStream(filePath+".sec");
        ){
            int data=-1;
            while((data=fis.read())!=-1){
                fos.write(data ^ pwd);
            }
        }catch(IOException e){
            e.printStackTrace();
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章