其他分享
首页 > 其他分享> > QT 利用QAxObject大数据读写excel文件

QT 利用QAxObject大数据读写excel文件

作者:互联网

       不知不觉已经加入CSDN很久了,但从来没有发过文章,最近开发了一个小工具踩了很多坑,主要是QT操作大数据Excel文件,现在和大家分享一下。

        查阅了大部分资料,大数据读写excel文件用QAxObject对象最快,借鉴了一些读写excel文件的demo,所以本文也采用了这种方法。

         对于代码的解释,大部分我都写在注释里了,有不明白或者建议的话,欢迎大家提出来。

         话不多说,上代码。

 

完整项目代码GitHub地址:https://github.com/ChinaSweetMilk/qtexcel

读写单元格时,常用以下这种方法:

// 根据行号、列号读取某个单元格内容
QAxObject* pCell = m_pWorksheet->querySubObject("Cells(int, int)", iirow, icolumn);
QString    strCell = pCell->property("Value").toString();

// 根据行列值读取某个单元格内容
QAxObject* pCell = pWorksheet->querySubObject("Range(QString)", strnumber);
QString    strCell = pCell->property("Value").toString();


// 根据行号、列号写入某个单元格内容
QAxObject* pCell = m_pWorksheet->querySubObject("Cells(int, int)", irow, icolumn);
QString    pCell->setProperty("Value", strvalue);

// 根据行列值读取某个单元格内容
QAxObject* pCell = m_pWorksheet->querySubObject("Range(QString)", strnumber);
QString    pCell->setProperty("Value", strvalue);

读取单个单元格内容时,可以使用以上方法,一秒大概能读写200行左右。但是需要大量读写单元格时,以上方法将会变得十分缓慢,所以这时需要用到QVariant,一次读取或写入整个工作表。采用以下方法,能将效率大大提高,一万行大概500毫秒。这个版本还没采用多线程读写,后面加入效率还可以提高。

 

以下是一次读取工作表全部内容的方法:

void ExcelOperator::readAll(QList<QList<QVariant> > &res)
{
    QVariant var;

    if (m_pWorksheet != NULL && ! m_pWorksheet->isNull())
    {
        // 获取该sheet的数据范围
        QAxObject * ax_usedRange = m_pWorksheet->querySubObject("UsedRange");
        if(NULL == ax_usedRange || ax_usedRange->isNull())
        {
            return;
        }

        // 获取该sheet的数据内容
        var = ax_usedRange->dynamicCall("Value");
        delete ax_usedRange;
    }

    this->castVariant2ListListVariant(var, res);

    return;
}

void ExcelOperator::castVariant2ListListVariant(const QVariant &var, QList<QList<QVariant> > &res)
{
    QVariantList varRows = var.toList();

    if(varRows.isEmpty())
    {
        return;
    }

    const int rowCount = varRows.size();
    QVariantList rowData;

    for(int i=0;i<rowCount;++i)
    {
        rowData = varRows[i].toList();
        res.push_back(rowData);
    }
}
以下是一次写入工作表全部内容的方法:
bool ExcelOperator::writeCurrentSheet(const QList<QList<QVariant> > &cells)
{
    if(cells.size() <= 0)
        return false;

    if(NULL == this->m_pWorksheet || this->m_pWorksheet->isNull())
        return false;

    // 获取写入内容行数
    int irow = cells.size();
    // 获取写入内容列数
    int icol = cells.at(0).size();

    QString strRang;
    // 获取写入矩形内容最右边列标识
    convertToColName(icol, strRang);
    // 获取写入矩形内容最右边右下角单元格标识
    strRang += QString::number(irow);
    // 获取写入矩形内容写入范围
    strRang = "A2:" + strRang;

    qDebug()<< strRang;
    QAxObject *range = this->m_pWorksheet->querySubObject("Range(const QString&)", strRang);

    if(NULL == range || range->isNull())
    {
        return false;
    }

    bool succ = false;

    QVariant var;
    castListListVariant2Variant(cells,var);

    // 将数据写入到sheet
    succ = range->setProperty("Value", var);

    delete range;
    return succ;
}

void ExcelOperator::castListListVariant2Variant(const QList<QList<QVariant> > &listcells, QVariant &varres)
{
    QVariantList vars;
    const int rows = listcells.size();
    for(int i = 0; i < rows; ++i)
    {
        vars.append(QVariant(listcells[i]));
    }
    varres = QVariant(vars);
}

void ExcelOperator::convertToColName(int idata, QString &strres)
{
    Q_ASSERT(idata > 0 && idata<65535);
    int tempData = idata / 26;
    if(tempData > 0)
    {
        int mode = idata % 26;
        convertToColName(mode, strres);
        convertToColName(tempData, strres);
    }
    else
    {
        strres = (to26AlphabetString(idata)+ strres);
    }
}

// 数字转换为26字母
QString ExcelOperator::to26AlphabetString(int idata)
{
    QChar ch = idata + 0x40; // A对应0x41
    return QString(ch);
}

 

一、简单应用demo
ExcelOperator * pexceloperator = new ExcelOperator;
QString strfilename = QFileDialog::getOpenFileName(this, tr("选择配置文件!"),
                                                          ".",
                                                          tr("excel文件(*.xlsx)"));
    
qDebug()<< "filename : " << QDir::toNativeSeparators(strfilename);

pexceloperator->open(QDir::toNativeSeparators(strfilename));

if(!pexceloperator->setSheet(1))
{
    QMessageBox::warning(this,QString(tr("错误")),
                              QString(tr("读取文件失败!")));
}
else
{
    QMessageBox::information(NULL, "提示","成功读取文件", QMessageBox::Yes);
}

QList<QList<QVariant> > listsheet;

// 读取sheet1全部内容
this->pexceloperator->readAll(listsheet);


QList< QList<QVariant> > listdatas;

for(int iEquipmentPos = 0; iEquipmentPos < 10000; iEquipmentPos++)
{
    // 当前行内容
    QList<QVariant> listrows;

    for(unsigned int iPosRows = 0; iPosRows < 100; iPosRows++)
    {
        // setCell(irowpos, 1, iEquipmentPos * iPosRows);

        listrows.append(iEquipmentPos * iPosRows);
    }

    // 将这行内容添加到列表
    listIDdatas.append(listrows);
}


// 将所有内容写入到sheet文件
this->writeCurrentSheet(listdatas);

// 保存并关闭工作表
pexceloperator->close();

delete pexceloperator;
二、excelOperator.h
#ifndef EXCELOPERATOR_H
#define EXCELOPERATOR_H

#include <QObject>
#include <ActiveQt/QAxObject>
#include <QDebug>
#include <QDir>
#include <QMap>
#include "equipment.h"

class ExcelOperator : public QObject
{
    Q_OBJECT
public:
    explicit ExcelOperator(QObject *parent = nullptr);
    ~ExcelOperator();
    //打开文件
    bool open(QString strpath);

    //关闭文件
    bool close();

    // 获取工作表数量
    int getSheetsCount();

    // 获取工作表路径
    QString getPath();

    // 根据名称创建工作表
    bool addSheet(QString strname);

    // 根据名称删除工作表
    bool delSheet(QString strname);

    //根据编号删除工作表
    bool delSheet(int iindex);

    // 根据名称设置工作表
    bool setSheet(QString strname);

    // 根据序号设置工作表
    bool setSheet(int iindex);

    //根据名称获取工作表
    QAxObject* getSheet(QString strname);

    //根据编号获取工作表
    QAxObject* getSheet(int iindex);

    //获取行对象
    QAxObject* getRows();

    //获取行数
    int getRowsCount();

    //获取列对象
    QAxObject* getColumns();

    //获取列数
    int getColumnsCount();

    //根据行列值获取单元格值, 如: 3行,5列
    QString getCell(int irow, int icolumn);

    //根据行列编号获取单元格值, 如: "F6"
    QString getCell(QString strnumber);

    //根据行列值设置单元格值
    bool setCell(int irow, int icolumn, QString strvalue);

    //根据行列编号设置单元格值
    bool setCell(QString strnumber, QString strvalue);

    // 一次读取sheet的全部内容
    void readAll(QList<QList<QVariant> > &res);

    // 一次写入sheet的全部内容
    bool writeCurrentSheet(const QList<QList<QVariant> > &cells);

    // 把QVariant转为QList<QList<QVariant>>
    void castVariant2ListListVariant(const QVariant &var, QList<QList<QVariant> > &res);

    // 把QList<QList<QVariant>>转为QVariant
    void castListListVariant2Variant(const QList<QList<QVariant> > &cells, QVariant &res);

    // 把列数转换为excel的字母列号
    void convertToColName(int idata, QString &res);

    // 数字转换为26字母
    QString to26AlphabetString(int idata);

signals:

public slots:
private:
    QAxObject*      m_pExcel;
    QAxObject*      m_pWorkbooks;
    QAxObject*      m_pWorkbook;
    QAxObject*      m_pWorksheets;
    QAxObject*      m_pWorksheet;
    QString         m_strPath;
};
#endif // EXCELOPERATOR_H
三、excelOperator.cpp
#include "exceloperator.h"
#include <objbase.h>
#include <QDebug>

ExcelOperator::ExcelOperator(QObject *parent) : QObject(parent)
  , m_pExcel(NULL)
  , m_pWorkbooks(NULL)
  , m_pWorkbook(NULL)
  , m_pWorksheets(NULL)
  , m_pWorksheet(NULL)
{
    m_pExcel = new(std::nothrow) QAxObject();
    m_pExcel->setControl("Excel.Application");
    m_pExcel->dynamicCall("SetVisible(bool)", false); // true 表示操作文件时可见,false表示为不可见
    m_pExcel->setProperty("DisplayAlerts", false); // 设置不显示任何警告信息
    m_pWorkbooks = m_pExcel->querySubObject("WorkBooks");
}

ExcelOperator::~ExcelOperator()
{
//    close();
    m_pExcel->dynamicCall("Quit()");
    delete m_pExcel;
    m_pExcel = NULL;
}

bool ExcelOperator::open(QString strpath)
{
    m_strPath = strpath;
    CoInitializeEx(NULL, COINIT_MULTITHREADED); // 为当前线程初始化COM库并设置并发模式
    if (NULL == m_pExcel)
    {
        qCritical()<<"创建Excel对象失败...";
        return false;
    }
    try{
        // 按文件路径打开文件
        m_pWorkbook = m_pWorkbooks->querySubObject("Open(QString&)", m_strPath);

        // 获取打开的excel文件中所有的工作sheet
        m_pWorksheets = m_pWorkbook->querySubObject("WorkSheets");
    } catch (...){
        qCritical()<<"打开文件失败...";
        return false;
    }
    return true;
}

bool ExcelOperator::close()
{
    qDebug()<<"excel close...";
    if (m_pExcel)
    {
        qDebug()<<"closing...";
        QDir::toNativeSeparators(m_strPath));
        m_pWorkbook->dynamicCall("Save()");
        m_pWorkbook->dynamicCall("Close()");
    }

    return true;
}

int ExcelOperator::getSheetsCount()
{
    int icount =  0;
    icount = m_pWorksheets->property("Count").toInt();
    return icount;
}

QString ExcelOperator::getPath()
{
    return m_strPath;
}

bool ExcelOperator::addSheet(QString strname)
{
    QAxObject *pWorkSheet = NULL;
    try{
        int icount = m_pWorksheets->property("Count").toInt();  //获取工作表数目

        QAxObject *pLastSheet = m_pWorksheets->querySubObject("Item(int)", icount);
        pWorkSheet = m_pWorksheets->querySubObject("Add(QVariant)", pLastSheet->asVariant());
        pLastSheet->dynamicCall("Move(QVariant)", pWorkSheet->asVariant());
        pWorkSheet->setProperty("Name", strname);  //设置工作表名称
        m_pWorkbook->dynamicCall("Save()");

        delete pWorkSheet;
        pWorkSheet = NULL;
    }
    catch (...){
        qCritical()<<"创建sheet失败...";
        return false;
    }

    if(pWorkSheet)
    {
        delete pWorkSheet;
        pWorkSheet = NULL;
    }
    return true;
}

bool ExcelOperator::delSheet(QString strname)
{
    try {
        QAxObject *pFirstSheet = m_pWorksheets->querySubObject("Item(QString)", strname);
        pFirstSheet->dynamicCall("delete");
    } catch (...) {
        qCritical()<<"删除sheet失败...";
        return false;
    }
    return true;
}

bool ExcelOperator::delSheet(int iindex)
{
    try {
        QAxObject *pFirstSheet = m_pWorksheets->querySubObject("Item(int)", iindex);
        pFirstSheet->dynamicCall("delete");
    } catch (...) {
        qCritical()<<"删除sheet失败...";
        return false;
    }
    return true;
}

bool ExcelOperator::setSheet(QString strname)
{
    try {
        m_pWorksheet = m_pWorksheet->querySubObject("Item(QString)", strname);
    } catch (...) {
        qCritical()<<"获取sheet失败...";
        return false;
    }
    return true;
}

bool ExcelOperator::setSheet(int iindex)
{
    // 获取当前工作簿的表数量
    int isheetcounts = this->getSheetsCount();

    // 如果要设置的表不在当前工作簿的范围,则创建该表
    if(iindex > isheetcounts)
    {
        QString strsheetname = "Sheet" + QString::number(iindex);
        if(!this->addSheet(strsheetname))
        {
            return false;
        }
    }

    try {
        m_pWorksheet = m_pWorksheets->querySubObject("Item(int)", iindex);
    } catch (...) {
        qCritical()<<"获取sheet失败...";
        return false;
    }
    return true;
}

QAxObject* ExcelOperator::getSheet(QString strname)
{
    QAxObject* pWorkSheet = NULL;
    try {
        pWorkSheet = m_pWorksheets->querySubObject("Item(QString)", strname);
    } catch (...) {
        qCritical()<<"获取sheet失败...";
    }
    return pWorkSheet;
}

QAxObject* ExcelOperator::getSheet(int iindex)
{
    QAxObject* pWorkSheet = NULL;
    try {
        pWorkSheet = m_pWorksheets->querySubObject("Item(int)", iindex);
    } catch (...) {
        qCritical()<<"获取sheet失败...";
    }
    return pWorkSheet;
}

QAxObject* ExcelOperator::getRows()
{
    QAxObject* pRows = NULL;
    try {
        QAxObject* pUsedRange = m_pWorksheet->querySubObject("UsedRange");
        pRows = pUsedRange->querySubObject("Rows");
    } catch (...) {
        qCritical()<<"获取行失败...";
    }
    return pRows;
}

int ExcelOperator::getRowsCount()
{
    int irows = 0;
    try {
        QAxObject* pUsedRange = m_pWorksheet->querySubObject("UsedRange");
        QAxObject* pRows = pUsedRange->querySubObject("Rows");
        irows = pRows->property("Count").toInt();
    } catch (...) {
        qCritical()<<"获取行数失败...";
        return 0;
    }
    return irows;
}

QAxObject* ExcelOperator::getColumns()
{
    QAxObject* pColumns = NULL;
    try {
        QAxObject* pUsedRange = m_pWorksheet->querySubObject("UsedRange");
        pColumns = pUsedRange->querySubObject("Columns");
    } catch (...) {
        qCritical()<<"获取列失败...";
    }
    return pColumns;
}

int ExcelOperator::getColumnsCount()
{
    int icolumns = 0;
    try {
        QAxObject* pUsedRange = m_pWorksheet->querySubObject("UsedRange");
        QAxObject* pColumns = pUsedRange->querySubObject("Columns");;
        icolumns = pColumns->property("Count").toInt();
    } catch (...) {
        qCritical()<<"获取列数失败...";
        return 0;
    }
    return icolumns;
}

QString ExcelOperator::getCell(int iirow, int icolumn)
{
    QString strCell = "";
    try {
        QAxObject* pCell = m_pWorksheet->querySubObject("Cells(int, int)", iirow, icolumn);
        strCell = pCell->property("Value").toString();
    } catch (...) {
        qCritical()<<"获取单元格信息失败...";
    }

    return strCell;
}

QString ExcelOperator::getCell(QString strnumber)
{
    QString strCell = "";
    try {
        QAxObject* pCell = m_pWorksheet->querySubObject("Range(QString)", strnumber);
        strCell = pCell->property("Value").toString();
    } catch (...) {
        qCritical()<<"获取单元格信息失败...";
    }

    return strCell;
}

bool ExcelOperator::setCell(int irow, int icolumn, QString strvalue)
{
    try {
        QAxObject* pCell = m_pWorksheet->querySubObject("Cells(int, int)", irow, icolumn);
        pCell->setProperty("Value", strvalue);
    } catch (...) {
        qCritical()<<"写入单元格信息失败...";
        return false;
    }
    return true;
}

bool ExcelOperator::setCell(QString strnumber, QString strvalue)
{
    try {
        QAxObject* pCell = m_pWorksheet->querySubObject("Range(QString)", strnumber);
        pCell->setProperty("Value", strvalue);
    } catch (...) {
        qCritical()<<"写入单元格信息失败...";
        return false;
    }
    return true;
}

void ExcelOperator::readAll(QList<QList<QVariant> > &res)
{
    QVariant var;

    if (m_pWorksheet != NULL && ! m_pWorksheet->isNull())
    {
        // 获取该sheet的数据范围
        QAxObject * ax_usedRange = m_pWorksheet->querySubObject("UsedRange");
        if(NULL == ax_usedRange || ax_usedRange->isNull())
        {
            return;
        }

        // 获取该sheet的数据内容
        var = ax_usedRange->dynamicCall("Value");
        delete ax_usedRange;
    }

    this->castVariant2ListListVariant(var, res);

    return;
}

bool ExcelOperator::writeCurrentSheet(const QList<QList<QVariant> > &cells)
{
    if(cells.size() <= 0)
        return false;

    if(NULL == this->m_pWorksheet || this->m_pWorksheet->isNull())
        return false;

    // 获取写入内容行数
    int irow = cells.size();
    // 获取写入内容列数
    int icol = cells.at(0).size();

    QString strRang;
    // 获取写入矩形内容最右边列标识
    this->convertToColName(icol, strRang);
    // 获取写入矩形内容最右边右下角单元格标识
    strRang += QString::number(irow);
    // 获取写入矩形内容写入范围
    strRang = "A2:" + strRang;

    qDebug()<< strRang;
    QAxObject *range = this->m_pWorksheet->querySubObject("Range(const QString&)", strRang);

    if(NULL == range || range->isNull())
    {
        return false;
    }

    bool succ = false;

    QVariant var;
    this->castListListVariant2Variant(cells, var);

    // 将数据写入到sheet
    succ = range->setProperty("Value", var);

    delete range;
    return succ;
}

void ExcelOperator::castVariant2ListListVariant(const QVariant &var, QList<QList<QVariant> > &res)
{
    QVariantList varRows = var.toList();

    if(varRows.isEmpty())
    {
        return;
    }

    const int rowCount = varRows.size();
    QVariantList rowData;

    for(int i=0;i<rowCount;++i)
    {
        rowData = varRows[i].toList();
        res.push_back(rowData);
    }
}

void ExcelOperator::castListListVariant2Variant(const QList<QList<QVariant> > &listcells, QVariant &varres)
{
    QVariantList vars;
    const int rows = listcells.size();
    for(int i = 0; i < rows; ++i)
    {
        vars.append(QVariant(listcells[i]));
    }
    varres = QVariant(vars);
}

void ExcelOperator::convertToColName(int idata, QString &strres)
{
    Q_ASSERT(idata > 0 && idata<65535);
    int tempData = idata / 26;
    if(tempData > 0)
    {
        int mode = idata % 26;
        this->convertToColName(mode, strres);
        this->convertToColName(tempData, strres);
    }
    else
    {
        strres = (to26AlphabetString(idata)+ strres);
    }
}

// 数字转换为26字母
QString ExcelOperator::to26AlphabetString(int idata)
{
    QChar ch = idata + 0x40; // A对应0x41
    return QString(ch);
}

 

 

标签:ExcelOperator,QT,int,querySubObject,excel,QAxObject,QString,QVariant
来源: https://blog.csdn.net/diaonimagebikao/article/details/110672058