快速追踪内存使用情况的方法

函数mtrace是linux中用于开启内存使用记录的函数接口,而函数muntrace是关闭内存使用记录的函数接口,另外环境变量MALLOC_TRACE则是决定内存使用记录是否记录到文件中。

#include <mcheck.h>
void mtrace(void);
void muntrace(void);

一、入门例子

1、首先给出简单的例子,初步了解函数的使用方法。

#include <mcheck.h>
#include <stdlib.h>

int main(int argc, char** argv)
{
    // 设置MALLOC_TRACE环境变量
    setenv("MALLOC_TRACE", "trace.log", 1);
    // 开启内存使用情况记录
    mtrace();
    return 0;
}

2、编译运行上面创建的cpp文件

3、运行成功之后,执行ls -l查看当前目录情况,可以看到生成了trace.log文件,其内容如下图所示, 文件以“= Start”开始。

二、没有内存泄露的情况

1、原来的例子中增加调用malloc申请内存,再调用free释放内存,最后再调用muntrace关闭内存使用情况记录

#include <mcheck.h>
#include <stdlib.h>

int main(int argc, char** argv)
{
    // 设置MALLOC_TRACE环境变量
    setenv("MALLOC_TRACE", "trace.log", 1);
    // 开启内存使用情况记录
    mtrace();

    char *p = (char *)malloc(10000);
    free(p);

    // 关闭内存使用情况记录
    muntrace();
    return 0;
}

2、编译上面的cpp文件,然后运行生成的可执行文件,再查看生成的trace.log文件,其内容如下所示,以“= Start”开始, 以“= End”结束, 其中+号代表申请内存,-号代表释放内存。从这个文件,我们可以发现,cpp文件最后调用muntrace函数之后,生成的tracel.log文件中的最后会以“= End”结束。

三、内存泄露的情况

1、这次在上面cpp文件的基础上,再次调用malloc申请内存,但是不释放内存

#include <mcheck.h>
#include <stdlib.h>

int main(int argc, char** argv)
{
    // 设置MALLOC_TRACE环境变量
    setenv("MALLOC_TRACE", "trace.log", 1);
    // 开启内存使用情况记录
    mtrace();

    char *p = (char *)malloc(10000);
    free(p);

    char *p2 = (char *)malloc(20000);

    // 关闭内存使用情况记录
    muntrace();
    return 0;
}

2、同样的编译cpp文件,然后运行可执行文件,查看生成的trace.log文件,从内容看,文件倒数第二行中申请了内存,但是没有释放内存。

四、转换为可理解的信息

从上面三个例子看,生成的trace.log的内容的可读性比较差,为了提高可读性,我们可以使用mtrace命令工具来解析trace.log文件,mtrace命令是glibc-utils的工具, 如果linux上没有mtrace命令,那么需要先下载安装glibc-utils。下面首先介绍基于fedora系统下下载安装glibc-utils的方法,最后再来介绍将trace.log转换为可读性的信息命令。

1、下载安装glibc-utils

首先终端执行命令yum install glib-utils,  然后会提示“Is this ok [y/N]:”,直接输入y,然后按下回车键。

如果安装成功的话,那么最后会出现“Complete!”的提示信息

2、将trace.log转换为可读性的信息,其格式为:mtrace 可执行文件  生成的记录内存文件, 如下图所示,终端执行mtrace  a.out trace.log之后,界面直接显示“Memory not freed:”表示内存没有释放,并且还指出文件的具体位置。

五、总结

mtrace()是linux上开启记录内存的函数接口,muntrace()是关闭记录内存的函数接口,MALLOC_TRACE是决定是否将记录内存情况写入文件的环境变量,mtrace命令是glibc-utils的工具,可以将记录内存情况的文件转换为可理解的信息。

读写excel文档入门讲解一

最近工作中需要将数据以各种格式存放到excel文档中,工程是基于C++,并且采用QT设计的界面,因此,通过网络搜索寻找到基于QT来读写excel文档的开源库QXlsx,本文将介绍开源库QXlsx的简单使用和效果,一方面提供入门知识,另一方面也给自己的学习成果做一个总结记录,方便后续查找复习。

一、简单的例子

1、 网上下载QXlsx开源库后,需要将QXlsx加载到Qt Creator工具,直接参考开源库代码中的HowToSetProject.md文件即可

2、展示写入数据,并且生成excel文档的例子

// 头文件
#include "xlsxdocument.h"
#include "xlsxformat.h"
#include "xlsxworkbook.h"

QXlsx::Document xlsx;
// 通过单元格名写入字符串
xlsx.write("A1", "Hello!");
// 通过行列方式写入字符串
xlsx.write(2, 1, "Everyone!");

std::string str_file_name = JPathHandler::GetDebugPath() + "excel_hello.xlsx";
// 生成excel文档
xlsx.saveAs(QString::fromStdString(str_file_name));

3、最后生成的excel文档的内容如下图所示

二、自动换行

有时候写入单元格的内容比较长,超过了单元格的展示范围,而我们又不想扩大单元格的宽度,怎么办呢,可以通过设置自动换行的方式,让写入单元格的内容自动适配当前的单元格。

1、携带配置好的格式写入到单元格

QXlsx::Document xlsx2;

// 初始化格式
QXlsx::Format format2;
format2.setFontSize(10);
format2.setHorizontalAlignment(QXlsx::Format::AlignHDistributed);

// 用上面的格式来初始化下面的单元格
xlsx2.write("A1", "onetwothree,onetwothree,onetwothree", format2);

// 最后生成excel文档(注意JPathHandler::GetDebugPath()自定义的类,用于获取路径,不是开源库的接口)
std::string str_file_name2 = JPathHandler::GetDebugPath() + "excel_align.xlsx";
xlsx2.saveAs(QString::fromStdString(str_file_name2));

2、最后生成的excel文档的内容效果如下图所示

三、多个表单

当需要多个表单来保存不同的数据的时候,这就涉及到创建多个表单的问题。那么将用创建两个表单的例子来作为简单的入门知识点。

1、生成两个表单,并且分别往表单中写入数据

QXlsx::Document xlsx3;

// 第一个表单
xlsx3.addSheet("first year");
xlsx3.write("A1", "first");

// 第二个表单
xlsx3.addSheet("second year");
xlsx3.write("A1", "second");

// 最后生成excel文档
std::string str_file_name3 = JPathHandler::GetDebugPath() + "excel_addsheet.xlsx";
xlsx3.saveAs(QString::fromStdString(str_file_name3));

2、最后生成的excel文档的内容效果如下图所示

四、读取excel文档

写入数据到excel文档之后,肯定有需求场景需要读取excel文档数据来展示,所以,接下来将说明读取excel文档的所有数据的方法。

1、先读取excel文档,然后获取每一个表单,读取每一个表单中的单元格内容

// 读取上一个步骤中生成的excel文档
QXlsx::Document xlsx4( QString::fromStdString(str_file_name3) );

int i_index_sheet = 0;
// 循环读取excel中的每一个表单,最后打印输出每个单元格的内容
foreach( QString current_sheetname, xlsx4.sheetNames() )
{
    QXlsx::AbstractSheet* p_current_sheet = xlsx4.sheet( current_sheetname );
    p_current_sheet->workbook()->setActiveSheet(i_index_sheet);
    QXlsx::Worksheet* wsheet = (QXlsx::Worksheet*) p_current_sheet->workbook()->activeSheet();

    int i_max_row = -1;
    int i_max_col = -1;
    QVector<QXlsx::CellLocation> clList = wsheet->getFullCells( &i_max_row, &i_max_col );
    for ( int i_cellindex = 0; i_cellindex < clList.size(); ++i_cellindex )
     {
        QXlsx::CellLocation cell_location = clList.at(i_cellindex);
        QVariant var = cell_location.cell.data()->value();
        QString str = var.toString();
        LOG(INFO)<< std::string( str.toLocal8Bit() );
    }
    i_index_sheet++;
}

2、最后输出的打印信息如下所示

[2019-10-20 21:09:48,039626] [bool JQtAttr::TestXlsx():135] first
[2019-10-20 21:09:48,039675] [bool JQtAttr::TestXlsx():135] second

五、总结

从上面的例子中,可以看出QXlsx::Document是控制整个excel文档的类,它可以控制表单的宽高,控制表单等功能,而QXlsx::Format则是控制单元格格式的类,它可以设置字体、字体大小,对齐方式等。

QXlsx的局限性,它不支持多线程,当表单比较多的时候,加载表单会很耗时。

参考链接:

Handling Microsoft Excel file format

QXlsx