时过境迁,时隔多个月之后,重新总结一些东西,工作是为生活。
C++读取Excel文件的方法有很多,但是也许就是因为方法太多,大家在选择的时候会很疑惑。
对于多种不同方法的区别和介绍,网上都比较详细。比如参考 https://www.cnblogs.com/whwywzhj/p/9176833.html
而并未过多深入研究。
一、一般我们只是进行简单的读写操作,可以通过C语言读写程序来实现
第一步:单纯C语言写入Excel文件只能是 *.csv的后缀文件(是和txt一样,以二进制文本形式存储,它是以都逗号分隔符做个单元格内容的划分, .xls存储比较复杂, .csv文件可以可以通过.xls或者.xlsx文件另存为,选择.csv文件格式),它们可以通过Notepad++等记事本软件当做txt文件打开。
需要注意的是:当对*.xls文件写入或者读取之后,再打开Excel文件时会弹出格式兼容的提示窗口,因为这样的C语言操作Excel文件是当文本文件打开操作的,所以会忽略原有格式,但是不影响,点击“是(Y)”即可,如下图所示:
第二步:对表格的处理,使用C语言打开表格后,文件指针指向整个表格的第1行第1列。
如果要给它的下一个同行单元格(第1行第2列)写数据,使用"\t" ;
如果要给它的下一个同列单元格(第2行第1列)写数据,使用"\n" 。
void writeExcel()
{
char chy[4]={ 'x' ,'a' ,'h','w' } ;
int data[4]={ 1 , 3 , 6 ,9 };
int i ;
FILE *fp = NULL ;
fp = fopen("G:\\Desktop\\test.csv","w") ;
for (i=0 ; i<4 ;i++)
fprintf(fp,"%c\t%d\n",chy[i],data[i] ) ;
fclose(fp);
}
void main()
{
writeExcel() ;
}
这些都是从网上的大佬那里偷学而来,当然有一些自己的总结,嘻嘻。下面是我实际使用的代码。
void writeExcel()
{
int data[4]={ 1 , 3 , 6 ,9 };
ofstream outfile_final_result;
outfile_final_result.open("D:/测量结果.txt", ios::in | ios::trunc);
outfile_final_result << "序号" << ',' << "结果" << endl; //第一行
for (i=0 ; i<4 ;i++)
{
outfile_final_result << i + 1 << ',' << data[i] << endl;
}
outfile_final_result.close();
}
void main()
{
writeExcel() ;
}
这里我使用了 << ‘,’ << 来代替 \t ,用 << endl; 来代替 换行。这些都看个人喜欢。
缺点:单元格内的内容不能太长,不能自适应,不能居中等操作,只适用于简单的数据存储,可以理解为txt文件。
二、其中在 MFC 中操作 Office 通过 vs 导入 OLE/COM 组件来实现对 Office 的操作,但是操作稍微复杂些,下面就针对这种方法来介绍:
环境:
Windows 10 (64位)
Microsoft Visual Studio 2015
Office 2016 (64位)
步骤:
1、安装 Office 2016
由于是用 vs 导入 Office 的 OLE/COM 组件来进行读写操作,所以必须先安装 Office。
2、创建一个空的 MFC 对话框项目
3、导入操作 Excel 的几个基本类
右击项目,选择“类向导”;
“添加类” --> "类型库中的MFC类"
选择 Excel 的 OLE/COM 组件,默认在 C 盘,除非你修改了安装目录。【还有另一种方式可以弹出这个界面:右击项目–>添加 --> 类 --> MFC --> ActiveX 控件中的 MFC 类】
从左侧选择接口 _Application、Workbooks、_Workbook、Worksheets、_Worksheet、Range、Shapes、Shape、ShapeRange、_Font、_nterior ,他们分别对应的类是 CApplication、CWorkbooks、CWorkbook、CWorksheets、CWorksheet、CRange、CShapes、CShape、CShapeRange、CFont0、Cnterior。然后就可以看到导入的这些类。
当然也可以直接copy别的号的项目的文件就行。
CWorkbooks:工作薄的容器;
CWorkbook:单个工作薄;
CWorksheets:单个工作簿中的Sheet表格的容器;
CWorksheet:单个Sheet表格;
CRange:单个或多个单元格;
CShapes:形状容器;
CShape:形状;
CShapeRange:形状所占的单元格范围;
4、解决导入 Excel 接口存在的问题
将每个类头文件中的 "#import "C:\\Program Files (x86)\\Microsoft Office\\Office15\\EXCEL.EXE" no_namespace"注释掉。
将头文件 CRange.h 中的 DialogBox 前面添加下划线 _DialogBox 即可解决问题,修改后如下所示。
VARIANT _DialogBox()
{
VARIANT result;
InvokeHelper(0xf5, DISPATCH_METHOD, VT_VARIANT, (void*)&result, NULL);
return result;
}
5、导入头文件和类型库
#pragma once
#include "CApplication.h"
#include "CWorkbook.h"
#include "CWorkbooks.h"
#include "CWorksheet.h"
#include "CWorksheets.h"
#include "CRange.h"
#include "CFont0.h"
#include "Cnterior.h"
#include "CShape.h"
#include "CShapes.h"
#include "CShapeRange.h"
#include <string>
#include <sstream>
using namespace std;
头文件定义变量:
CApplication m_ExcelApp;
CWorkbook m_ExcelBook;
CWorkbooks m_ExcelBooks;
CWorksheet m_ExcelSheet;
CWorksheets m_ExcelSheets;
CRange m_ExcelRange;
CRange m_ExcelCols;
CFont0 m_ExcelFont;
Cnterior current_interior;
CString m_openFilePath;
CString m_saveFilePath;
6、操作 Excel 步骤
(1)创建一个Excel应用程序。
(2)得到Workbook的容器。
(3)打开一个Workbook或者创建一个Workbook。
(4)得到Workbook中的Worksheet的容器。
(5)打开一个Worksheet或者创建一个WorkSheet。
(6)通过Range对WorkSheet中的单元格进行读写操作。
(7)如果导入图片,通过Shape来操作。
(8)保存Excel。
(9)释放资源。
//创建execl表格
void ExcelRW::OpenTable(CString OpenPath)
{
m_openFilePath = OpenPath;
if (!m_ExcelApp.CreateDispatch(_T("Excel.Application")))
{
AfxMessageBox(_T("居然你连OFFICE都没有安装吗?"), MB_OK | MB_ICONWARNING);
return;
}
m_ExcelApp.put_Visible(FALSE); //是否显示execl
m_ExcelApp.put_UserControl(FALSE);
m_ExcelApp.put_DisplayFullScreen(FALSE);//设置全屏显示
m_ExcelApp.put_DisplayAlerts(FALSE);//屏蔽警告
/*打开工作簿*/
m_ExcelBooks = m_ExcelApp.get_Workbooks();
try
{
m_ExcelBook = m_ExcelBooks.Add(_variant_t(m_openFilePath));//打开execl文件
}
catch (CException* e)
{
m_ExcelBook = m_ExcelBooks.Add(vtMissing);//找不到就新建一个
}
/*得到工作薄中1Sheet容器*/
m_ExcelSheets.AttachDispatch(m_ExcelBook.get_Worksheets());
//m_ExcelSheet = m_ExcelBook.get_ActiveSheet();//获取当前工作表
m_ExcelSheet = m_ExcelSheets.get_Item(COleVariant((short)1));//获取第一个工作表
}
//关闭execl
void ExcelRW::CloseTable(CString strSavePath)
{
m_saveFilePath=strSavePath;
m_ExcelBook.SaveCopyAs(COleVariant(m_saveFilePath));
m_ExcelBook.put_Saved(TRUE);
// 释放对象
m_ExcelBooks.ReleaseDispatch();
m_ExcelBook.ReleaseDispatch();
m_ExcelSheets.ReleaseDispatch();
m_ExcelSheet.ReleaseDispatch();
m_ExcelRange.ReleaseDispatch();
m_ExcelApp.Quit();
m_ExcelApp.ReleaseDispatch();
}
//写数据到表格单元,clocow为表格位置(比如“A5”),strWrite为要写入的字符
void ExcelRW::WriteTable(CString clocow,CString strWrite)
{
m_ExcelRange = m_ExcelSheet.get_Range(COleVariant(clocow), COleVariant(clocow));
m_ExcelRange.put_Value2(COleVariant(strWrite));
//选择整列,并设置宽度为自适应
m_ExcelCols = m_ExcelRange.get_EntireColumn();
m_ExcelCols.AutoFit();
}
但在这里感觉还是不方便,所以做了一些优化。
//获得单元格的名称,比如 2行3列= C2
string ExcelRW::GetCellName(int irow, int icol)
{
//将int类型的irow转换为string类型的srow
string srow;
string scol;
string result;
stringstream sstemp;
sstemp << irow;
sstemp >> srow;
//当列数小于等于26时 返回值为单个字母+数字
if (icol <= 26 && icol > 0)
{
char ctemp = 'A';
ctemp = 'A' + icol - 1;
result = ctemp + srow;
return result;
}
//目前只考虑列数范围在ZZ以内
//当列数大于26时 返回值为两个字母+数字
else if (icol > 26)
{
//ctemp1 表示第一个字母
char ctemp1 = 'A';
//ctemp2 表示第二个字母
char ctemp2 = 'A';
int itemp1 = (icol - 1) / 26 - 1;
int itemp2 = icol % 26 - 1;
if (itemp2 == -1)
{
itemp2 = 25;
}
ctemp1 = 'A' + itemp1;
ctemp2 = 'A' + itemp2;
//将字符类型转换为字符串类型
stringstream sstemp1;
stringstream sstemp2;
sstemp1 << ctemp1;
sstemp2 << ctemp2;
scol = sstemp1.str() + sstemp2.str();
result = scol + srow;
return result;
}
}
//写入一个Cell中一个int型数据
BOOL ExcelRW::SetCellInt(int irow, int icol, int new_int)
{
if (irow > 0 && icol > 0)
{
string sint;
stringstream sstemp;
sstemp << new_int;
sstemp >> sint;
m_ExcelRange.put_Item(COleVariant(long(irow)), COleVariant(long(icol)), COleVariant(sint.c_str()));
return TRUE;
}
else
{ia
return FALSE;
}
}
//写入一个Cell中一个string型数据
BOOL ExcelRW::SetCellString(int irow, int icol, string new_string)
{
if (irow > 0 && icol > 0)
{
m_ExcelRange.put_Item(COleVariant(long(irow)), COleVariant(long(icol)), COleVariant(new_string.c_str()));
return TRUE;
}
else
{
return FALSE;
}
}
//写入一个Cell中一个double型数据
BOOL ExcelRW::SetCellDouble(int irow, int icol, double new_double)
{
if (irow > 0 && icol > 0)
{
string sdouble;
stringstream sstemp;
sstemp << new_double;
sstemp >> sdouble;
m_ExcelRange.put_Item(COleVariant(long(irow)), COleVariant(long(icol)), COleVariant(sdouble.c_str()));
return TRUE;
}
else
{
return FALSE;
}
}
还有一些常用的函数,后续函数可以一起补充,都是很简单的。
//设置单个单元格的颜色
void ExcelRW::SetCellColor(int irow, int icol, int red, int green, int blue)
{
m_ExcelRange = m_ExcelSheet.get_Range(COleVariant(GetCellName(irow, icol).c_str()), COleVariant(GetCellName(irow, icol).c_str()));
m_ExcelFont.AttachDispatch(m_ExcelRange.get_Font());
m_ExcelFont.put_Color(_variant_t(RGB(red, green, blue)));
}
//设置单个单元格的背景颜色
//icolor<=56 icolor(常用) = 1黑 /2白 /3红 /4绿 /5蓝 /6黄 /7紫 /8青
void ExcelRW::SetCellBackground(int irow, int icol, int icolor)
{
m_ExcelRange = m_ExcelSheet.get_Range(COleVariant(GetCellName(irow, icol).c_str()), COleVariant(GetCellName(irow, icol).c_str()));
current_interior.AttachDispatch(m_ExcelRange.get_Interior());
current_interior.put_ColorIndex(_variant_t(icolor));
}
//设置单个单元格的字体风格,比如 "宋体"、"华文行楷"
void ExcelRW::SetCellFont(int irow, int icol, const char *font)
{
m_ExcelRange = m_ExcelSheet.get_Range(COleVariant(GetCellName(irow, icol).c_str()), COleVariant(GetCellName(irow, icol).c_str()));
m_ExcelFont.AttachDispatch(m_ExcelRange.get_Font());
m_ExcelFont.put_Name(_variant_t(font));
}
7、载入图像数据
//向已存在的Excel中导入图片,放在固定位置
void ExcelRW::ImportImage2Excel(CString imagePath, int startIndex_irow, int startIndex_icol, int endIndex_irow, int endIndex_icol)
{
//获取图像插入的范围
//从Sheet对象上获得一个Shapes
CShapes pShapes;
pShapes.AttachDispatch(m_ExcelSheet.get_Shapes());
//获得Range对象,用来插入图片
CRange range = m_ExcelSheet.get_Range(COleVariant(GetCellName(startIndex_irow, startIndex_icol).c_str()), COleVariant(GetCellName(endIndex_irow, endIndex_icol).c_str()));
VARIANT rLeft = range.get_Left();
VARIANT rTop = range.get_Top();
VARIANT rWidth = range.get_Width();
VARIANT rHeight = range.get_Height();
long msoFalse = 1, msoTrue = 1;
//添加图像到 H1 - K10范围区域
CShape pShape = pShapes.AddPicture(imagePath, msoFalse, msoTrue,
(float)rLeft.dblVal, (float)rTop.dblVal, (float)rWidth.dblVal, (float)rHeight.dblVal);
//设置图像所占的宽高
CShapeRange shapeRange = pShapes.get_Range(_variant_t(long(1)));
shapeRange.put_Height((float)rHeight.dblVal);
shapeRange.put_Width((float)rWidth.dblVal);
}
后续包括合并单元格,设置边框等等操作。学无止境。
另外参考博主。
例子在这:https://download.csdn.net/download/cao_jie_xin/13101573