[c++]文件IO_fscanf、fprintf、fputs、fputc、fgets、fgetc、fseek、fread、fwrite、ftell

版权声明:转载请注明出处 https://blog.csdn.net/weixin_40937100/article/details/88245811

文件IO_fscanf、fprintf、fputs、fputc、fgets、fgetc、fseek、fread、fwrite、ftell

在对矩阵操作的过程中应用fscanf()函数对文件内容进行读取,于是对这个函数的用法的原理总结如下

一、用法

函数原型:

int fscanf ( FILE *fp, char * format, ... );
int fprintf ( FILE *fp, char * format, ... );

fprintf() 返回成功写入的字符的个数,失败则返回负数。fscanf() 返回参数列表中被成功赋值的参数个数。

FILE *fp;
int i, j;
double num;
char *str, ch;
fscanf(fp, "%d %s &lf", &i, str, &num);//这里要注意,且scanf时候double数格式都是%lf,否则读不出来
fprintf(fp,"%d %c %f", j, ch, num);//写入时不需要取地址,与printf()一致,且printf时候double数格式都是%f,否则写不进去

/* 另外,读取文件中的字符串时,最好char temp[50],而非char* tmp,
   调用fprintf(fp,"%s", temp);这样比较稳定,用指针存在问题 */

二、文件指针与基本定义方式

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

int main(){

    FILE* fp;
    char* FileName;
    FileName = "input_file2";
    fp = fopen(FileName, "r");

    return 0;
}

三、演示1

文件内容:

用fscanf读取:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

int main(){
    FILE* fp;
    char* FileName;
    FileName = "input_file2";
    fp = fopen(FileName, "r");
    double num = 0;
    fscanf(fp, "%lf", &num);
    cout<<num<<endl;
    cout<<endl;

    fscanf(fp, "%lf", &num);
    cout<<num<<endl;
    cout<<endl;

    fscanf(fp, "%lf", &num);
    cout<<num<<endl;
    cout<<endl;

    return 0;
}

结果:

扫描二维码关注公众号,回复: 5647731 查看本文章

四、演示2

向inputfile2中写入信息:用fprintf

在这里强调一下fopen()函数和fclose()函数:

fopen函数用于打开文件, 其调用格式为:
     FILE *fopen(char *filename, *type);
如果成功的打开一个文件, fopen()函数返回文件指针,   否则返回空指针 (NULL)。由此可判断文件打开是否成功。
fclose()函数用来关闭一个由fopen()函数打开的文件 , 其调用格式为:
      int fclose(FILE *stream);
该函数返回一个整型数。当文件关闭成功时, 返回0, 否则返回一个非零值。可以根据函数的返回值判断文件是否关闭成功。

文件操作类型:

用r不如用r+,可读可写,但每fopen()一次文件进行写入,就会刷掉之前的内容。

在用r+的情况下,在一次fopen()中先写后读会出错:字符串作为最后一个写入元素和最后一个读出元素,会乱码。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

int main(){
    FILE* fp;
    char* FileName;
    FileName = "input_file2";
    fp = fopen(FileName, "r+");
    double num = 0;
//    fscanf(fp, "%lf", &num);
//    cout<<num<<endl;
//    cout<<endl;
//
//    fscanf(fp, "%lf", &num);
//    cout<<num<<endl;
//    cout<<endl;
//
//    fscanf(fp, "%lf", &num);
//    cout<<num<<endl;
//    cout<<endl;

    double num1 = 123456.789;
    double num2 = 3456.889;
    char* str = "I Love China";
    fprintf(fp, "%f %f %s", num1, num2, str); //注意这里,printf对应double数的格式%f,否则出错
    //fputs("sssssssss", fp);
    //fprintf(fp, "%s", str);
    //fprintf(fp, "%f", num1);

    fclose(fp);
    return 0;
}

五、演示3(将读取fscanf和写入fprintf都测试到)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<stdlib.h>
using namespace std;

int main(){
    FILE* fp;
    char* FileName;
    FileName = "input_file2";
    fp = fopen(FileName, "r+");
    double write1 = 1.23;
    double write2 = 2.34;
    int write3 = 88;
    char write4[50] = "ILoveChina";

    double read1 = 0;
    double read2 = 0;
    int read3 = 0;
    char read4[55] = {};

    /* 写入文件fprintf */
    //fprintf(fp, "%f %f %d %s", write1, write2, write3, write4);



    /* 读取文件 */
    fscanf(fp, "%lf %lf %d %s", &read1, &read2, &read3, read4);
    cout<<read1<<endl;
    cout<<read2<<endl;
    cout<<read3<<endl;
    cout<<read4<<endl;

    //注意,不要在一次fopen()中既写又读,容易出错

    fclose(fp);
    return 0;
}

六、文件操作函数归类

1、文件写

fprintf()、fputs()和fputc()函数
    函数fprintf()、fputs()和fputc()均为文件的顺序写操作函数,  其调用格式如下:
    int fprintf(FILE *stream, char *format, <variable-list>);
    int fputs(char *string, FILE *steam);
    int fputc(int ch, FILE *steam);
上述三个函数的返回值均为整型量。fprintf() 函数的返回值为实际写入文件中的字罕个数(字节数)。如果写错误, 则返回一个负数, fputs()函数返回0时表明将string指针所指的字符串写入文件中的操作成功, 返回非0时,  表明写操作失败。fputc()函数返回一个向文件所写字符的值, 此时写操作成功,  否则返回EOF(文件结束结束其值为-1, 在stdio.h中定义)表示写操作错误。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<stdlib.h>
using namespace std;

int main(){
    FILE* fp;
    char* FileName;
    FileName = "input_file2";
    fp = fopen(FileName, "r+");
    double write1 = 1.23;
    double write2 = 2.34;
    int write3 = 88;
    char write4[50] = "ILoveChina";

    double read1 = 0;
    double read2 = 0;
    int read3 = 0;
    char read4[55] = {};

    /* 写入文件fprintf */
    fprintf(fp, "%f %f %d %s", write1, write2, write3, write4);
    fputc(':', fp); //向文件写入一个字符
    fputs("use fputs write",fp); //向文件写一个字符串

    fclose(fp);
    return 0;
}

2、文件读

fscanf()、fgets()和fgetc()函数
    函数fscanf()、fgets()和fgetc()均为文件的顺序读操作函数, 其调用格式如下:
     int fscanf(FILE *stream, char *format, <address-list>);
     char fgets(char *string, int n, FILE *steam);
     int fgetc(FILE *steam);
fscanf()函数的用法与scanf()函数相似,只是它是从文件中读到信息。fscanf()函数的返回值为EOF(即-1), 表明读错误, 否则读数据成功。
fgets()函数从文件中读取至多n-1个字符(n用来指定字符数), 并把它们放入string指向的字符串中, 在读入之后自动向字符串末尾加一个空字符, 读成功返回string指针, 失败返回一个空指针。
fgetc()函数返回文件当前位置的一个字符,  读错误时返回EOF
#include<iostream>
#include<cstdio>
#include<cstring>
#include<stdlib.h>
using namespace std;

int main(){
    FILE* fp;
    char* FileName;
    FileName = "input_file2";
    fp = fopen(FileName, "r+");
    double write1 = 1.23;
    double write2 = 2.34;
    int write3 = 88;
    char write4[50] = "ILoveChina";

    double read1 = 0;
    double read2 = 0;
    int read3 = 0;
    char read4[55] = {};


    char get[50];
    fgets(get, 4, fp); //读取三个字符
    cout<<get<<endl;

    fgets(get, 5, fp); //读取四个字符
    cout<<get<<endl;

    fgets(get, 6, fp); //读取五个字符
    cout<<get<<endl;

    fgets(get, 11, fp); //读取十个字符
    cout<<get<<endl;
    //注意,gets函数是包含空格在内的,即一个空格是一个字符


    fclose(fp);
    return 0;
}

注:\t为一个字符

3、文件的随机读写

文件的随机读写函数如下:
    int fseek (FILE *stream, long offset, int fromwhere);
    int fread(void *buf, int size, int count, FILE *stream);
    int fwrite(void *buf, int size, int count, FILE *stream);
    long ftell(FILE *stream);
fseek()函数的作用是将文件的位置指针设置到从fromwhere开始的第offset字节的位置上, 其中fromwhere是下列几个宏定义之一,是指文件位置指针起始计算位置
符号常数          数值         含义 
SEEK_SET          0        从文件开头
SEEK_CUR          1        从文件指针的现行位置
SEEK_END          2        从文件末尾 
offset是指文件位置指针从指定开始位置(fromwhere指出的位置)跳过的字节数。它是一个长整型量, 以支持大于64K字节的文件。fseek()函数一般用于对二进制文件进行操作。即当offset置为7时,将从fromwhere位置跳过7个字节,从第八个字节开始读取

当fseek()函数返回0时表明操作成功, 返回非0表示失败。
if(fseek(fp, 7, SEEK_SET) == 0){ //定位成功
        char num = fgetc(fp); //读出跳过7个字符后的第八个字符的内容
        cout<<num<<endl;

    }

在这里需要注意,fread和fwrite是以二进制方式读写文件,和fprintf、fscanf是有很大区别的:

fprintf(fp, "%d", buffer); 是将格式化的数据写入文件
fprintf(文件指针,格式字符串,输出表列);

fwrite(&buffer, sizeof(int), 1, fp);是以二进位方式写入文件
fwrite(数据,数据类型大小(字节数),写入数据的最大数量,文件指针);

当使用fwrite将一个int型数字65写入文本文件时,由于65对应的二进制数是1000001,十六进制数是0x41,存储的是以二进制的形式1000001.在notepad++中使用十六进制方式打开显示的是:0x0041,转换为十进制则为65,使用记事本打开这个文本文件后显示的是A,因为记事本程序默认为存储在文本文件中的数据都是ASCII码形式存储,它把65当做ASCII码翻译为字符A。

当使用fpintf将一个int型数字65写入文本文件时,将65每一位转换为ASCII码存储,6、5分别对应ASCII码54、53,存储的是ASCII码54、53.在notepad++中使用十六进制方式打开显示的是:3635,转换为十进制则为54、53,这正是数字6、5的ASCII码。使用记事本打开这个文本文件时,记事本将存储在其中的54、53当做ASCII码翻译为字符6、5显示,我们看到的是便是字符65。

七、常见问题

1、fscanf()函数读取文件最后一个元素被重复读取的问题

这个问题发生于文末存在多余空格,回车等符号时发生,手动去除多余符号可使问题解决,但治标不治本。

关于问题出现的机理,有不少解析和对策,现整理有效方法如下:

原理:

当我们用while(!feof(pFile)){...}判断文件是否结束,并处理文件内容的时候经常会遇到文件最后一行重复两遍的问题,这是因为feof在遇到文件结束符EOF这个位置时,返回的还是0;而到下一个位置时才返回1,这时while循环才退出。所以单纯用上面的while循环判断会出现重复的现象。

fscanf函数在遇到文件EOF时会返回-1;因此可以将程序的原fscanf行改成:
       int res = fscanf(fp, "%s", temp); //即:每次正常fscanf,其返回值拿来判断是否break
        if(res == -1) break;

//解决策略:
while(!feof(fp)){

        int res = fscanf(fp, "%s", temp); //即:每次正常fscanf,其返回值拿来判断是否break
        if(res == -1) break;
        cout<<"当前的feature号:"<<featureNum<<endl;
        cout<<temp<<endl;

        featureNum++;
        if(featureNum>784){
            cout<<"以上featrue的sample编号:"<<sampleNum<<endl;
            sampleNum++;
            featureNum = 1;
        }
    }
    fclose(fp);
}

猜你喜欢

转载自blog.csdn.net/weixin_40937100/article/details/88245811