日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

IO流之字節流與常見編碼

作者:草原灰太狼666 更新時間: 2022-07-13 編程語言

IO流

1.流的概念和分類

  • IO流核心組成
    • 核心組成:一個類(File)、一個接口(Serializable)、四個抽象類(InputStream/OutputStream、Reader/Writer)

思維

  • 什么是流

    • 流:內存與存儲設備之間傳輸數據的通道
  • 流的分類

  • 按方向

    • 輸入流:將<存儲設備>中的內容讀到<內存>中
    • 輸出流:將<內存>中的內容寫到<存儲設備>中
  • 按單位

    • 字節流:以字節為單位,可以讀寫所有數據
    • 字符流:以字符為單位,只能讀寫文本數據
  • 按功能

    • 節點流:具有實際傳輸數據的讀寫功能
    • 過濾流:在節點流的基礎之上增強功能

2.字節流

2.1字節流抽象類

  • InputStream

    • 此抽象類是表示字節輸入流的所有類的父類。InputSteam是一個抽象類,它不可以實例化。數據的讀取需要由它的子類來實現。根據節點的不同,它派生了不同的節點流子類。
    • 繼承自InputSteam的流都是用于向程序中輸入數據,且數據的單位為字節(8 bit)
    • 常用方法:

      public int read(){}:讀取一個字節的數據,并將字節的值作為int類型返回(0-255之間的一個值)。如果未讀出字節則返回-1(返回值為-1表示讀取結束)。while((data=fis.read())!=-1)來判斷讀取文件是否結束。
      public int read(byte[] b){} :從該輸入流讀取最多 b.length個字節的數據,并把讀取的數據存放到b這個字符數組里面
      public int read(byte[] b, int off, int len){}
  • OutputStream

    • 此抽象類是表示字節輸出流的所有類的父類。輸出流接收輸出字節并將這些字節發送到某個目的地
      public void write(int n):向目的地中寫入一個字節
      public void write(byte[] b){} : 將 b.length個字節從指定的字節數組寫入此文件輸出流。
      public void write(byte[] b, int off, int len){}

2.2文件字節流

  • FileInputStream:

    • public int read(byte[] b) // 從流中讀取多個字節,將讀到內容存入 b 數組,返回實際讀到的字節數;如果達到文件的尾部,則返回 -1
  • FileOutputStream:

    • public void write(byte[] b) // 一次寫多個字節,將 b 數組中所有字節,寫入輸出流

2.21 文件字節輸入流代碼演示

package stream.demo01;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/*
    FileInputStream 使用
    文件字節輸入流

*/

public class FileInputStreamTest {
    public static void main(String[] args) throws IOException {
        // 1 創建FileInputStream 并指定文件路徑
        FileInputStream fileInputStream = new FileInputStream("D:\\atext.txt");

        // 2 讀取文件
        /*
         // 方式一:單字節讀取
        int date=0;
        //int d1 = fileInputStream.read();  不可以在這里賦值給d1,不然就只是讀了一個字節
        //必須加括號,不然不知道執行誰
        while ((date=fileInputStream.read())!=-1){
            System.out.print((char) date);
        }
        */
        // 方式二:一次讀取多個字節(3)
        byte[] array=new byte[3];// 大小為3的緩存區
        /*
        int count = fileInputStream.read(array);
        //System.out.println((new String(array)));// 一次讀3個
        String  s  = new String(array);
        System.out.println(s);

        int count2 = fileInputStream.read(array);
        System.out.println(new String(array));// 再讀3個

        int count3=fileInputStream.read(array);
        System.out.println(new String(array));
*/

        // 上述優化后
        int count=0;
        //返回讀取到了多少個元素,如果沒有讀取到返回-1
        while ((count=fileInputStream.read(array))!=-1){
            System.out.println(new String(array,0,count)); // new String(bytes, 0, count) 轉換字符串從bytes的0下標開始,讀count個字符串

        }
        // 3 關閉
        fileInputStream.close();
        System.out.println();
        System.out.println("執行完畢");

    }
}



運行結果

abc
def
gha
bcd
efg
hab
cde
fga
bcd
efg
hab
cde
fgh
abc
def
ghh
abc
def
gha
bcd
efg
h

執行完畢


2.22 文件字節輸出流代碼演示

package stream.demo01;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/*
   文件輸出流的使用
    FileOutputStream

*/

public class FileOutputStreamTest {
    public static void main(String[] args) throws IOException {
        // 1 創建文件字節輸出流
        FileOutputStream fileOutputStream = new FileOutputStream("D:\\fff.txt",true);// true表示不覆蓋 接著寫
        /*
        // 2 寫入文件
        fileOutputStream.write(97);
        fileOutputStream.write('b');
        fileOutputStream.write('c');
        fileOutputStream.write('d');
        fileOutputStream.write('e');
        */

        byte[] bytes = new byte[10];
        String st="hello world";
        fileOutputStream.write(st.getBytes());//getBytes():獲取字節數據
        //getBytes(String charsetName):使用指定的字符集將字符串編碼為byte序列,并將結果存儲到一個新的byte數組中


        // 3 關閉
        fileOutputStream.close();
        System.out.println("復制完成");
    }
}



運行結果

復制完成

fff


2.23 文件字節流復制文件代碼演示

package stream.demo01;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

//使用字節流實現文件的復制
public class StreamCopyTest {
    public static void main(String[] args) throws IOException {
        // 1 創建流
        // 1.1 文件字節輸入流
        FileInputStream fis = new FileInputStream("D:\\a50.jpg");
        // 1.2 文件字節輸出流
        FileOutputStream fos = new FileOutputStream("D:\\b50.jpg");

        // 2 邊讀邊寫
        byte[] bytes = new byte[1024*5];
        int count=0;
        while ((count=fis.read(bytes))!=-1){
            fos.write(bytes,0,count);
        }

        // 3 關閉
        fis.close();
        fos.close();
        System.out.println("執行完畢");
    }
}



運行結果

執行完畢

2.3 字節緩沖流

  • 緩沖流:BufferedInputStream/ BufferedOutputStream
    • 提高IO效率,減少訪問磁盤次數
    • 數據存儲在緩沖區中,flush是將緩沖區的內容寫入文件中,也可以直接close


2.31 字節緩沖輸入流代碼演示:

package stream.demo01;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
// 字節緩沖流輸入流
public class BufferedInputStreamTest {
    public static void main(String[] args) throws Exception {
        // 1 創建BufferedInputStream
        FileInputStream fis = new FileInputStream("D:\\atext.txt");
        BufferedInputStream bis = new BufferedInputStream(fis);// 目的是增強底層流的功能, 先讀到緩沖區
                                                    //創建字節緩沖流對象時需要向其輸入一個底層流
        /*
        // 2 讀取
        int count=0;
        while ((count=bis.read())!=-1){
            System.out.print((char)count);
        }
        */
        // 用自己創建的緩沖流
        byte[] bytes = new byte[1024 * 5];
        int count=0;
        while ((count=bis.read(bytes))!=-1){
            System.out.println(new String(bytes,0,count));
        }
        // 3 關閉
        bis.close();

    }
}


運行結果

abcdefghabcdefghabcdefgabcdefghabcdefghabcdefghhabcdefghabcdefgh

2.32 字節緩沖輸出流代碼演示:

package stream.demo01;


import java.io.*;

// 使用字節緩沖流 寫入 文件
public class BufferedOutputStreamTest {

    public static void main(String[] args) throws IOException {
        // 1 創建BufferedInputStream
        FileOutputStream fis = new FileOutputStream("D:\\buffer.txt");
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fis);
        // 2 寫入文件
        for (int i = 0; i < 10; i++) {// 此時讀到了緩沖區,沒有在文件中
            bufferedOutputStream.write("hello world\r\n".getBytes());//\r\n 轉義字符換行打印

        }
        System.out.println("執行完畢");
        // 3 關閉    bos.flush(); // 刷新到硬盤   close方法里自帶有
        bufferedOutputStream.close();

    }
}

運行結果

執行完畢

jieg


2.4 對象流

  • ObjectInputStream/ObiectOutputStream

    • 增強了緩沖區功能

    • 增強了讀寫 8 種基本數據類型和字符串功能

    • 增強了讀寫對象的功能
      readObject()從流中讀取一個對象
      writeObject(Object obj)向流中寫入一個對象

    • 使用流傳輸對象的過程稱為序列化、反序列化

  • 序列化對象類的書寫注意事項

    • 序列化類必須實現 Serializable 接口
    • 序列化類中的對象屬性要實現 Serializable 接口
    • 序列化版本號ID,保證序列化的類和反序列化的類是同一個類private static final long serialVersionUID = 100L;
    • 使用transient修飾屬性,這個屬性就不能序列化
    • 靜態屬性不能序列化
    • 序列化多個對象,可以借助集合來實現

2.41 序列化代碼演示:

package stream.demo01;

import java.io.Serializable;

public class Student implements Serializable {
    //類要想序列化必須實現Serializable接口
    private String name;
   // private int age;
    private  transient int age;  //Student{name='zhangsan', age=0}
    //使用transient修飾屬性,使屬性不能被序列化

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}


package stream.demo01;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;

// 使用objectoutputStream實現對象的序列化
//注意:在寫對象這個類的時候需要public class Student implements Serializable實現這個接口。
/*
1.某個類要想序列化必須實現Serializable接口
2.序列化類中對象屬性要求實現Serializable接口
3.序列化版本號ID,保證序列化的類和反序列化的類是同一個類
4.使用transient修飾屬性,這個屬性就不能序列化
5.靜態屬性不能序列化
6.序列化多個對象,可以借助集合來實現


*/

public class ObjectOutputStreamTest {
    public static void main(String[] args) throws IOException {
        // 1. 創建對象流
        FileOutputStream fos = new FileOutputStream("D:\\student.bin");//這里文件類型可以隨便寫,但是和反序列化一定要一致
        ObjectOutputStream obj = new ObjectOutputStream(fos);               //bin表示這是一個二進制文件類型
        // 2. 序列化(寫入操作)
        Student s = new Student("zhangsan",18);
        Student lisi = new Student("lisi", 20);

        ArrayList<Student> Arry=new ArrayList<>();
        Arry.add(s);
        Arry.add(lisi);
        obj.writeObject(Arry);

//        obj.writeObject(s);
//        obj.writeObject(lisi);

        // 3. 關閉
        obj.close();
        System.out.println("序列化執行完畢");


    }
}

運行結果

序列化執行完畢



2.42 反序列化代碼演示:

package stream.demo01;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;
import java.util.Objects;

// 使用ObjectInputSteam實現反序列化(讀取重構對象)
//反序列化(重構成對象)
public class ObjectInputStreamTest {
    public static <ArrrayList> void main(String[] args) throws IOException, ClassNotFoundException {
        // 1. 創建對象流
        FileInputStream fis = new FileInputStream("D:\\student.bin");
        ObjectInputStream obs = new ObjectInputStream(fis);
        // 2. 讀取文件(反序列化)
//        Student s = (Student) obs.readObject();  //readObject() 返回類型為Object,因此需要強轉
//        Student o = (Student)obs.readObject();


        ArrayList<Student> objects=(ArrayList<Student>) obs.readObject();

        // 3. 關閉
        obs.close();
        System.out.println("反序列化執行完畢");
//        System.out.println(s.toString());
//        System.out.println(o.toString());

        System.out.println(objects.toString());

    }
}

運行結果

反序列化執行完畢
[Student{name='zhangsan', age=0}, Student{name='lisi', age=0}]


3.編碼方式(常見字符形式)

  • 引言

    • 在計算機中提供給用戶最常見的顯示就是字符,也稱之為文本,字符的種類非常多,每種語言都有自己的字符集,那么,這么多的字符,如何存儲進計算機中呢?
  • 計算機存儲字符的本質原理

    • 每個字符都通過字符集的映射轉化為一個整數存儲在計算機中,所以存儲字符的本質還是存儲整數。
    • 將字符轉為對應碼值,然后將碼值轉換為二進制,最后存到計算機中。

3.1 常用編碼介紹

  • 采用不同的編碼方式,則字符對應的碼值就不同。目前常見的編碼方式有:
    • ASCII碼。固定使用1個字節來表示字符,可以表示128個字符
    • Unicode碼。固定使用2個字節來表示字符(字母和漢字都是)。
    • utf-8。字母用1個字節表示,漢字用3個字節表示。
    • GBK。字母用1個字節表示,漢字用2個字節表示。

3.2 各編碼詳細介紹

  • 英文字符集 —— ASCII

    • 上個世紀60年代,美國制定了一套字符編碼,對英語字符與二進制位之間的關系做了統一規定,這一規定被稱為 ASCII 碼

    • ASCII ((American Standard Code for Information Interchange): 美國信息交換標準代碼

    • ASCII 碼規定使用一個字節來存儲英文字符,最前面的一位統一規定為0,不同的字符由后面的7位確定, 所以ASCII碼一共規定了128個字符的編碼

    • 【優點】只用1個字節表示字符

    • 【缺點】最多只表示127個字符,表示字符數量有限。

  • 全世界的字符集 —— Unicode

    • 全世界的語言非常多,每種語言都有自己的字符集,非常的不方便,并且極其容易出現亂碼,所以Unicode字符集的誕生就是為了將世界上所有的字符都納入其中,形成一種統一的編碼規定。

    • Unicode,統一碼,又叫萬國碼。

    • Unicode只是一個符號集,它只規定了符號的二進制代碼,卻沒有規定這個二進制代碼應該如何存儲

    • 【優點】不會出現亂碼現象

    • 【缺點】固定使用2個字節表示一個字符(包括字母、漢字),比較占用存儲空間。

  • UTF-8編碼

    • UTF-8(8位元,Universal Character Set/Unicode Transformation Format)是針對Unicode的一種可變長度字符編碼。(可以理解為是對Unicode編碼的改進)

    • 它可以用來表示Unicode編碼中的任何字符,而且其編碼中的第一個字節仍與ASCII相容(即同樣向下兼容ASCII編碼),使得原來處理ASCII字符的軟件無須或只進行少部分修改后,便可繼續使用。因此,它逐漸成為電子郵件、網頁及其他存儲或傳送文字的應用中,優先采用的編碼。

    • 【特點】字母用1個字節表示,漢字用3個字節。

  • 中文字符集 —— GBK

    • 中華文明源遠流長,目前漢字大約有十萬個,常用的漢字都有幾千個,這么多的字符,顯然靠美國的ASCII碼字符集是不可能存儲的
    • 國家標準化委員于1995年頒布了GBK標準,全稱“漢字編碼擴展規范”,GBK兼容GB2312標準,同時在GB2312標準的基礎上擴展了GB13000包含的字,但編碼不同
    • GBK標準中收錄了2萬多漢字及符號,因其最早被WINDOWS采用,所以其應用范圍非常廣

————————————————————————————————————————————————————————————————————————————

更多參考

千峰教育-IO框架

字符在計算機中的存儲
Java計算機如何存儲字符&&常用編碼介紹

原文鏈接:https://blog.csdn.net/qq_60501861/article/details/125627734

欄目分類
最近更新