• C语言文件操作


    FILE为C语言提供的文件类型,它是一个结构体类型,用于存放文件的相关信息。文件打开成功时,对它作了内存分配和初始化。

    每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息,使用者不必关心细节。
    一般都是通过一个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便。

    文件打开和关闭

    C语言的安全文件打开函数为_wfopen_s和_fopen_s。

    1. errno_t _wfopen_s(
    2. FILE** pFile,
    3. const wchar_t *filename,
    4. const wchar_t *mode
    5. );
    • 参数pFile

    指向文件指针的指针,该文件指针将接收指向打开文件的指针。

    • 参数filename

    表示要打开的文件名

    • 参数mode 

     表示访问方式,该参数可设置的值如下表。

    模式含义
    "r"打开以供阅读。如果文件不存在或找不到,则fopen_s调用将失败。
    "w"打开一个空文件进行写入。如果给定文件存在,则其内容将被销毁。
    "a"在新数据写入文件之前,在文件末尾打开以写入(追加),而不删除文件结束 (EOF) 标记。如果文件不存在,则创建该文件。
    "r+"可打开读取和写入。该文件必须存在。
    "w+"打开一个空文件进行读取和写入。如果该文件存在,则其内容将被销毁。
    "a+"打开以供阅读和追加。追加操作包括在将新数据写入文件之前删除 EOF 标记。写入完成后,不会还原 EOF 标记。如果文件不存在,则创建该文件。

    除了上述模式外,还可以包含以下字符,以指定换行符的转换模式: 

    模式修改器翻译模式
    t以文本(翻译)模式打开。
    b以二进制(未翻译)模式打开;禁止显示涉及回车符和换行符的转换。

    返回值 

     如果成功,则为零;失败时的错误代码

    使用Close函数文件关闭。

    文件写入

    1. size_t fwrite(
    2. const void *buffer,
    3. size_t size,
    4. size_t count,
    5. FILE *stream
    6. );
    • 参数buffer

    表示存储读取数据的缓冲区

    • 参数size

    表示要写入数据的一个字符的大小(以字节为单位)

    • 参数count

    表示要写入数据的字符数

    • 参数stream

    表示指向文件结构体的指针 

    • 返回值

     fwrite返回函数写入的完整项数,如果发生错误,该数可能小于count

    文件读取

    1. size_t fread(
    2. void *buffer,
    3. size_t size,
    4. size_t count,
    5. FILE *stream
    6. );
    • 参数buffer

    表示存储读取数据的缓冲区

    • 参数size

    表示要读取数据的一个字符的大小(以字节为单位)

    • 参数count

    表示要读取数据的字符数

    • 参数stream

    表示指向文件结构体的指针 

    • 返回值

    fread返回函数读取的完整项目数,如果发生错误,或者在达到COUNT 之前遇到文件末尾,则可能小于Count

    fseek函数

    可以通过fseek函数设置文件指针的位置。通过该方法可以计算出文件的大小

    1. int fseek(
    2. FILE *stream,
    3. long offset,
    4. int origin
    5. );
    • 参数stream

    表示指向文件结构体的指针

    • 参数offset

    表示指针的偏移量

    • 参数origin

    表示指针所处位置 ,其可以设置的如下的取值

    取值意义
    SEEK_CUR文件指针的当前位置。
    SEEK_END文件结束。
    SEEK_SET文件的开头。

    ftell函数 

    ftell函数可以获取文件指针的当前位置。通过fseek结合ftell两个函数可以计算出文件的大小

    1. long ftell(
    2. FILE *stream
    3. );

    Demo示例

    写数据:

    1. void CMyFileCFileView::OnFileWrite() {
    2. FILE* pFile = NULL;
    3. //打开文件
    4. errno_t err = _wfopen_s(&pFile, _T("1.txt"), _T("w"));
    5. if (err != 0) {
    6. TRACE("Open File failed errorcode :%d", GetLastError());
    7. return;
    8. }
    9. fwrite(L"Hello World", 2, wcslen(L"Hello World")+1, pFile);
    10. fclose(pFile);
    11. }

    读数据: 

    1. void CMyFileCFileView::OnFileRead() {
    2. FILE* pFile = NULL;
    3. errno_t err = _wfopen_s(&pFile, _T("1.txt"), _T("r"));
    4. if (err != 0) {
    5. TRACE("Open File failed errorcode :%d", GetLastError());
    6. return;
    7. }
    8. fseek(pFile, 0, SEEK_END);//文件指针定位,偏移到结尾
    9. int len = ftell(pFile);//得到文件指针的当前位置 = 每个字符大小×字符长度
    10. WCHAR* pBuf = new WCHAR[len];
    11. fseek(pFile, 0, SEEK_SET);//文件指针回到文件起始位置
    12. pBuf[len] = 0;
    13. fread(pBuf, 2, len, pFile);
    14. MessageBox(pBuf);
    15. fclose(pFile);
    16. }

    解决读取乱码 

    使用C语言进行文件操作时如果稍微不注意很容易读出乱码。一般出现这种乱码有两种原因,一种时写入的数据没有写入结束符,另一种是读取数据时,读取数据的个数设置出错。

    例如下面这种写入数据和读取数据的代码,运行出来就会出现乱码。

    1. //写数据
    2. fwrite(L"Hello World", 2, wcslen(L"Hello World"), pFile);
    3. //读数据
    4. //pFile此时移动到了文件末尾
    5. int len = ftell(pFile);//得到文件指针的当前位置 = 每个字符大小×字符长度
    6. WCHAR* pBuf = new WCHAR[len];
    7. fseek(pFile, 0, SEEK_SET);//文件指针回到文件起始位置
    8. fread(pBuf, 2, len, pFile);

    使用ftell获取的文件指针到的位置为 一个字符大小 ×字符长度。 虽然"Hello World"有11个字符,但是WCHAR类型,所以ftell返回的值为22。在之后使用fread读取数据时读到了len×2=44字节的内容,而写入的数据才22字节,因此会出现乱码。

    解决方法:

    方法一:给存储的数据结尾加上结束符'\0"

    在写数据时:

    fwrite(L"Hello World", 2, wcslen(L"Hello World")+1, pFile);

    写入的实际长度+1,这样写入的数据后面会增加一个'\0'结束符。

    fread在达到COUNT 之前遇到文件末尾也会停止。这样在缓冲缓冲数组就可以读到实际数据和一个结束符。打印时就能正常打印出。

    1. //写数据
    2. fwrite(L"Hello World", 2, wcslen(L"Hello World")+1, pFile);
    3. //读数据
    4. //pFile此时移动到了文件末尾
    5. int len = ftell(pFile);//得到文件指针的当前位置 = 每个字符大小×字符长度
    6. WCHAR* pBuf = new WCHAR[len];
    7. fseek(pFile, 0, SEEK_SET);//文件指针回到文件起始位置
    8. fread(pBuf, 2, len, pFile);

    方法二:限制读取的大小

    因为len得到的是一个字符的大小×字符数,如果把len/sizeof(WCHAR),就可以获得实际的字符数。在读取数据时使用这个实际的长度作为读取数,之后将缓冲数组len的位置赋值为'\0'。就可以读取到实际数据

    1. //写数据
    2. fwrite(L"Hello World", 2, wcslen(L"Hello World"), pFile);
    3. //读数据
    4. //pFile此时移动到了文件末尾
    5. int len = ftell(pFile);//得到文件指针的当前位置 = 每个字符大小×字符长度
    6. WCHAR* pBuf = new WCHAR[len];
    7. fseek(pFile, 0, SEEK_SET);//文件指针回到文件起始位置
    8. fread(pBuf, 2, len/2, pFile);
    9. pBuf[len/2] = 0;

  • 相关阅读:
    Prometheus、node_exporter、Grafana端口修改(端口占用)
    哈夫曼树的递归打印不能实现
    Kubernetes学习笔记-StatefulSet:部署有状态的多副本应用(2)20220625
    SQL每日一练(牛客新题库)——第7天:必会常用函数
    ​力扣解法汇总1779. 找到最近的有相同 X 或 Y 坐标的点
    Maven多模块快速升级超好用Idea插件-MPVP
    vsimk is exiting with code 211
    一行代码让你的项目轻松使用Dapr
    腾讯蓝鲸 API 网关如何借助 APISIX 实现产品升级与业务完善
    Create Web, Desktop, Mobile Apps for .NET 8 with Telerik
  • 原文地址:https://blog.csdn.net/qq_54169998/article/details/128095615