C++文件读写

本文介绍C++处理不同编码格式文件的方法,包括识别文件格式、创建不同格式的文件及字符编码间的转换,并提供示例代码。

最近遇到读取不同格式文件,由于C++不是原生支持unicode,读取起来比较麻烦。这篇博客详细介绍了不同编码格式的区别


常见的类型有:ANSI,utf-8无bom格式,utf-8,usc2 little end,usc2 big end。其中utf8是unicode的一种最常见的实现方式。windows中的宽字符串支持:USC-2 Little End。BOM: Byte Order Mark。UTF-8 BOM又叫UTF-8 签名,其实UTF-8 的BOM对UFT-8没有作用,是为了支援UTF-16,UTF-32才加上的BOM,BOM签名的意思就是告诉编辑器当前文件采用何种编码,方便编辑器识别,但是BOM虽然在编辑器中不显示,但是用cout直接输出的时候会产生一串乱码,在Notepad++中打开可以看到黑色的0xff等字节。因此要跳过头三个字节。


windows下的wchar_t读取的就是UCS2 Little End,通常说的Unicode编码指的是UCS-2 little endian格式编码方式,即直接用两个字节存入字符的Unicode码。windows中C++有3种字符类型,char, wchar_t, TCHAR,最熟悉的char是单字节字符,适用于ANSI编码;wchar_t是双字节的宽字符类型,适用于unicode编码;TCHAR是一个宏,在ANSI坏境下定义为char,unicode坏境下定义为wchar_t。


文件格式:

通过文本文件的文件头(Bom)识别文件格式:
ANSI文件的文件头为空,不需要处理;
UNICODE文件的文件头为0xFF,0xFE共计两个字节,读取时需要偏移两个字节再行读取;
UTF-8文件的文件头为0xEF,0xBB,0xBF共计三个字节,读取时需要偏移三个字节后再行读取;
#include <string>
#include <iostream>
#include <fstream>
using namespace std;

enum FileType
{
	ansi = 0,
	unicode,
	utf8
};

FileType GetFileType(ifstream& infile)
{
	
	FileType type = ansi;
	if (!infile) {
		return ansi;
	}
	if (infile.good()){
		char head[3];
		infile.read(head,sizeof head);

		//utf-8无bom格式无法通过开始字节区分
		cout<<(head[0] & 0xff)<<" "<<(head[1] & 0xff)<<" "<<(head[2] & 0xff)<<endl;
		cout<<head[0]<<head[1]<<head[2]<<endl;

		if ((unsigned char)head[0] == 0xff && (unsigned char)head[1] == 0xfe) {
			type = unicode;
		} else if((unsigned char)head[0] == 0xef && (unsigned char)head[1] == 0xbb 
			&& (unsigned char)head[2] == 0xbf){
			type = utf8;
		}
	}
	return type;
}

(注意:这种方法不能识别 utf-8 无bom格式。)

用C++输入输出流创建不同格式的文件:
如果直接创建一个普通文件则文件格式为ANSI。
如果创建二进制文件:可设置头3个字节来设置文件格式(如果不设置则为:utf-8无bom格式)。
//-----------------------------------------------
//创建不同格式的文件
//-----------------------------------------------
void CreateFileTest(FileType type,const char* filename)
{
	ofstream out;
	out.open(filename,ios::binary);
	if (!out) {
		return;
	}
	char head[3];
	if (type == utf8) { //utf8有bom
		head[0] = 0xef;
		head[1] = 0xbb;
		head[2] = 0xbf;
		out.write(head,sizeof(head));
		out<<"UTF-8"<<endl;
	} else if (type == unicode) { //ucs-2 little end
		head[0] = 0xff;
		head[1] = 0xfe;
		head[2] = 0;
		cout<<(head[0] & 0xff)<<" "<<(head[1] & 0xff)<<" "<<(sizeof(head[0]) * 2)<<endl;
		out.write(head,sizeof(head[0]) * 2);
		wchar_t* buf = L"UNICODE字符";
		out<<buf<<endl;
	}
	out<<"ANSI OR UTF-8"<<endl; //utf-8无bom
	out.close();
}

不同字符格式之间转换:

用windows.h中的WideCharToMultiByte和MultiByteToWideChar函数。使用CP_ACP代码页就实现了ANSI与Unicode之间的转换,使用CP_UTF8代码页就实现了UTF-8与Unicode之间的转换。
经试验发现,windows控制台,用cout只能输出ANSI字符,用wcout只能输出UCS2(unicode)字符。utf8暂时没找到直接输出到控制台的方法,只好通过二进制直接写入到文件保存为utf8无bom格式(也可自己加3字节头部,变成普通utf8格式)。
ANIS字符和utf-8均为multiBytes,unicode字符为宽字符串(windows下为ucs2 little end)。
//-----------------------------------------------
//C++不同文件格式之间的转换
//-----------------------------------------------
#include <windows.h>
#include <locale>

//ANSI 转换成 unicode
bool ANSIToUnicode(const char* inStr,wchar_t* outDest,int MaxLen)
{
	int len = 0;
	if (MaxLen <= 0) {
		return false;
	}
	len = ::MultiByteToWideChar(CP_ACP,0,inStr,-1,outDest,MaxLen);
	if (len < 0) {
		len = 0;
	}
	if (len < MaxLen) {
		outDest[len] = 0;
	} else {
		outDest[MaxLen - 1] = 0;
		return false;//溢出
	}
	return true;
}

//unicode转成utf-8无bom格式
bool UnicodeToUtf8(const wchar_t* inDest,char* outStr,int MaxLen)
{
	
	int len = ::WideCharToMultiByte(CP_UTF8,0,inDest,-1,NULL,0,NULL,NULL);//获取长度
	if (len > MaxLen) {
		return false;
	}
	WideCharToMultiByte(CP_UTF8,0,inDest,-1,outStr,len,NULL,NULL);
	outStr[len - 1] = 0;
	return true;
}

//utf-8转换成unicode
bool Utf8ToUnicode(const char* inStr,wchar_t* outDest,int MaxLen)
{
	int len = ::MultiByteToWideChar(CP_UTF8,0,inStr,-1,NULL,NULL);
	if (len > MaxLen) {
		return false;
	}
	::MultiByteToWideChar(CP_UTF8,0,inStr,-1,outDest,len);
	outDest[len - 1] = 0;
	return true;
}

bool UnicodeToANSI(const wchar_t* inStr,char* outDest,int MAxLen)
{
	int len = ::WideCharToMultiByte(CP_ACP,0,inStr,-1,NULL,0,NULL,NULL);
	if (len > MAxLen) {
		return false;
	}
	::WideCharToMultiByte(CP_ACP,0,inStr,-1,outDest,len,NULL,NULL);
	outDest[len - 1] = 0;
	return true;
}

void testANSIUNICODE()
{
	//把ansi转换成unicode
	locale::global(locale("chs"));
	wchar_t unicodebuf[1024];
	memset(unicodebuf,0,sizeof(unicodebuf));
	char* ansibuf = "这是一个字符串,任务是把字符串转换成不同格式";
	cout<<"[ansi]:"<<ansibuf<<endl;
	ANSIToUnicode(ansibuf,unicodebuf,1024);
	wcout<<L"[unicode]:"<<unicodebuf<<endl;

	//把unicode转换成utf8,
	ofstream fout;
	fout.open("UTF8TEST.txt",ios::binary);
	char utf8buf[1024];
	memset(utf8buf,0,sizeof utf8buf);
	UnicodeToUtf8(unicodebuf,utf8buf,1024);
	cout<<"[utf8]:"<<utf8buf<<endl; //控制台输出为乱码
	fout<<utf8buf<<endl; //文件输出正常
	fout.close();
	cout<<endl;
	
	//unicode转换成ansi
	char newansi[1024];
	memset(newansi,0,sizeof(newansi));
	UnicodeToANSI(unicodebuf,newansi,1024);
	cout<<"[依然ansi]:"<<newansi<<endl;

	//utf8转换成unicode
	memset(unicodebuf,0,sizeof(unicodebuf));
	Utf8ToUnicode(utf8buf,unicodebuf,1024);
	wcout<<L"[依然unicode]:"<<unicodebuf<<endl;
}


C++读写UTF-8文件的方法:



<think>首先,用户的问题是关于C++文件读写的方法和示例。我需要基于提供的引用来回答。 回顾系统指令: - 所有行内数学表达式必须使用$...$格式。 - 独立公式必须使用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 在回答中引用的段落末尾自然地添加引用标识,例如[^1]。 用户提供的引用: - 引用[1]: 解释C++文件读写示例代码的重要性。 - 引用[2]: 提到C++标准库不支持直接读写Excel文件,建议使用第三方库。 - 引用[3]: 给出一个简单的C++文件写入示例代码(写入文本文件)。 用户的问题:"我想查找C++进行文件读写的相关知识 请问C++文件读写的方法和示例" 我需要构建一个结构清晰的回答,帮助用户逐步解决问题。回答应该包括: 1. 介绍C++文件读写的概念。 2. 解释如何使用C++标准库(如<fstream>)进行文件操作。 3. 提供示例代码:包括读取文件和写入文件。 4. 基于引用[2],讨论如何处理Excel或其他格式文件。 5. 最后,生成相关问题。 从引用中提取关键点: - 引用[1]和[3]强调使用C++标准库进行文本文件读写- 引用[2]提到Excel文件需要第三方库。 - 引用[3]提供一个写入文件的代码示例。 我需要补充一些常见的方法,因为用户要求"方法和示例"。标准C++文件读写通常使用: - `ofstream`用于输出文件(写入)。 - `ifstream`用于输入文件读取)。 - `fstream`用于输入输出。 示例应包括: - 写入文件(如引用[3]所示)。 - 读取文件(引用中没有提供,所以我需要添加一个读取文件的示例)。 确保回答真实可靠: - 基于C++标准最佳实践。 - 代码示例正确。 回答结构: 1. **介绍**:简要介绍C++文件读写的重要性和方法。 2. **核心方法**: - 写入文件:使用`ofstream`。 - 读取文件:使用`ifstream`。 - 提到错误处理(如检查文件是否打开成功)。 3. **高级话题**:简要讨论非文本文件(如Excel),基于引用[2]。 4. **完整示例**:提供写入和读取文件的代码。 5. **总结**:重申关键点。 6. **相关问题**:生成3-4个相关问题。 生成相关问题: - 类似于示例中的快速排序问题,我需要创建与C++文件读写相关的后续问题。 - 例如: 1. 如何处理大文件读写以提高性能? 2. 如何在C++读写二进制文件? 3. 推荐哪些第三方库用于Excel文件操作? 4. 文件读写中的错误处理最佳实践是什么? 现在,构建回答: - 使用中文。 - 在引用部分添加引用标识。 - 代码块使用类似Markdown格式,如示例中的Python代码。 代码示例: - 引用[3]提供了一个写入文件的代码。 - 我需要添加一个读取文件的代码。 - 确保代码正确且可运行。 示例读取文件代码: ```cpp #include <fstream> #include <iostream> #include <string> int main() { std::ifstream inputFile; inputFile.open("input.txt"); if (!inputFile) { std::cout << "无法打开文件!" << std::endl; return 1; } std::string line; while (getline(inputFile, line)) { std::cout << line << std::endl; } inputFile.close(); return 0; } ``` 在回答中引用引用: - 当提到文本文件读写时,参考引用[1]和[3]。 - 当提到Excel时,参考引用[2]。 最终回答结构: 1. 引言:C++文件读写的重要性。 2. 基本文件读写方法: - 写入文件(示例)。 - 读取文件(示例)。 3. 处理其他格式文件(Excel)。 4. 总结。 5. 相关问题。 </think>### C++ 文件读写的方法与示例 在 C++ 中,文件读写是后端开发的核心操作之一,用于数据持久化、日志记录和数据交换等场景。C++ 标准库提供了 `<fstream>` 头文件,包含 `std::ifstream`(输入文件流)和 `std::ofstream`(输出文件流)等类,支持文本文件的基本读写操作。以下是基于标准库的详细方法和示例代码。 #### 1. **基本文件读写方法** - **写入文件**:使用 `std::ofstream` 打开文件并写入数据。关键步骤包括创建对象、打开文件、检查错误、写入内容并关闭文件。错误处理至关重要,例如检查文件是否成功打开以避免运行时错误[^1][^3]。 - **读取文件**:使用 `std::ifstream` 打开文件并逐行读取数据。常用 `std::getline` 函数读取文本行。 - **通用流程**: - 包含头文件:`#include <fstream>` 和 `#include <iostream>`。 - 打开文件:使用 `open()` 方法,指定文件名和模式(如 `std::ios::in` 或 `std::ios::out`)。 - 错误检查:验证文件是否成功打开(例如 `if (!file)`)。 - 读写操作:使用流操作符(如 `<<` 或 `>>`)或 `getline`。 - 关闭文件:调用 `close()` 释放资源。 #### 2. **示例代码** 以下示例演示文本文件的写入和读取操作。代码基于 C++17 标准,确保可移植性。 **示例 1:写入文件(文本格式)** 此代码将字符串写入 `output.txt` 文件,并处理可能的错误。 ```cpp #include <fstream> #include <iostream> int main() { std::ofstream outputFile; outputFile.open("output.txt"); // 打开文件用于写入 if (!outputFile) { // 检查文件是否打开成功 std::cout << "无法创建文件!" << std::endl; return 1; // 返回错误码 } outputFile << "Hello, World!" << std::endl; // 写入数据 outputFile << "这是 C++ 文件写入测试。" << std::endl; outputFile.close(); // 关闭文件 std::cout << "数据写入成功!" << std::endl; return 0; } ``` *参考引用[^3]的类似实现。* **示例 2读取文件(文本格式)** 此代码从 `input.txt` 文件读取内容并输出到控制台。 ```cpp #include <fstream> #include <iostream> #include <string> int main() { std::ifstream inputFile; inputFile.open("input.txt"); // 打开文件用于读取 if (!inputFile) { std::cout << "无法打开文件!" << std::endl; return 1; } std::string line; while (std::getline(inputFile, line)) { // 逐行读取 std::cout << line << std::endl; // 输出到控制台 } inputFile.close(); // 关闭文件 return 0; } ``` *此示例补充了引用[^1]中提到的文件操作概念。* #### 3. **处理其他文件格式** 对于 Excel 等复杂格式(如 `.xlsx` 或 `.xls`),C++ 标准库不支持直接读写,因为这些文件的内部结构复杂(包含元数据、多工作表等)。建议使用第三方库: - **推荐库**:如 LibXL 或 OpenXLSX,提供专用 API 读写 Excel 文件- **替代方案**:将 Excel 导出为 CSV 文件,然后用标准库读写 CSV(使用 `std::getline` 和字符串分割)[^2]。 - **二进制文件**:使用 `fstream` 的二进制模式(如 `std::ios::binary`),但需手动处理字节序列。 #### 4. **最佳实践** - **错误处理**:始终检查文件打开状态(`if (!file)`),避免程序崩溃。 - **资源管理**:使用 RAII(Resource Acquisition Is Initialization)原则,或在 `main` 结束时自动关闭文件- **性能优化**:对大文件使用缓冲区(如 `std::vector<char>`),减少 I/O 操作次数。 - **跨平台**:文件名使用相对路径(如 `"data.txt"`),确保在不同系统兼容。 掌握这些基础方法后,您可以高效处理文本文件读写任务。对于高级场景(如 Excel),结合第三方库是关键[^1][^2][^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值