1.CSV文件格式简介
逗号分隔值(Comma-SeparatedValues,CSV,有时也称为字符分隔值,因为分隔字符也可以不是逗号),其文件以纯文本形式存储表格数据(数字和文本)。纯文本意味着该文件是一个字符序列,不含必须像二进制数字那样被解读的数据。CSV文件由任意数目的记录组成,记录间以某种换行符分隔;每条记录由字段组成,字段间的分隔符是其它字符或字符串,最常见的是逗号或制表符。通常,所有记录都有完全相同的字段序列。
CSV文件格式的通用标准并不存在,但是在RFC 4180中有基础性的描述。使用的字符编码同样没有被指定,但是7-bitASCII是最基本的通用编码。
一般情况下,CSV文件格式规则如下:
1. 开头是不留空,以行为单位。
2. 可含或不含列名,含列名则居文件第一行。
3. 一行数据不跨行,无空行。
4. 以半角逗号(即,)作分隔符,列为空也要表达其存在。
5. 列内容如存在半角逗号(即,)则用半角双引号(即"")将该字段值包含起来。
6. 列内容如存在半角引号(即")则应替换成半角双引号("")转义,并用半角引号(即"")将该字段值包含起来。
7. 文件读写时引号,逗号操作规则互逆。
8. 内码格式不限,可为 ASCII、Unicode 或者其他。
9. 不支持特殊字符
2. Mat矩阵数据存储到CSV文件中
博文“C++读写CSV文件”提出了一种CSV文件的读取遍历算法和写入算法,http://www.cnblogs.com/snake-hand/p/3170483.html,并利用C++实现了对CSV文件的读、写操作。
本文结合OpenCV的CSV风格格式化输出与流缓冲重定向,比较巧妙地实现了“将Mat 矩阵数据存储到CSV文件”功能。
代码如下:
//保存cout流缓冲区指针
streambuf *coutBuf = cout.rdbuf();
fstream matData("E:\\Test\\data\\fire_2.csv",ios::out|ios::trunc);
if(!matData){
cerr<<"File open or create error!"<<endl;
exit(1);
}
//获取文件fire.csv的流缓冲区指针
streambuf *fileBuf = matData.rdbuf();
//设置cout流缓冲区指针为文件的流缓冲区指针
cout.rdbuf(fileBuf);
cout<<format(svmMat,"csv");
matData.flush();
matData.close();
//恢复cout原来的流缓冲区指针
cout.rdbuf(coutBuf);
测试结果:
3.从CSV文件读取数据到Mat 矩阵
以下代码为我自己写的read_csv函数,实现了从CSV文件读取数据到Mat矩阵中的功能。
/**
*函数功能:将csv文件数据提取到Mat类型矩阵中
*输入:filepath 文件路径数组指针;img_size Mat类型数据的Size;img_type Mat类型数据的类型(32FC1)
*返回值:Mat 矩阵
*/
Mat read_csv(const char *filepath, Size img_size, int img_type)
{
Mat image;
image.create(img_size,img_type);
string pixel;
ifstream file(filepath, ifstream::in);
if (!file)
cout << "CSV read fail" << endl;
int nl= image.rows; // number of lines
int nc= image.cols ; // number of columns
int eolElem = image.cols - 1; //每行最后一个元素的下标
int elemCount = 0;
if (image.isContinuous())
{
nc= nc*nl; // then no padded pixels
nl= 1; // it is now a 1D array
}
for (int i = 0; i<nl; i++)
{
float* data = (float*)image.ptr<ushort>(i);
for (int j = 0; j < nc; j++)
{
if(elemCount == eolElem){
getline(file,pixel,'\n'); //任意地读入,直到读到delim字符 '\n',delim字符不会被放入buffer中
data[j] = (float)atof(pixel.c_str()); //将字符串str转换成一个双精度数值并返回结果
elemCount = 0; //计数器置零
}
else{
getline(file,pixel,','); //任意地读入,直到读到delim字符 ','delim字符不会被放入buffer中
data[j] = (float)atof(pixel.c_str()); //将字符串str转换成一个双精度数值并返回结果
elemCount++;
}
}
}
return image;
}
测试结果:
注意事项:
注意每一个记录结束时以’\n’结尾,而非’,’因此需要特别处理。
OpenCV 的实现:
在我调用read_csv函数时,发现OpenCV已经有实现类似功能的函数:intCvMLData::read_csv(const char* filename)。利用CMake编译OpenCV,点击鼠标右键->转到定义可以方便查看opencv的源代码。(具体方法见以下链接博文)http://blog.csdn.net/solomon1558/article/details/43780533
实现一:
例程Fisherfaces in OpenCV中的read_csv函数
static void read_csv(const string& filename, vector<Mat>&images, vector<int>& labels, char separator = ';'){
std::ifstream file(filename.c_str(),ifstream::in);
if (!file){
string error_message = "No valid input file was given, please check thegiven filename.";
CV_Error(CV_StsBadArg, error_message);
}
string line, path, classlabel;
while(getline(file, line)) {
stringstream liness(line);
getline(liness, path, separator);
getline(liness, classlabel);
if(!path.empty()&& !classlabel.empty()) {
images.push_back(imread(path, 0));
labels.push_back(atoi(classlabel.c_str()));
}
}
}
实现二:int CvMLData::read_csv(const char* filename)
int CvMLData::read_csv(constchar* filename)
{
const int M = 1000000;
const char str_delimiter[3] = { '', delimiter, '\0' };
FILE* file = 0;
CvMemStorage* storage;
CvSeq* seq;
char *ptr;
float*el_ptr;
CvSeqReader reader;
intcols_count = 0;
uchar *var_types_ptr = 0;
clear();
file = fopen( filename, "rt" );
if( !file )
return-1;
// read the firstline and determine the number of variables
std::vector<char>_buf(M);
char* buf =&_buf[0];
if(!fgets_chomp( buf, M, file ))
{
fclose(file);
return-1;
}
ptr = buf;
while( *ptr== ' ' )
ptr++;
for( ; *ptr!= '\0'; )
{
if(*ptr== delimiter || *ptr == ' ')
{
cols_count++;
ptr++;
while(*ptr == ' ' ) ptr++;
}
else
ptr++;
}
cols_count++;
if (cols_count == 0)
{
fclose(file);
return-1;
}
// createtemporary memory storage to store the whole database
el_ptr = newfloat[cols_count];
storage = cvCreateMemStorage();
seq = cvCreateSeq( 0, sizeof(*seq), cols_count*sizeof(float), storage );
var_types = cvCreateMat( 1, cols_count,CV_8U );
cvZero( var_types );
var_types_ptr = var_types->data.ptr;
for(;;)
{
char*token = NULL;
inttype;
token = strtok(buf, str_delimiter);
if(!token)
break;
for (int i = 0; i < cols_count-1; i++)
{
str_to_flt_elem( token, el_ptr[i],type);
var_types_ptr[i] |= type;
token = strtok(NULL,str_delimiter);
if(!token)
{
fclose(file);
delete[] el_ptr;
return-1;
}
}
str_to_flt_elem( token,el_ptr[cols_count-1], type);
var_types_ptr[cols_count-1] |= type;
cvSeqPush( seq, el_ptr );
if(!fgets_chomp( buf, M, file ) )
break;
}
fclose(file);
values = cvCreateMat( seq->total,cols_count, CV_32FC1 );
missing = cvCreateMat( seq->total,cols_count, CV_8U );
var_idx_mask = cvCreateMat( 1,values->cols, CV_8UC1 );
cvSet( var_idx_mask, cvRealScalar(1) );
train_sample_count = seq->total;
cvStartReadSeq( seq, &reader );
for(int i = 0; i < seq->total; i++ )
{
const float* sdata = (float*)reader.ptr;
float*ddata = values->data.fl + cols_count*i;
uchar* dm = missing->data.ptr +cols_count*i;
for( int j = 0; j < cols_count; j++ )
{
ddata[j] = sdata[j];
dm[j] = ( fabs( MISS_VAL - sdata[j]) <= FLT_EPSILON );
}
CV_NEXT_SEQ_ELEM( seq->elem_size,reader );
}
if (cvNorm( missing, 0, CV_L1 ) <= FLT_EPSILON )
cvReleaseMat( &missing );
cvReleaseMemStorage( &storage );
delete[]el_ptr;
return 0;
}