【流 Stream】数据传输:字节流与字符流详解 文件读写必备基础知识!!!

目录

  • 0 引言
  • 1 流 Stream
    • 1.1 定义与作用
    • 1.2 流的分类
    • 1.3 字节流与字符流
      • 扩展知识
  • 2 IO编程实践
    • 2.1 字节流的使用
      • 小结
    • 2.2 字符流的使用
    • 2.3 总结

请添加图片描述

  • ‍♂️ 作者:海码007
  • 专栏:C++专栏
  • 相关文章:字符编码
  • 标题:【流 Stream】数据传输:字节流与字符流详解 文件读写必备基础知识!!!
  • ❣️ 寄语:书到用时方恨少,事非经过不知难。
  • 最后:文章作者技术和水平有限,如果文中出现错误,希望大家能指正

0 引言

本文介绍一下字节流与字符流的概念以及区别,需要一些预备知识,可以更好的阅读本文。相关前置知识文章链接:预备知识文章链接

1 流 Stream

1.1 定义与作用

在计算机编程中,流(stream) 是一种抽象概念,用于表示数据在程序中的传输和处理方式。流可以看作是数据的通道,通过这个通道,数据可以按照特定的顺序从源头流向目的地。

主要用于解决数据传输和处理中的一些基本问题,例如:

  • 输入和输出:流提供了一种统一的方式来处理输入和输出。程序可以从输入流中读取数据,或将数据写入到输出流中。这使得程序可以与不同的数据源(如文件、网络、设备等)进行交互,而无需关心底层的细节。
  • 数据传输:流可以将数据分割成小的数据块(如字节或字符),并按照顺序逐个传输。这种分块传输的方式可以提高效率,特别是当处理大量数据时。
  • 缓冲机制:流还可以提供缓冲机制,将数据暂时存储在内存中,以提高读写的效率。通过缓冲,程序可以一次读取或写入多个数据块,减少了与底层数据源的频繁交互,从而提高了性能。

1.2 流的分类

根据流的不同分类标准,可以将流分为以下几类:

  • 按照流向分类
    • 输入流(Input Stream):从数据源(如文件、键盘、网络等)读取数据的流,程序可以通过输入流来接收外部数据
    • 输出流(Output Stream):向目标位置(如文件、屏幕、网络等)写入数据的流,程序可以通过输出流将数据发送到外部
  • 按照传输单位分类
    • 字节流(Byte Stream):以字节为单位进行数据传输的流。字节流主要用于处理二进制数据,如图像、音频等。在字节流中,数据被看作是一系列的字节流,可以按照字节的方式进行读取和写入。
    • 字符流(Character Stream):以字符为单位进行数据传输的流。字符流主要用于处理文本数据,如读写文本文件。在字符流中,数据被视为字符的序列,并根据字符编码将字符转换为字节进行传输。

1.3 字节流与字符流

字节流(Byte Stream)和字符流(Character Stream)是输入流(Input Stream)和输出流(Output Stream)在不同层次上的抽象。它们主要处理不同类型的数据。

  1. 字节流
    字节流以字节为单位进行数据传输和处理,适合处理二进制数据,如图像、音频、视频等。字节流将数据视为字节序列,在读取和写入时按照字节的方式进行操作,不对数据进行任何解析或处理。常见的字节流类包括InputStream和OutputStream。

  2. 字符流
    字符流以字符为单位进行数据传输和处理,适合处理文本数据,如读写文本文件。字符流将数据视为字符的序列,并根据指定的字符编码将字符转换为字节进行传输。字符流在读取和写入时会自动进行字符编码和解码。常见的字符流类包括Reader和Writer。

总结一下两者的区别

  • 数据类型:字节流处理原始字节数据,而字符流处理字符数据
  • 处理内容:字节流适合处理任意类型的二进制数据,如图像、音频等。字符流适合处理文本数据,如读写文本文件。
  • 编码和解码:字节流原封不动地传输数据,不进行编码和解码操作。字符流在传输数据之前会进行字符编码(将字符转换为字节)和解码(将字节转换为字符)
  • 接口方法:字节流提供了一些读取和写入原始字节的方法。字符流提供了一些读取和写入字符的方法,以及一些处理字符数据的辅助方法(如换行符转换)。

总体而言,字节流适用于处理底层的原始数据,而字符流适用于处理更高层次的文本数据。选择使用哪种类型的流取决于所处理数据的类型和需求。

扩展知识

所有文件的储存是都是字节(byte)的储存,在磁盘上保留的并不是文件的字符而是先把字符编码成字节,再储存这些字节到磁盘。在读取文件(特别是文本文件)时,虽然我们看到的是字符文字(这是使用字符编码将二进制数转换成了字符),也是一个字节一个字节地读取以形成字节序列。只不过字符流会会先进行编码和解码操作。

  • 如何区分使用字节流还是字符流
  • 最简单的方法,就是使用记事本打开文件。在计算机中所有的都是一个文件,假如使用记事本打开,是看的懂的字符,那么就使用字符流传输。如果乱码的话就使用字节流传输。(前提是,使用了正确的字符编码,不然有些事文本文件的,但是没使用对应的字符编码打开也会乱码)

例如:我打开了CompileVer.pri 和 shellbrd.dll 两个文件,不需要关心文件后缀是什么。只要关心打开之后乱不乱码就行。.dll文件就乱码了。(Tips:使用记事本打开任何文件时,右下角会显示当前使用的编码方式)
【流 Stream】数据传输:字节流与字符流详解 文件读写必备基础知识!!!_第1张图片

【流 Stream】数据传输:字节流与字符流详解 文件读写必备基础知识!!!_第2张图片

2 IO编程实践

接下来使用的是QT库实现对数据的IO操作。

预备知识

  • 在Qt中,Unicode编码是默认的字符编码方式,QString类和QChar类都使用Unicode编码来表示字符。
  • 可以使用QString的toUtf8()和fromUtf8()方法将文本数据转换为UTF-8编码的字节流进行存储和读取。
  • QByteArray类是用于处理原始字节数据的类,它并不对字节数据应用任何特定的编码方式。QByteArray只是简单地存储字节序列,不涉及解码或编码操作。

2.1 字节流的使用

首先添加 QFile 和 QDebug 头文件。

#include 
#include 
  1. 新建函数 ReadFileByByteStream 实现通过字节流的方式读取文件
void ReadWriteFile::ReadFileByByteStream()
{
    // 打开文件进行读取
    QFile inputFile("output.bin");
    if (!inputFile.open(QIODevice::ReadOnly)) {
        qDebug() << "无法打开输入文件";
        return;
    }

    // 读取字节流数据
    QByteArray  readData          = inputFile.readAll();
    QString     readData_UTF8     = QString::fromUtf8(readData);    // 将读取的字节流数据以UTF-8编码规则解码为Unicode字符

    qDebug() << "readData = "           << readData;
    qDebug() << "readData_UTF8 = "      << readData_UTF8;

    // 关闭输入文件
    inputFile.close();
}
  1. 新建函数 WriteFileByByteStream 实现通过字节流的方式写入文件
void ReadWriteFile::WriteFileByByteStream()
{
    // 打开文件进行写入
    QFile outputFile("output.bin");
    if (!outputFile.open(QIODevice::WriteOnly)) {
        qDebug() << "无法打开输出文件";
        return;
    }

    // 写入字节流数据
    QByteArray data;
    data.append("hello你好");                     // Unicode字符,UTF-8编码
    outputFile.write(data);

    // 关闭输出文件
    outputFile.close();
}
  1. 测试功能,先写入数据,再读取数据
ReadWriteFile myIOFile;
myIOFile.WriteFileByByteStream();
myIOFile.ReadFileByByteStream();
  1. 输出结果如下:
readData =  "hello\xE4\xBD\xA0\xE5\xA5\xBD"
readData_UTF8 =  "hello你好"

结果分析

  • 结果一:readData = “hello\xE4\xBD\xA0\xE5\xA5\xBD”
    • 可以看到hello是正确显示了,但是 “你好” 字符串(以UTF-8编码)缺没有正常显示,是因为 QByteArray 是没有编码解码这种操作的。所以,存储了什么数据就直接输出了什么数据。但是你可以会疑惑,明明我写入的时候是 "hello你好"字符串,为什么变成这种形式。
    • 要弄清楚,现在我们测试的是字节流输入输出,是不涉及字符编码解码操作的。所以在将 “hello你好” 数据添加给 QByteArray 时,就已经是存储的字节数据。QByteArray 中存储的就是 “hello\xE4\xBD\xA0\xE5\xA5\xBD” 数据。这一步其实是QT底层帮我们进行的编码操作,将字符串解析成二进制数。“\xE4\xBD\xA0\xE5\xA5\xBD” 字符其实就是 “你好” 以UTF-8编码下的表示。
      【流 Stream】数据传输:字节流与字符流详解 文件读写必备基础知识!!!_第3张图片
  • 结果二:readData_UTF8 = “hello你好”
    • 为什么结果二正确显示了,因为我们使用了QString::fromUtf8()函数将 QByteArray 存储的原始数据进行UTF-8规则的解码。所以就可以正常显示中文了。(注意假如解码的规则和编码规则不同,也是会乱码的)
      【流 Stream】数据传输:字节流与字符流详解 文件读写必备基础知识!!!_第4张图片

小结

Tips:编码是将字符转换为二进制形式的过程,而解码则是将二进制形式的字符转换为可读的字符。

使用字节流输入输出操作,不会对数据进行解码与编码。上面的测试程序,之所以能够正常显示出来,是因为我们对数据进行了解码,才正确显示输出。假如不进行解码直接输出的就是原始数据。

2.2 字符流的使用

同样添加 QFile 和 QDebug 头文件。

#include 
#include 
  1. 新建函数 ReadFileByCharacterStream 实现通过字符流的方式读取文件
void ReadWriteFile::ReadFileByCharacterStream()
{
    // 打开文件进行读取
    QFile inputFile("output.txt");
    if (!inputFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qDebug() << "无法打开输入文件";
        return;
    }

    // 创建QTextStream对象,并与输入文件关联
    QTextStream inputStream(&inputFile);

    // 读取字符流数据
    QString readData = inputStream.readAll();
    qDebug() << "readData = " << readData;

    // 关闭输入文件
    inputFile.close();
}
  1. 新建函数 WriteFileByCharacterStream 实现通过字符流的方式写入文件
void ReadWriteFile::WriteFileByCharacterStream()
{
    // 打开文件进行写入
    QFile outputFile("output.txt");
    if (!outputFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
        qDebug() << "无法打开输出文件";
        return;
    }

    // 创建QTextStream对象,并与输出文件关联
    QTextStream outputStream(&outputFile);

    // 写入字符流数据
    outputStream << "Hello, World!";

    // 关闭输出文件
    outputFile.close();
}

2.3 总结

  • 所有文件的存储都是字节(byte)的存储,在磁盘上保留的是字节。
  • 所有文件都能使用字节流的方式读取,而字符流主要处理文本数据。

你可能感兴趣的:(C++,QT,流,Steam,Streaming,流化,字节流,字符流,读写)