快速Get-JAVA-IO流

第四階段 IO

IO流

前言:

前面的學習咱們只可以在已有的必定封閉範圍內進行一些操做,可是這顯然是無趣的,也是不支持咱們實現一些複雜的需求,因此Java提供IO流這樣一種概念,方便咱們對數據進行操做

而使用IO流咱們能夠實現一些強大的功能,例如針對文件的移動複製等操做,又或者程序與外部文件之間的數據存儲或者讀取,又或者實現一個實時的聊天程序(網絡編程),其中數據的傳輸也用到了咱們的IO流,這些內容咱們都會在後面設計,下面我就開始IO流的正式學習java

(一) IO流的概述及分類

(1) 概念

IO 即 input/output(輸入/輸出),流的概念仍是有一些陌生的linux

「流」從字面看來就是相似水流的概念,其具備方向性,流動性,連續性、而且能夠承載一些事物,而在咱們計算機中,「流」是對一種有序連續具備方向性的數據的抽象描述。其本質就是數據的傳輸,而根據其特色將其抽象封裝爲各類類,更加方便了用戶的操做編程

(2) 分類

A:流向windows

  • 輸入流——讀取數據
  • 輸出流——寫出數據

B:數據類型設計模式

  • 字節流數組

    • 字節輸入流——InputStream
    • 字節輸出流——OutputStream
  • 字符流網絡

    • 字符輸入流——Reader
    • 字符輸出流——Writer

注意:app

a: 若是咱們沒有明確說明按照什麼分,默認按照數據類型分。框架

b: 除非文件用windows自帶的記事本打開咱們可以讀懂,才採用字符流,不然建議使用字節流。dom

(二) 字節流

(1) FileOutputStream 寫出數據

A:操做步驟

  • 建立字節輸出流對象
  • 調用writer()方法
  • 釋放資源

B:代碼體現

FileOutputStream fos = new FileOutputStream("fos.txt");
for.write("hello".getBytes());
fos.close;

換行操做

由於不一樣的系統針對不一樣的換行符號識別是不同的

windows:rn linux:n Mac:r

而一些常見的高級記事本是能夠識別任意換行符號的

如何實現數據的追加寫入 ?

用構造方法帶第二個參數是true的狀況便可

FileOutputStream fos = new FileOutputStream("fos.txt", true);

(2) FileInputStream 讀取數據

A:操做步驟

  • 建立字節輸入流對象
  • 調用writer()方法
  • 釋放資源

B:代碼體現

FileInputStream fis = new FileInputStream("fos.txt");
//使用FileInputStream對指定路徑下內容進行讀取,能夠結合FileOutputStream實現對文件的操做
import java.io.FileInputStream;
import java.io.IOException;

public class FileInputStreamDemo {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("F:\\fos.txt");

        //方式一
        int by = 0;
        while ((by = fis.read()) != -1){
            System.out.print((char)by);
        }

        //方式二(這種方式更加快,推薦)
        //數組長度通常是1024或者1024的整數倍
        byte[] bys = new byte[1024];
        int len = 0;
        while((len = fis.read(bys))!=-1){
            System.out.print(new String(bys,0,len));
        }
        //釋放資源
        fis.close();
    }
}

(3) 字節緩衝流

//統計這段程序運行時間

long start = System.currentTimeMillis();
//受測試代碼
long end = System.currentTimeMillis();
System.out.println("共耗時" + (end - start) + "毫秒");

字節流一次讀寫一個數組的速度明顯比一次讀寫一個字節的速度快不少,這是加入了數組這樣的緩衝區效果,java自己在設計的時候,也考慮到了這樣的設計思想(裝飾設計模式後面講解),因此提供了字節緩衝區流

//字節緩衝輸出流
BuffereOutputStream
//字節緩衝輸入流
BufferedInputStream
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class BufferedOutputStreamDemo {
    public static void main(String[] args) throws IOException {
//        FileOutputStream fos = new FileOutputStream("F:\\fos.txt");
//        BufferedOutputStream bos = new BufferedOutputStream(fos);
        
        //簡單寫法
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("F:\\fos.txt"));
        
        //寫數據
        bos.write("hello".getBytes());
        //釋放資源,注意不須要fos.close
        bos.close();
    }
}

爲何不傳遞一個具體的文件或者文件路徑,而是傳遞一個OutputStream對象呢?

緣由很簡單,字節緩衝區流僅僅提供緩衝區,爲高效而設計的。可是呢,真正的讀寫操做還得靠基本的流對象實現。

import java.io.*;

public class Test {
    public static void main(String[] args) throws IOException {
        long start = System.currentTimeMillis();

        method1("E:\\夜曲.mp3", "F:\\Test1.mp3");
        method2("E:\\夜曲.mp3", "F:\\Test2.mp3");
        method3("E:\\夜曲.mp3", "F:\\Test3.mp3");
        method4("E:\\夜曲.mp3", "F:\\Test4.mp3");
        long end = System.currentTimeMillis();
        System.out.println("共耗時" + (end - start) + "毫秒");
    }

    //基本字節流一次讀寫一個字符
    public static void method1(String srcString, String deskString) throws IOException {
        FileInputStream fis = new FileInputStream(srcString);
        FileOutputStream fos = new FileOutputStream(deskString);
        int by = 0;
        while ((by = fis.read()) != -1) {
            fos.write(by);
        }
        fis.close();
        fos.close();
    }

    //基本字節流一次讀寫一個字節數組
    public static void method2(String srcString, String deskString) throws IOException {
        FileInputStream fis = new FileInputStream(srcString);
        FileOutputStream fos = new FileOutputStream(deskString);
        byte[] bys = new byte[1024];
        int len = 0;
        while ((len = fis.read(bys)) != -1) {
            fos.write(bys, 0, len);
        }
        fis.close();
        fos.close();
    }

    //高效字節流一次讀寫一個字節
    public static void method3(String srcString, String deskString) throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcString));
        BufferedOutputStream bos = new 
            BufferedOutputStream(new FileOutputStream(deskString));
        int by = 0;
        while ((by = bis.read()) != -1) {
            bos.write(by);
        }
        bis.close();
        bos.close();
    }

    //高效字節流一次讀寫一個字節數組
    public static void method4(String srcString, String deskString) throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcString));
        BufferedOutputStream bos = new 
            BufferedOutputStream(new FileOutputStream(deskString));
        byte[] bys = new byte[1024];
        int len = 0;
        while ((len = bis.read(bys)) != -1) {
            bos.write(bys, 0, len);
        }
    }
}


//運行結果
共耗時125961毫秒
共耗時143毫秒
共耗時1356毫秒
共耗時29毫秒

因而可知在上述四種方式中,效率最高的仍是最後一種——高效字節流一次讀寫一個字節數組!

(三) 字符流

咱們在開發中,若是想要對咱們所能識別的文本內容進行數據的傳輸,若是咱們繼續使用咱們上面所學習的字節流,咱們就會發現顯示出來的內容是亂碼,這是由於編碼出現了問題,而這個時候咱們就會用到咱們的字符流,咱們能夠先簡單的認識 字符流 = 字節流 + 編碼

(1) 編碼解碼

編碼 說明
ASCII 美國標準信息交換碼,用一個字節的7位能夠表示。
ISO8859-1 拉丁碼錶。歐洲碼錶,用一個字節的8位表示。
GB2312 中國的中文編碼表。
GBK 中國的中文編碼表升級,融合了更多的中文文字符號。
GB18030 GBK的取代版本
BIG-5碼 行於臺灣、香港地區的一個繁體字編碼方案,俗稱「大五碼」。
Unicode 國際標準碼,融合了多種文字, 有文字都用兩個字節來表示,Java語言使用的就是unicode
UTF-8 最多用三個字節來表示一個字符。UTF-8不一樣,它定義了一種「區間規則」,這種規則能夠和ASCII編碼保持最大程度的兼容:它將Unicode編碼爲00000000-0000007F的字符,用單個字節來表示它將Unicode編碼爲00000080-000007FF的字符用兩個字節表示 它將Unicode編碼爲00000800-0000FFFF的字符用3字節表示

字節流咱們前面已經有了必定的認識,那麼什麼是編碼和解碼呢?

編碼是信息從一種形式或格式轉換爲另外一種形式的過程;解碼則是編碼的逆過程。

咱們先經過一個例子來了解一下它的流程

//經過指定的字符集解碼字節數組
String(byte[] bytes, String charsetName)
//使用指定的字符集合把字符串編碼爲字節數組
byte[] getBytes(String charsetName)
import java.io.UnsupportedEncodingException;
import java.util.Arrays;

public class EncodingDemo {
    public static void main(String[] args) throws UnsupportedEncodingException {
        String s = "理想";

        //String - byte[] - 編碼
        byte[] bys = s.getBytes();  //[-25, -112, -122, -26, -125, -77]
//        byte[] bys = s.getBytes("UTF-8");   //[-25, -112, -122, -26, -125, -77]
//        byte[] bys = s.getBytes("GBK"); //[-64, -19, -49, -21]
        System.out.println(Arrays.toString(bys));
        
        //byte[] - String - 解碼
        String string = new String(bys);  //理想
//        String string = new String(bys,"UTF-8");    //理想
//        String string = new String(bys,"GBK");    //鐞嗘兂
        System.out.println(string);
    }
}

發送過程:「理想」 —— 數字 —— 二進制 —— 發送

接收過程:接收 —— 二進制 —— 十進制 —— 數值 —— 字符 —— 「理想」

其實簡單的總結就是:

編碼:把看得懂的變成看不懂的

解碼:把看不懂的變成看得懂的

(2) 字符輸入輸出流

OutputStreamWriter 字符輸出流(寫出)

public OutputStreamWriter(OutputStream out)
public OutputStreamWriter(OutputStream out,String charsetName)

InputStreamReader 字符輸入流(讀取)

public InputStreamReader(InputStream in)
public InputStreamReader(InputStream in,String charsetName)

OutputStreamWriter寫數據方法

//寫一個字符
public void write(int c)

//寫一個字符數組
public void write(char[] cbuf)

//寫一個字符數組的一部分
public void write(char[] cbuf,int off,int len)

//寫一個字符串
public void write(String str)

//寫一個字符串的一部分
public void write(String str,int off,int len)

OutputStreamWriter讀數據方法

//讀一個字符
public int read()

//第一個字符數組
public int read(char[] cbuf)

字符流操做要注意的問題

flush()和close()的區別 ?

close:關閉流對象,可是先刷新一次緩衝區,關閉以後,流對象就不能繼續使用了

flush:僅僅刷新緩衝區,刷新以後,流對象還能夠繼續使用

(2) 字符流的簡單寫法

轉換流的名字比較長,而咱們常見的操做都是按照本地默認編碼實現的,因此,爲了簡化咱們的書寫,轉換流提供了對應的子類

//輸出流
FileWriter
//輸入流
FileReader
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("b.txt"))
//等價
FileWriter fw = new FileWriter("b.txt"); (寫出)

InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"))
//等價 
FileReader fr = new FileReader("a.txt"); (讀取)

(3) 字符緩衝流

 BufferedWriter:字符緩衝輸出流

將文本寫入字符輸出流,緩衝各個字符,從而提供單個字符、數組和字符串的高效寫入

能夠指定緩衝區的大小,或者接受默認的大小。在大多數狀況下,默認值就足夠大了。

BufferedReader:字符緩衝輸入流

從字符輸入流中讀取文本,緩衝各個字符,從而實現字符、數組和行的高效讀取

能夠指定緩衝區的大小,或者可以使用默認的大小。大多數狀況下,默認值就足夠大了。

特殊功能

BufferedWriter:

//根據系統來決定換行符
public void newLine()

BufferedReader:

//一次讀取一行數據,包含該行內容的字符串,不包含任何行終止符,若是已到達流末尾,則返回 null
public String readLine()

(四) IO流案例

字節流案例

案例一:複製單級文件夾

import java.io.*;

/*
 * 需求:複製單級文件夾
 *
 * 數據源:f:\\demo
 * 目的地:f:\\test
 *
 * 分析:
 *         A:封裝目錄
 *         B:獲取該目錄下的全部文本的File數組
 *         C:遍歷該File數組,獲得每個File對象
 *         D:把該File進行復制
 */


public class CopyFolderDemo {
    public static void main(String[] args) throws IOException {
        File srcFloder = new File("F:\\demo");
        File deskFloder = new File("F:\\test");

        if (!deskFloder.exists()) {
            deskFloder.mkdirs();
        }

        File[] fileArray = srcFloder.listFiles();
        for (File file : fileArray) {
            String name = file.getName();
            //拼湊出每個文件的路徑
            File newFile = new File(deskFloder, name);
            copyFloder(file, newFile);
        }
    }

    public static void copyFloder(File file, File newFile) throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
        BufferedOutputStream bos = 
            new BufferedOutputStream(new FileOutputStream(newFile));

        byte[] bys = new byte[1024];
        int len = 0;
        while ((len = bis.read()) != -1) {
            bos.write(bys, 0, len);
        }

        bis.close();
        bos.close();

    }
}

案例二:複製指定目錄下的指定文件,並修改後綴名

import java.io.*;

/*
 * 需求:複製指定目錄下的指定文件,並修改後綴名。
 * 指定的文件是:.txt文件。
 * 指定的後綴名是:.bat
 * 指定的目錄是:test
 *
 * 數據源:f:\\demo\\A.txt
 * 目的地:f:\test\\A.bat
 *
 * 分析:
 *         A:封裝目錄
 *         B:獲取該目錄下的java文件的File數組
 *         C:遍歷該File數組,獲得每個File對象
 *         D:把該File進行復制
 *         E:在目的地目錄下更名
 */
public class CopyFolderDemo2 {
    public static void main(String[] args) throws IOException {
        File srcFloder = new File("F:\\demo");
        File destFloder = new File("F:\\test");

        if (!destFloder.exists()) {
            destFloder.mkdirs();
        }

        File[] fileArray = srcFloder.listFiles(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                return new File(dir, name).isFile() && name.endsWith(".txt");
            }
        });


        for (File file : fileArray) {
            String name = file.getName();
            File newFile = new File(destFloder, name);
            copyFile(file, newFile);
        }

        File[] deskFileArray = destFloder.listFiles();
        for (File destFile : deskFileArray) {
            String name = destFile.getName();
            String newName = name.replace(".txt", ".bat");

            File newFile = new File(destFloder, newName);
            destFile.renameTo(newFile);
        }
    }

    public static void copyFile(File file, File newFile) throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
        BufferedOutputStream bos = 
            new BufferedOutputStream(new FileOutputStream(newFile));

        byte[] bys = new byte[1024];
        int len = 0;
        while ((len = bis.read(bys)) != -1) {
            bos.write(bys, 0, len);
        }
        bis.close();
        bos.close();

    }
}

案例三:複製多級文件夾

import java.io.*;

/*
 * 需求:複製多極文件夾
 *
 * 數據源:F:\\admin
 * 目的地:E:\\
 *
 * 分析:
 *         A:封裝數據源File
 *         B:封裝目的地File
 *         C:判斷該File是文件夾仍是文件
 *             a:是文件夾
 *                 就在目的地目錄下建立該文件夾
 *                 獲取該File對象下的全部文件或者文件夾File對象
 *                 遍歷獲得每個File對象
 *                 回到C
 *             b:是文件
 *                 就複製(字節流)
 */
public class CopyFloderDemo3 {
    public static void main(String[] args) throws IOException {
        File srcFile = new File("F:\\admin");
        File destFile = new File("E:\\");
        copyFolder(srcFile, destFile);
    }


    private static void copyFolder(File srcFile, File destFile) throws IOException {
        if (srcFile.isDirectory()) {
            File newFolder = new File(destFile, srcFile.getName());
            newFolder.mkdirs();

            //獲取該File對象下的全部文件或者文件夾File對象
            File[] fileArray = srcFile.listFiles();
            for (File file : fileArray) {
                //遞歸,繼續判斷
                copyFolder(file, newFolder);
            }

        } else {
            File newFile = new File(destFile, srcFile.getName());
            copyFile(srcFile, newFile);
        }
    }

    private static void copyFile(File srcFile, File newFile) throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
        BufferedOutputStream bos = 
            new BufferedOutputStream(new FileOutputStream(newFile));

        byte[] bys = new byte[1024];
        int len = 0;
        while ((len = bis.read(bys)) != -1) {
            bos.write(bys, 0, len);
        }

        bos.close();
        bis.close();
    }
}

字符流案例

案例一:隨機獲取文本文件中的姓名案例

import java.io.*;
import java.util.ArrayList;
import java.util.Random;

/*
 * 隨機獲取文本文件中的姓名案例
 *      需求:我有一個文本文件中存儲了幾個名稱
 *      請你們寫一個程序實現隨機獲取一我的的名字。
 *
 * 分析:
 *        A:把文本文件中的數據存儲到集合中
 *        B:隨機產生一個索引
 *        C:根據該索引獲取一個值
 */
public class GetRandName {
    public static void main(String[] args) throws IOException {
        String path = "F:\\test.txt";
//        BufferedReader br = new BufferedReader(new FileReader(path));
        //默認記事本以ansi編碼保存,可是使用FileReader默認使用UTF-8輸出,因此使用上面語句會亂碼
        BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(path), "gb2312"));

        ArrayList<String> array = new ArrayList<>();
        String line = null;
        while ((line = br.readLine()) != null) {
            array.add(line);
        }
        br.close();

        Random r = new Random();
        int index = r.nextInt(array.size());

        String name = array.get(index);
        System.out.println("該幸運兒是:" + name);
    }
}

案例二:鍵盤錄入學生信息按照總分排序並寫入文本文件案例

//Student類自行補充
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Comparator;
import java.util.Scanner;
import java.util.TreeSet;

public class StudentDemo {
    public static void main(String[] args) throws IOException {
        TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student s2) {
                int num = s2.getSum() - s1.getSum();
                int num2 = num == 0 ? s1.getChinese() - s2.getChinese() : num;
                int num3 = num2 == 0 ? s1.getMath() - s2.getMath() : num2;
                int num4 = num3 == 0 ? s1.getEnglish() - s2.getEnglish() : num3;
                int num5 = num4 == 0 ? s1.getName().compareTo(s2.getName()) : num4;
                return num5;
            }
        });

        for (int x = 1; x <= 3; x++) {
            Scanner sc = new Scanner(System.in);
            System.out.println("請輸入第" + x + "個學生成績信息");
            System.out.println("姓名:");
            String name = sc.nextLine();
            System.out.println("語文成績:");
            int chinese = sc.nextInt();
            System.out.println("數學成績:");
            int math = sc.nextInt();
            System.out.println("英語成績:");
            int english = sc.nextInt();

            Student s = new Student();
            s.setName(name);
            s.setChinese(chinese);
            s.setMath(math);
            s.setEnglish(english);
            ts.add(s);

            BufferedWriter bw = new BufferedWriter(new FileWriter("F:\\students.txt"));
            bw.write("學生成績信息以下");

            bw.newLine();
            bw.flush();
            bw.write("姓名-語文成績-數學成績-英語成績");
            bw.newLine();
            bw.flush();
            for (Student stu : ts) {
                StringBuilder sb = new StringBuilder();
                sb.append(stu.getName() + "-" + stu.getChinese() + "-" + stu.getMath() + "-" + stu.getEnglish());
                bw.write(sb.toString());
                bw.newLine();
                bw.flush();
            }

            bw.close();
            System.out.println("學生成績信息錄入完畢");
        }
    }
}

案例三:登錄註冊案例(使用IO)

在前幾篇中集合實現的基礎上,其他文件不變,只須要對 UserDaoImpl.java 文件進行重寫

因爲篇幅較長,其他dao、pojo、test層代碼請翻閱前幾篇中 集合框架——List篇

package cn.bwh_05_LoginDemo.dao.impl;

import cn.bwh_05_LoginDemo.dao.UserDao;
import cn.bwh_05_LoginDemo.pojo.User;

import java.io.*;

/**
 * 這是用戶操做的具體實現類 (IO)
 *
 * @author BWH_Steven
 * @version v1.1
 */
public class UserDaoImpl implements UserDao {
    private static File file = new File("User.txt");

    static {
        try {
            file.createNewFile();
        } catch (IOException e) {
            System.out.println("建立文件失敗");
            e.printStackTrace();
        }
    }

    @Override
    public boolean isLogin(String username, String password) {
        boolean flag = false;
        BufferedReader br = null;
        String path = "user.txt";

        try {
            br = new BufferedReader(new FileReader(file));
            String line = null;
            while ((line = br.readLine()) != null) {
                //用戶名--密碼
                String[] datas = line.split("--");
                if (datas[0].equals(username) && datas[1].equals(password)) {
                    flag = true;
                    break;
                }
            }
        } catch (FileNotFoundException e) {
            System.out.println("找不到登陸所須要的信息文件");
            e.printStackTrace();
        } catch (IOException e) {
            System.out.println("用戶登陸失敗");
            e.printStackTrace();
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    System.out.println("用戶登陸釋放資源失敗");
                    e.printStackTrace();
                }
            }
        }
        return flag;
    }

    @Override
    public void regist(User user) {
        /*
         *  爲註冊的數據定義一個規則: 用戶名--密碼
         */
        BufferedWriter bw = null;
        String path = "user.txt";
        try {
            //爲了保證數據是追加寫入,因此必須加true
            bw = new BufferedWriter(new FileWriter(file, true));
            //bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(path,true),"gb2312"));
            bw.write(user.getUsername() + "--" + user.getPassword());
            bw.newLine();
            bw.flush();
        } catch (IOException e) {
            System.out.println("用戶註冊失敗");
            e.printStackTrace();
        } finally {
            try {
                bw.close();
            } catch (IOException e) {
                System.out.println("用戶註冊釋放資源失敗");
                e.printStackTrace();
            }
        }
    }

}

結尾:

若是內容中有什麼不足,或者錯誤的地方,歡迎你們給我留言提出意見, 蟹蟹你們 !^_^

若是能幫到你的話,那就來關注我吧!(系列文章均會在公衆號第一時間更新)

在這裏的咱們素不相識,卻都在爲了本身的夢而努力 ❤

一個堅持推送原創Java技術的公衆號:理想二旬不止

相關文章
相關標籤/搜索