Solved

How to handle .CSV file in MFC

Posted on 2001-07-12
13
1,184 Views
Last Modified: 2013-11-20
I am learning MFC & having a Proj. to read CSV(Comma saperated) file from MFC and generate report. Can any one help me how to handle .CSV File through MFC.
This might be very simple but being a frasher with MFC unable to get break through from MSDN or web.
Bipin
0
Comment
Question by:bpnmistry
  • 6
  • 4
  • 3
13 Comments
 
LVL 4

Expert Comment

by:IainHere
ID: 6276774
You have at least two options:

1) Use a CStdioFile object.  The documentation for this is very good, and you will find it easy.   Essentially, you create one, open a file [use the Open() fn], write your string out [use the WriteString() function], then close the file [you'll be suprised to learn that this requires the Close() function].  Remember that a .csv file uses a , to separate by row, and a \n [newline] to separate columns.

2) Use a fstream.  This is a file stream, and is a part of the standard C++ library, which is included with *every* compliant compiler.  If you have a good book on C++, it will include documentation on how to use it, or else find info on the Web.  The concept is exactly the same as using CStdioFile - except that it will be (a) independent of the compiler you use (b) of more lasting use to you to learn the STL, and your understanding of C++ will be improved.

My preference would be for (2)
0
 
LVL 4

Expert Comment

by:IainHere
ID: 6276782
>a .csv file uses a , to separate by row, and a \n [newline] to separate columns.

erm, oops.  Should read

a .csv file uses a ',' to separate by column, and a '\n' [newline] to separate rows.
0
 
LVL 4

Expert Comment

by:IainHere
ID: 6277084
Not that I want to force you into one direction with this file writing business, but here's a complete example of how to write a csv file using *standard* C++


#include <fstream>
//write file out
std::ofstream bob;
bob.open( "C:/atestfile.csv");
bob << "First Col , Second Col, Third Col" << std::endl << "1, 1, 2" << std::endl << "3, 5, 8"
bob << std::endl "13, 21, 34" << std::endl;
bob.close();

//read file in (dirty method, but an example)
std::ifstream bill;
bill.open("C:/atestfile.csv");
std::string one, two, three, comma;
int     a, b, c;
bill >> one >> two >> three >> a ;
bill >> comma >> b >> comma >> c;
AfxMessageBox(three.c_str());  //display that it worked
bill.close();

note that all standard library stuff is in the std namespace, hence the std:: business.  You can avoid that by putting

using namespace std

straight after the includes, but that is a dirtier way of doing it, because you mix the namespaces.  Also notice that I used endl rather than \n for the newline character.

Reading in again is about as easy, but here I've ignored a few problems (dealing with commas is rubbish in my example - I'm simply parsing the known contents of the file, you'd have to do a loop to find the comma).  

Good luck with the work.
0
 
LVL 10

Expert Comment

by:makerp
ID: 6277328
i have written a CSV file class, its easy to use, do you want the code, it will do what you want
0
 

Author Comment

by:bpnmistry
ID: 6279930
hi to all there & thanks for the tips & help provided by you.
One more thing I need to ask you all that is there is any way where you can access .CSV file & transfer it to any kind of grid(array,structure) from where data access is possibal in simplest way as I have to run query & create report from the data recived.
bipin
0
 

Author Comment

by:bpnmistry
ID: 6279953
hi to all there & thanks for the tips & help provided by you.
One more thing I need to ask you all that is there is any way where you can access .CSV file & transfer it to any kind of grid(array,structure) from where data access is possibal in simplest way as I have to run query & create report from the data recived.
bipin
0
Maximize Your Threat Intelligence Reporting

Reporting is one of the most important and least talked about aspects of a world-class threat intelligence program. Here’s how to do it right.

 
LVL 10

Expert Comment

by:makerp
ID: 6279956
do you want the code for my CSVFile class, this will provide you with a way to access a CSV file and hold it in an array of arrays in memory,

have you already got a way to access and parse the file
0
 

Author Comment

by:bpnmistry
ID: 6280642
hi makerp
I would like to get the code if it is possible.
as of now I have not came to a decision as I have planed it to get in a buer then write code to frame it in a structure.
Pls send the code.
bipin
0
 
LVL 10

Accepted Solution

by:
makerp earned 50 total points
ID: 6280663
/*
      Written By      :      Paul Maker & Jane Maxy & Alan O'Connell
      Date            :      14/02/2001
      File            :      csv_file.h
      Description      :      provides a simple abstraction of a CSV file, the add
                              method adds a row, the get_all gets all the rows
                              from the csv file. see functions for detailed
                              descriptions. it is apreciated that some of the
                              approches used will not scale for large files
                              (2MB and above), this is due to memory consumtion
                              etc.

  NOTES : the flashFile method takes a structure of the file
              (rows of columns, see below) and replaces the file
              with it. WARNING, this action destroys the file
              content completly and replaces it with the csv structure
              you have passed in!. this is useful to update various
              rows and cols in the csv file.
*/

#ifndef __PM_CSV_FILE_H
#define __PM_CSV_FILE_H

/* HANDLE definition */
#include <windows.h>

/* a col_value can be one of these types, text is enclosed in quotes */
#define TYPE_NUMBER                  2001
#define TYPE_TEXT                  2002
/* represents a single column value in the csv file, a row will be
made up of one or more of these  */
struct col_value
{
      /* the type, either number or text, use the above constants */
      int type;
      /* we use a union here so we can represent both basic types
      the union can only be used to hold ONE of the types at a time */
      union
      {
            char *text; /* points to a HEAP allocated block of memory */
            int number;      /* contains the number */
      };
};
/* represents a single row in the file, we can use an array of these
to represent the whole file or a sub-set of rows */
struct csv_row
{
      /* how many columns in this row (this should be the same for
      all rows if the csv file is being used as a database table) */
      int col_count;
      /* the array of column values in this row, this will be
      dynamically allocated */
      struct col_value *values;      
};

/* abstracts a basic csv file, this is as basic as possible and
as efficent as possible */
class CsvFile
{
protected:
      long fhandle;        /* the file handle */
      HANDLE file_lock; /* private lock to control access to the file
                                    (this only needs to be used in a multithreaded
                                    environment)*/
public:
      /* opens the file, we keep it open for effiency and move the
      file pointer when we have to */
      int initalise(char *filename);
      /* adds a row of 'count' 'col_value's to the file, these are appended
      to the file, the 'col_value's must be properly initalised and allocated */
      int add_row(struct col_value *array, int count);
      /* gets the whole file as a heap allocated array of 'csv_row's,
      it is up to the caller to free the returned array. once the
      caller has the csv_row's they can perform there data-manipulation
      very qucikly as the whole lot is in memory. count will contain
      the number of rows returned */
      struct csv_row *get_all(int *count);
      /* closes the file, call this prior to object destruction */
      void close_up();
      /* clean and free 'csv_row's returned from get_all, this frees
      absolutly every thing in the array and it must be called in
      order to aviod potentaily un-bounded memory consumption */
      static void free_csv_rows(struct csv_row *rows,int count);
      /* debug methods, displays all rows and cols in 'rows' array */
      static void dump_all(struct csv_row *rows,int count);
      /* gets the size in bytes of an array of rows, this is usefull
      to gauge memory consumtion */
      static int get_sizeof_csv_rows(struct csv_row* rows,int count);
      /* WARNING, this is potentaily destructive if used with a bad
      rows data structure and count variable */
      int flash_file(struct csv_row* rows,int count);
};       

#endif /* __PM_CSV_FILE_H */

/*
      Written By      :      Paul Maker & Jane Maxy & Alan O'Connell
      Date            :      14/02/2001
      File            :      csv_file.cpp
      Description      :      provides a simple abstraction of a CSV file.
*/

/* the usual run-time stuff */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* memory allocation */
#include <malloc.h>
/* file management stuff */
#include <io.h>
#include <share.h>
#include <fcntl.h>
#include <sys/stat.h>

/* include the CsvFile header */                          
#include "csv_file.h"

/* open the file, fails if the file does not already exist, we keep the
file open for the sake of speed */
int CsvFile::initalise(char *filename)
{
      if((fhandle = _open(filename,_O_RDWR|_O_APPEND|_O_TEXT,_S_IWRITE|_S_IREAD)) == -1)
      {
            perror("opening file failed, create the CSV file first");
            return 0;
      }
      /* create the mutex that will guard access to critical sections */
      file_lock = CreateMutex(NULL,FALSE,NULL);
      return 1;
}

/* closes the file */
void CsvFile::close_up()
{
      _close(fhandle);
}

/* adds a row to the file. the row to be added is passed as an
array of properly initalised col_value's */
int CsvFile::add_row(struct col_value *array, int count)
{       
      /* format strings */
      static char *sep = ",",*end = "\n",*quote = "\"";
      /* temp buffer for number conversion */
      char number_buffer[20];
      /* lock the file */
      WaitForSingleObject(file_lock,INFINITE);
      /* loop through each col to be added */
      for(int i = 0;i<count;i++)
      {
            /* if its a number */
            if(array[i].type == TYPE_NUMBER)
            {
                  /* convert */
                  itoa(array[i].number,number_buffer,10);
                  /* write it to the file */
                  _write(fhandle,number_buffer,strlen(number_buffer));
            }
            else
            {
                  /* it nust be text so put it between quotes */
                  _write(fhandle,quote,strlen(quote));      
                  _write(fhandle,array[i].text,strlen(array[i].text));
                  _write(fhandle,quote,strlen(quote));      
            }      
            /* last loop we dont put a commer at the end */
            if(i < count - 1)
            {
                  _write(fhandle,sep,strlen(sep));      
            }
            else /* instead we put a new-line */
            {
                  _write(fhandle,end,strlen(end));      
            }
      }
      /* release the file lock */
      ReleaseMutex(file_lock);
      return 1;
}

/* gets all the rows and returns them as an array of csv_row's,
the caller must free this with the free method bellow when they are
done with this array, count will contain the number of elements in the
array */
struct csv_row *CsvFile::get_all(int *count)
{
      int so_far = 0;
      struct csv_row *result = NULL;
      /* get the file length */
      int flen = filelength(fhandle);
      *count = 0;
      /* allocate enough memory for the whole file */
      char *whole_file = (char*)malloc(flen);
      /* zero the memory */
      memset(whole_file,0,flen);
      /* anchor the pointer, we use this to free the memory rather than
      whole_file pointer as that gets messed about with and might not be
      pointing to the begining of the block */
      char *anchor = whole_file;
      /* enter the critical section */
      WaitForSingleObject(file_lock,INFINITE);
      /* set the file pointer to the begining of the file */
      _lseek(fhandle,0L,SEEK_SET);
      /* read the whole file in */
      while(true)
      {      
            int t = _read(fhandle,whole_file + so_far,flen - so_far);
            if(t == 0) break;      /* EOF, break out of the loop */
            so_far += t;
      }
      whole_file[so_far] = '\0'; /* null the end */
      /* leave critical section */
      ReleaseMutex(file_lock);
      /* now this bit requires some serious work to be done, we have to
      rip through the memory image of the file and chop it up populating
      the csv_row's array as we go */
      char *ptr = strchr(whole_file,'\n');
      while(ptr != NULL) /* loop through each line */
      {
            *ptr = '\0';
            /* duplicate the line */
            char *dupl = strdup(whole_file);
            /* reallocate the csv_row's array, first time round this will act like malloc */
            result = (struct csv_row*)realloc(result,((*count + 1) * sizeof(csv_row)));
            /* prepare the new array element */
            result[*count].values = NULL;
            result[*count].col_count = 0;
            /* tokenize on the comma */
            char *sub_ptr = strtok(dupl,",");
            while(sub_ptr != NULL)
            {      
                  /* reallocte the values array in the row */
                  result[*count].values = (struct col_value*)realloc(result[*count].values,((result[*count].col_count + 1) * sizeof(col_value)));
                  /* starts with a quote so it must be a string */
                  if(sub_ptr[0] == '"')
                  {
                        /* set the type and duplicate */
                        result[*count].values[result[*count].col_count].type = TYPE_TEXT;
                        result[*count].values[result[*count].col_count].text = strdup(sub_ptr + 1);
                        /* trim the quotes off the end */
                        int z = strlen(result[*count].values[result[*count].col_count].text) - 1;
                        result[*count].values[result[*count].col_count].text[z] = '\0';
                  }
                  else
                  {
                        /* its a number so convert and store */
                        result[*count].values[result[*count].col_count].type = TYPE_NUMBER;
                        result[*count].values[result[*count].col_count].number = atoi(sub_ptr);
                  }
                  /* increment the col count */
                  result[*count].col_count++;
                  /* get next token */
                  sub_ptr = strtok(NULL,",");
            }
            /* free our duplicated line */
            free(dupl);
            /* increase the row count */
            *count = *count + 1;
            whole_file = ptr + 1;
            /* get next new line character */
            ptr = strchr(whole_file,'\n');
      }
      /* free the anchor (remeber this points to the whole_file) */
      if(anchor) free(anchor);
      return result;      
}

int CsvFile::flash_file(struct csv_row* rows,int count)
{
      /* lets check things before we commit to this */
      if(count == 0 || !rows)
      {
            return 0;
      }
      if(_chsize(fhandle,0) == -1)
      {
            return 0;
      }
      for(int i=0;i<count;i++)
      {
            add_row(rows[i].values,rows[i].col_count);
      }
      return 1;
}

/* carefully frees an array of csv_row's, use this when you have used
the get_all method */
void CsvFile::free_csv_rows(struct csv_row *rows,int count)
{
      for(int i=0;i<count;i++)
      {
            for(int a=0;a<rows[i].col_count;a++)
            {
                  /* remember the text member is a heap allocated pointer */
                  if(rows[i].values[a].type != TYPE_NUMBER)
                  {
                        free(rows[i].values[a].text);
                  }
            }
            free(rows[i].values);
      }
      free(rows);
}

/* debug method */
void CsvFile::dump_all(struct csv_row *rows,int count)
{
      for(int i=0;i<count;i++)
      {
            printf("Row number %d\n",count);
            for(int a=0;a<rows[i].col_count;a++)
            {
                  if(rows[i].values[a].type == TYPE_NUMBER)
                  {
                        printf("\tNumber %d\n",rows[i].values[a].number);
                  }
                  else
                  {
                        printf("\tText %s\n",rows[i].values[a].text);
                  }
            }
      }

}

int  CsvFile::get_sizeof_csv_rows(struct csv_row* rows,int count)
{
      int size = 0;
      for(int i=0;i<count;i++)
      {      
            size += sizeof(csv_row);
            for(int a=0;a<rows[i].col_count;a++)
            {
                  size += sizeof(col_value);
                  if(rows[i].values[a].type == TYPE_TEXT)
                  {
                        size += _msize(rows[i].values[a].text);
                  }
            }
      }
      return size;
}


to use to to add a row, (you would have alreafy created the object)

csv_cols[0].type = TYPE_NUMBER;
csv_cols[0].number = new_id;
csv_cols[1].type = TYPE_TEXT;
csv_cols[1].text = in_book->isbn;
csv_cols[2].type = TYPE_NUMBER;
csv_cols[2].number = in_book->price;
csv_cols[3].type = TYPE_TEXT;
csv_cols[3].text = in_book->comment;
if(!csv_file.add_row(csv_cols,4))

heres ahow to get the rows and manipulate them, ignore the database stuff

int csv_count = 0;
      /* get the whole csv file */
      struct csv_row *rows = csv_file.get_all(&csv_count);
      printf("csv file obtained [row count %d, size in bytes %d]\n",csv_count,csv_file.get_sizeof_csv_rows(rows,csv_count));      
      /* bind the columns of the result set that was passed in */
      long lens;
      rs->bindCol(1,SQL_C_SLONG,&temp_book.id,sizeof(long),&lens);
      rs->bindCol(2,SQL_C_CHAR,temp_book.title,sizeof(temp_book.title),&lens);
      rs->bindCol(3,SQL_C_CHAR,temp_book.author,sizeof(temp_book.author),&lens);
      /* first lets loop through the database result and fill in an array
      with the id,title and author */
      while(rs->fetch())
      {
            /* resize the array by another book */            
            result = (struct book*)realloc(result,(*count + 1) * sizeof(book));      
            /* copy it in */      
            result[*count] = temp_book;
            *count = *count + 1;
            __SLOW_DOWN__;
            printf("\rDatabase row count %d",*count);
      }
      rs->finish();
      /* now lets join the csv row to database rows on the primary key */
      printf("\nMatching 'database' rows with 'csv file' rows on primary key\n");
      for(int i=0;i<*count;i++)
      {
            for(int a=0;a<csv_count;a++)
            {
                  /* right we have a match, copy the isbn,price and comments
                  over and then break so we can move on to the next record */
                  if(rows[a].values[0].number == result[i].id)
                  {
                        __SLOW_DOWN__;
                        printf("\rDatbase row : %d matched csv row :%d",i,a);
                        /* do the copy */
                        strcpy(result[i].isbn,rows[a].values[1].text);      
                        result[i].price = rows[a].values[2].number;
                        strcpy(result[i].comment,rows[a].values[3].text);
                        break;
                  }
            }
      }
0
 

Author Comment

by:bpnmistry
ID: 6280697
hi makerp
I would like to get the code if it is possible.
as of now I have not came to a decision as I have planed it to get in a buer then write code to frame it in a structure.
Pls send the code.
bipin
0
 

Author Comment

by:bpnmistry
ID: 6285451
Thanks Paul
this has shown me a new way to reduce usage of valuable time & resaurces.
bipin
0
 
LVL 10

Expert Comment

by:makerp
ID: 6285467
cool, thanx for the points
0
 

Author Comment

by:bpnmistry
ID: 6289534
hi paul
can you provide me your mail id
bipin
0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

This is to be the first in a series of articles demonstrating the development of a complete windows based application using the MFC classes.  I’ll try to keep each article focused on one (or a couple) of the tasks that one may meet.   Introductio…
Introduction: Dialogs (2) modeless dialog and a worker thread.  Handling data shared between threads.  Recursive functions. Continuing from the tenth article about sudoku.   Last article we worked with a modal dialog to help maintain informat…
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
Internet Business Fax to Email Made Easy - With eFax Corporate (http://www.enterprise.efax.com), you'll receive a dedicated online fax number, which is used the same way as a typical analog fax number. You'll receive secure faxes in your email, fr…

708 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

14 Experts available now in Live!

Get 1:1 Help Now