• C中文件的输入输出与C++的文件流


    C中文件输入输出

    文件的缓冲区:

    每一个被使用的文件都在内存中用一个FILE结构体来存储相应的文件信息(如文件的名字、文件的状态、文件当前位置等)。FILE的定义在stdio.h中

    typedef struct
    {
        short level; //缓冲区“满”的程度
        unsigned flags; //文件状态标志
        char fd;//文件描述符
        unsigned char hold;//如缓冲区无内容不读取字符
        short bsize;//缓冲区的大小
        unsigned char* buffer;//缓冲区位置
        unsigned char* curp;//指针当前的指向
        unsigned istemp; //临时文件指示器
        short token; //用于有效性检查
    }FILE;
    注:不同的编译器的FILE类型略有不同。例如,VC6.0中
    struct _iobuf {
            char *_ptr;
            int   _cnt;
            char *_base;
            int   _flag;
            int   _file;
            int   _charbuf;
            int   _bufsiz;
            char *_tmpfname;
            };
    typedef struct _iobuf FILE;
    

    一般使用文件类型指针,FILE* fp(注:它指向内存中的文件信息区(即FILE)的开头,而不是指向外部介质上的数据文件的开头)。
    FILE* fp;
    fp = fopen("test.txt","w");//以只写的方式打开文件
    ...
    fclose(fp);//关闭文件

    文件打开方式 若指定文件不存在
    "r"(只读) 出错
    "w"(只写) 建立新文件,若文件存在,内容被销毁
    "a"(追加) 出错(自测VC6可以打开,新建一个空文件,应该编译器默认添加了w)
    "rb"(二进制文件的只读) 出错
    "wb"(二进制文件的只写) 建立新文件,若文件存在,内容被销毁
    "ab"(二进制文件的追加) 出错
    "r+"(读写) 出错
    "w+"(读写) 建立新文件,若文件存在,内容被销毁
    "a+"(读写) 出错(自测VC6可以打开,新建一个空文件,应该编译器默认添加了w)
    "rb+"(二进制文件的读写) 出错
    "wb+"(二进制文件的读写) 建立新文件,若文件存在,内容被销毁
    "ab+"(二进制文件的读写) 出错(自测VC6可以打开,新建一个空文件,应该编译器默认添加了w)

    注:加b的表示对二进制文件操作,默认(不加b)对文本文件操作。a(append)表示在文件末尾写入。

    常用下面的方法打开一个文件

    if((fp=fopen("filename","r"))==NULL)
    {
      printf("打开文件失败!");
      exit(0);    
    }
    

     向文件读写的函数

    fgetc(fp) 或 getc(fp) 从fp指向的文件读入一个字符 成功,返回读到的字符,失败返回文件结束标志EOF(即-1)
    fput(ch, fp) 或 putc(ch, fp) 把字符ch写到文件中去  成功,返回写入的字符,失败返回EOF
    fgets(char* str, int n, FILE* fp) 从文件读入一个长度为n-1的字符串,存到字符数组str中 成功,返回str的地址,失败返回NULL
    fput(char* str, FILE* fp) 把字符串str写入文件中 成功返回0,失败返回非0
    fprintf(文件指针, 格式字符串, 输出列表) 以格式化的方式写文件  
    fscanf(文件指针, 格式字符串, 输入列表) 以格式化的方式读文件  
    fwrite(const void *, size_t, size_t, FILE *) 向文件中写一个数据块  
    fread(void *, size_t, size_t, FILE *) 从文件中读一个数据块  

    最最常用的是 fread 和 fwrite 函数,它们在读写时是以二进制的方式进行的,速度快。

    fread(void *buffer, size_t size, size_t count, FILE * fp)
    fwrite(const void *buffer, size_t size, size_t count, FILE *fp)
    buffer 是一个地址
    size:要读写的字节数
    count:要读写多少个数据项(每个数据项的长度为size)
    fp:文件指针  
    返回值:成功返回count,失败返回小于 count 的整数
    

     返回值

    返回成功读取的对象个数,若出现错误或到达文件末尾,则可能小于count。
    若size或count为零,则fread返回零且不进行其他动作。
    fread不区分文件尾和错误,因此调用者必须用feofferror才能判断发生了什么。

    注意:用”w+"打开时,用fwrite写入后就用fread读有点问题。不知为何?建议“w“和”r”分开读写。知道了原因:写入后文件位置标记(或称文件位置指针)是在文件的末尾,应该把标记定位到文件头再读文件。

    • 文件位置标记(文件位置指针)

    文件位置标记是指“接下来要读写的下一个字符的位置”,它随文件的读或写向后移动。
    改变文件标记的位置:
    void rewind(FILE* fp):使文件标记指向文件开头。
    int  fseek(FILE* fp, long offset, int origin); origin表示起始点,offset表示相对起始点的偏移量(正数向后偏移,负值向前偏移),成功返回0,失败返回非0值。
      origin可以取值0,1,2;0表示文件头位置,1表示当前位置,2表示文件末尾位置。
      #define SEEK_SET 0 //文件的开头位置
      #define SEEK_CUR 1
      #define SEEK_END 2
    long ftell(FILE* fp); 获取文件文件位置标记的当前位置。是用相对于文件头的位移量来表示。成功返回位置,失败返回-1L。
    feof(fp) 若标记到文件末尾,再读文件,该函数就返回1。否则返回0
    还有一些文件读写的函数:
    ferror(fp) 若文件的输入输出出错了,该函数返回非0值,未出错返回0。
    clearerr(fp) 清空错误,使文件错误标志和文件结束标志置为0。当ferror函数为非0值后,应该调用clearerr函数,使ferror(fp)的值为0,以便下次检查。

     

    C++的文件流

    ifstream / ofstream / fstream
    输入 / 输出 / 输入和输出

    关于流的继承关系:Cpp中流继承关系

    写入文件时用 std::ofstream
    读取文件时用 std::ifstream

    打开模式:(所在类 std::ios_base::openmode  或  std::ios::openmode)可以按位或“|”组合下面模式

    openmode effect
    in 打开文件读,若文件不存在会打开失败。
    out 打开文件写,若文件存在,覆盖原来内容;若不存在,则新建。
    ate 打开文件,并把文件流位置移动到结尾。
    app 打开文件,在文件结尾追加
    trunc 在写之前删除文件内容
    binary 以二进制模式打开(不指定binary,默认是text模式)

    建议用binary方式打开,因为对于非文本数据的文件(如图形文件)用文本模式打开,将是一场灾难。(例如:写 -->   读 --> )

    打开文件的两种方式:1. 在构造时直接传递文件名,2. 用 open()

    #include <fstream>
    ...
    // 1.
    std::ofstream ofile("filename");//ofstream 默认的打开模式为 std::ios::out
    // 2.
    std::ofstream ofile;
    ofile.open("filename");
    /// 检查文件是否打开
    if(!ofile.is_open())  或者  if(!ofile) return; 或者  if(ofile.fail()) return;
    {
        return;//...   
    }
    

    ifstream

    	std::ifstream ifile("test.txt");//默认的模式是 std::ios_base::in
    	if (!ifile)
    		return -1;
    	const int size = 256;
    	char buffer[size];
    	do
    	{
    		//ifile.getline(buffer, size); 
    		//std::cout << buffer << '
    ';
    		std::string str;
    		std::getline(ifile, str);
    	} while (!ifile.eof());
    

     文件位置标记

    对于输入操作 ifstream ,获取标记(get marker)判断下一个读取的位置。用 tellg() 返回获取标记的位置,用 seekg(..) 来设置 get marker 的位置。
    对于输出操作 ofstream,放置标记(put marker)判断下一个写入的位置。用 tellp() 返回放置标记的位置,用 seekp(..) 来设置 put marker 的位置。

    istream& seekg (streamoff off, ios_base::seekdir way);
    ostream& seekp (streamoff off, ios_base::seekdir way);
    //@Param           off :  相对 way 的偏移字节数
    //                 way :  std::ios_base::beg 文件的开始         0
    //                        std::ios_base::cur 文件的当前位置      1
    //                        std::ios_base::end 文件的结尾         2

     二进制模式

    在C++程序和文件之间总是存在另一个程序,就是操作系统(OS)。your program <-->operating system <--> file

    //未格式化的输出 (以二进制形式或字节形式写入,buf为起始地址,bufsize为字节数)
    std::ofstream& std::ofstream::write(const char* buf, std::streamsize bufsize);
    //未格式化的输入
    std::ifstream& std::ifstream::read(char* buf, std::streamsize bufsize);
    

     以二进制读写文件:

    	std::ofstream ofile("test.dat", std::ios_base::binary);//默认的模式是 std::ios_base::out
    	if (!ofile.is_open())
    		return -1;
    	const char szBuf[] = "I love you.
    12x00kk3
    123";
    	ofile.write(szBuf, sizeof(szBuf));
    	ofile.close();
    
    	std::ifstream ifile("test.dat", std::ios_base::binary);//默认的模式是 std::ios_base::in
    	if (!ifile)
    		return -1;
    	std::string str;
    	const int size = 7;//一般指定 1024 2048 4096
    	char buffer[size];
    	while (!ifile.eof())
    	{
    		ifile.read(buffer, size);
    		std::streamsize n = ifile.gcount();//获得实际读到的字节数
    		std::string s(buffer, n);
    		str += s;
    	}
    	ifile.close();
    	//或者用下面 读取整个文件的内容 的方法
    
    	BYTE* buf = (BYTE*)str.c_str();
    	int nSize = str.size();
    

    读取整个文件的内容 (重点)

    	// 读取整个文件内容的方法 
    	// std::string s; ifile >> s;//不要用这种,因为到空白符会截止。例如:pretty girl 读到s就是 pretty
    	//1.
    	ifile.seekg(0, std::ios::end);
    	int nLen = ifile.tellg();
    	ifile.seekg(0, std::ios::beg);
    	char* buffer = new char[nLen];
    	ifile.read(buffer, nLen);
    	std::string s(buffer, buffer + nLen);
    	delete[] buffer;
    
    	//2. #include <streambuf> 
    	std::istreambuf_iterator<char> eos;
    	std::string sText(std::istreambuf_iterator<char>(ifile), eos);
    	//3. 注意括号
    	std::string sText((std::istreambuf_iterator<char>(ifile)), std::istreambuf_iterator<char>());
    	
    	//4. 用 stringstream (#include <sstream>)
    	std::stringstream buffer;
    	buffer << ifile.rdbuf();
    	std::string s(buffer.str());
    	
    	//5. getline 有时会出错,不知为何
    	std::string s;
    	getline(ifile, s, (char)ifile.eof());
    
    	//6. 用Poco库中的 StreamCopier Header: Poco/StreamCopier.h
    	std::string s;
    	Poco::StreamCopier::copyToString(ifile, s);
    

     注意:文件的大小是否大于string的最大存储空间

        std::string s;
        std::ifstream ifile("F:/兄弟连.rmvb");
        ifile.seekg(0, std::ios::end);
        std::istream::pos_type pos = ifile.tellg();
        if (pos > s.max_size())//max_size : 4294967294  0xfffffffe  4G-2  2^32-2
        {
            std::cout << "文件大小超过string的最大空间,需要切割。";
        }

    MFC中的 CFile

        CFile ofile;
        if (!ofile.Open(_T("test.dat"), CFile::modeCreate | CFile::modeWrite))
            return FALSE;
        const char szBuf[] = "I love you.
    12x00kk3
    123";
        ofile.Write(szBuf, sizeof(szBuf));
        ofile.Close();
    
        CFile ifile;
        if (!ifile.Open(_T("test.dat"), CFile::modeRead))
            return FALSE;
    /*    std::string str;
        char buf[7]; //一般 1024 2048 4096
        int nLen = 0;
        while (nLen = ifile.Read(buf, sizeof(buf)))
        {
            std::string s(buf, nLen);
            str += s;
        }
        */
        //or
        UINT nSize = ifile.GetLength();
        //ifile.SeekToBegin();
        //int n = ifile.SeekToEnd();
        char* buf = new char[nSize];
        ifile.Read(buf, nSize);
        //do something ...
        std::string s(buf, nSize);
        delete[] buf;

    MS的 API 函数

        //写文件
        HANDLE hFile = ::CreateFile(_T("test.dat"), GENERIC_WRITE, 0, 
                                    NULL, CREATE_ALWAYS, 0, NULL);
        if (INVALID_HANDLE_VALUE == hFile)
            return FALSE;
        const char szBuf[] = "I love you.
    12x00kk3
    123";
        DWORD dwRet = 0;
        ::WriteFile(hFile, szBuf, sizeof(szBuf), &dwRet, NULL);
        CloseHandle(hFile);
    
        //读文件
        HANDLE hFile2 = ::CreateFile(_T("test.dat"), GENERIC_READ, FILE_SHARE_READ,
                                        NULL, OPEN_EXISTING, 0, NULL);
        if (INVALID_HANDLE_VALUE == hFile2)
            return FALSE;
        DWORD dwLen = ::GetFileSize(hFile2, NULL);
        char* buf = new char[dwLen];
        DWORD dwRet2; //实际读到的字节数
        ::ReadFile(hFile2, buf, dwLen, &dwRet2, NULL);
       CloseHandle(hFile2);
    
        std::string s(buf, dwRet2);
    
        delete[] buf;

    FILE的知识:www.cnblogs.com/lxy2015/p/5302365.html


    2020/12/19

    Cpp读文件

    std::ifstream ifile("test.dat", std::ios_base::binary);//默认的模式是 std::ios_base::in , 并会 |std::ios_base::in
    if (!ifile)
        return -1;
    std::string str;
    const int size = 1024;
    char buffer[size];
    while (!ifile.eof())
    {
        ifile.read(buffer, size);
        std::streamsize n = ifile.gcount();//获得实际读到的字节数
        std::string s(buffer, n);
        //... do something
        // str += s;
    }
    ifile.close();

    复制文件

    #include<fstream>
    bool Copyfile(char* FileSource, char* FileDest)
    {
        fstream fsCopee(FileSource, ios::binary | ios::in);
        if (!fsCopee)
            return false;
        fstream fsCoper(FileDest, ios::binary | ios::out);
        if (!fsCoper)
            return false;
    
        fsCoper << fsCopee.rdbuf();
        return true
    }

    常记溪亭日暮,沉醉不知归路。兴尽晚回舟,误入藕花深处。争渡,争渡,惊起一滩鸥鹭。

    昨夜雨疏风骤,浓睡不消残酒。试问卷帘人,却道海棠依旧。知否?知否?应是绿肥红瘦。
  • 相关阅读:
    Vue Cli 3.x项目如何部署到IIS子站点下
    IntelliJ IDEA 2018.3.2无法正常输入字符问题解决方案
    解决macOS git clone Azure DevOps提示身份认证失败问题
    JDBC driver for MySQL连接提示"The connection property 'zeroDateTimeBehavior' acceptable values are: 'CONVERT_TO_NULL', 'EXCEPTION' or 'ROUND'. The value 'convertToNull' is not acceptable."解决方案
    dbeaver导出MySQL的架构提示"IO Error: Utility 'mysqldump.exe' not found in client home 'MySQL Connector/Net"解决方案
    Git 常用命令和 Git Flow 梳理
    gitflow工作流程基本命令使用
    RocketMQ学习分享
    解决docker中使用nginx做负载均衡时并发过高时的一些问题
    Tomcat中session共享问题的简单解决办法
  • 原文地址:https://www.cnblogs.com/htj10/p/10728143.html
Copyright © 2020-2023  润新知