Click here to Skip to main content
15,896,063 members
Please Sign up or sign in to vote.
4.00/5 (1 vote)
See more:
Hi ,

I want to declare an array of strings in my C++ code. But i am not getting the correct result.

My req. is to read from a csv file and then store each field in an array.

The csv file looks somehow like this:

sg,vsvs,22,53,dd,,,,dg
34,2,f,d,343,gg,45,,g,4
gdg,,3,fd,gd,3453,53,,,453,STRING1,,
aa,,sg,2,32fwef,3f3f3,f3,STRING1,,,,


Null fields are also there and should not be ignored. These null fields should also be pushed into the string array.

My code for the above req. is :
C++
#include<iostream>
#include<fstream>
#include<string>
#include<stdio.h>
#include<stdlib.h>



using namespace std;

int main()
{
  char* arr[50][50];
  const int N=100;
  char* toks=NULL;
  char line[100];
  int i=0,j=0;
  static int row=0,col=0;
  ifstream myfile("sample.csv");

  while(myfile)
  {
    myfile.getline(line,N);
    toks=strtok(line,",");
    while(toks!=NULL)
    {
      arr[i][j]=toks;
      toks=strtok(NULL,",");
      j++;
      col=j;
    }
    i++;
    row=i-1;
    j=0;
  }

  for(int i=0;i<row;i++)
  {
    for(j=0;j<col;j++)
    {
       cout<<arr[i][j]<<"\t";
    }
    cout<<"\n"; 
  }
  myfile.close();

  return 0;
}



But the result comes as :

,sg fwef f 3



I dont know y there are lots of spaces in between and also the desired result doesn't come.

Please anyone help me on this...

Thanks
Deepak
Posted
Updated 10-May-11 21:37pm
v3

Remember that your variable toks was declared as a pointer to char.
So if you're doing this:
MIDL
arr[i][j]=toks;

you're not copying the content where toks points to into your array but you're copying the pointer itself.
Meaning after this operation arr[i][j] points to the same memory as toks.

If you really want to copy the content where a char* points to you can use strcpy()[^]

Considering the explained, while doing this:
C#
while(toks!=NULL)
{
     // after this the element arr[i][j] points to the same memory as toks
     arr[i][j]=toks;
     // you're writing the next token to the memory pointed to by toks (which is the same as arr[i][j] !)
     // In the next iteration toks still points to the same memory.
     // All of your elements in arr[i][j] as well as toks point to the same memory!
     toks = strtok(NULL,",");
     j++;
     col=j;
}


All your array elements point to the same memory which you're overwriting with each call to strtok.

Another thing is. If you're in an C++ environment why you're not using the much safer std::string and std::vector?
 
Share this answer
 
v4
Your code currently only copies pointers to tokens that you get through strtok. These tokens are stored in a static copy of your input string, which you stored in line. This means that each time you make a new call to strtok at the start of a new line, this static copy will be overwritten with the new line, and thus your previous pointers will all be invalidated: they still point into valid memory, but the contents therein are different!

Therefore, it may well be that your code worked as long as you only looked at a one-line input file. but the moment you start reading the second line and apply strtok to it, your program breaks.

As others have pointed out, it would be wiser to use std::string and std::vector like this:
std::vector<std::vector<std::string> > arr; // dynamic 2D array of dynamic strings
For just a quick fix, it would suffice to replace the char* from your declaration:
std::string arr[50][50]; // fixed size 2D array of dynamic strings
But note, that if you ever have more than 50 lines or more than 50 strings per line, your program will break!

Changing the type like this will result in changing the meaning of
arr[i][j] = toks;
from merely copying the pointer to actually copying the string that toks points to. This should be sufficient to fix your problem for now.
 
Share this answer
 
v3
Comments
Legor 11-May-11 4:41am    
Good answer!
Niklas L 11-May-11 6:30am    
You got a five here, even though you'll have to fix your first code block. It's a bit XML-ish. :)
Stefan_Lang 11-May-11 6:34am    
fixed it - thanks for the hint (and for the 5 ;) )
Basically you are handling badly the C-like strings, inserting temporary character pointers into your array.
Since you are using C++ why don't you exploit its standard library?
Try, for instance:
C++
#include<iostream>
#include <vector>
#include<fstream>
#include<string>
using namespace std;

int main()
{
  ifstream myfile("sample.csv");

  vector < vector< string > > ar;
  while (true)
  {
    string s;
    size_t lastpos, curpos;

    getline(myfile, s);
    if ( !myfile) break;
    ar.push_back( vector<string>());
    for (lastpos=curpos=0; curpos!=string::npos; lastpos=curpos+1)
    {
      curpos = s.find(',', lastpos);
      if (curpos != string::npos)
        ar.back().push_back(s.substr(lastpos, curpos-lastpos));
      else
        ar.back().push_back(s.substr(lastpos));
    }
  }

  myfile.close();

  for (size_t i=0; i < ar.size(); i++)
  {
    for (size_t j=0; j < ar[i].size(); j++)
    {
        cout << ar[i][j] << "\t";
    }
    cout << endl;
  }
  return 0;
}
 
Share this answer
 
Comments
deepakrout1987 11-May-11 7:35am    
Thanks Stefan for your answer:

But i am ignorant about the vectors. It would be very helpful of you if you explain me the below code you have written(it would be gr8 if you add comments besides each statement).


if ( !myfile) break;
ar.push_back( vector<string>());
for (lastpos=curpos=0; curpos!=string::npos; lastpos=curpos+1)
{
curpos = s.find(',', lastpos);
if (curpos != string::npos)
ar.back().push_back(s.substr(lastpos, curpos-lastpos));
else
ar.back().push_back(s.substr(lastpos));
}

And what is this declaration of the vector?? I tried to google out the declaration and its like:

vector<string int=""> vectorname

Please explain..Your explanation would help me learn.

Thanks
Deepak
CPallini 11-May-11 7:50am    
Stefan?! (my name is 'Carlo').
As C++ developer you should know templates and the standard C++ library. Please read (study!) a good book about. Anyway 'the poor man explanation' follows:
vectors are generic containers. They are like arrays but can grow when it is needed.
For instance vector<int> vt;
is like a dynamic array of int (i.e. you may address its elements via the [] operator, like, for instance vt[5]); if you need to append a value to the array you may call the push_back method, e.g. vt.push_back(23) the vector will internally grow if needed.
In your code you are saving pointers to each item, but then overwriting the data with the next line. By the time you come to your output routine the original data pointed at by the elements of your array has been overwritten. The only remaining pointers that are valid are the ones for the last line read in. You should use std::string and std::vector to ensure that each string you break out is copied to a permanent location before you read the next line.
 
Share this answer
 
Comments
Niklas L 11-May-11 6:43am    
United Kingdom: 5 points. Royaume-Uni: cinq points.
This is just an explanation on how to use vector, since you asked about that in response to Carlos solution. Here is a bit of code with comments that explain its differences to a standard array:
int arr[50];        // standard array with 50 elements of type int
int* parr = arr;    // ok: arr is an array, but it is also the address
                    // of the first element
int n_arr_elements = sizeof(arr)/sizeof(int); // this evaluates to 50,
                    // the number of elements in arr
arr[5] = 17;        // assign the value 17 to the sixth element of arr
arr[500] = 23;      // error! this will likely cause an error at runtime,
                    // since you are writing past the end of your array!

vector<int> vec;    // vector holding elements of type int
size_t n_vec_elements = vec.size(); // this evaluates to the _current_
                    // number of elements in vec; right now, it is 0
int* pvec = vec;    // compiler error: can not convert std::vector<int> to int*
pvec = &(vec[0]);   // ok: now pvec points to the first element
vec[5] = 17;        // resize vector to hold at least six elements, then
                    // assign 17 to sixth element
n_vec_elements = vec.size(); // now the size will be 6
vec[500] = 23;      // ok: vec will be resized to hold 501 elements, then
                    // the 500th gets assigned
n_vec_elements = vec.size(); // now the size will be 501


As you see, vector behaves quite similar to a standard array, with the following differences:
- a variable of type vector is not convertible to a pointer
- a vector resizes itself dynamically, as needed; you will never accidentally read or write past the end of the buffer
- you can easily query the current size of a vector, but you cannot rely on it to stay the same
 
Share this answer
 
v5
Dear Richard,

I have tried using it on a different context and it works fine.Its not been overwritten by the next line as per my knowledge. If in case i am wrong, then please suggest me with the desired changes to my code.

Thanks

Deepak
 
Share this answer
 
Comments
Tarun.K.S 11-May-11 3:58am    
Add this as a comment to Richard's answer instead of writing as an answer.
Legor 11-May-11 4:00am    
yes it is overwritten, just as Richard explained it. And dont answer as a solution but comment a solution if you've anything to comment.
deepakrout1987 11-May-11 4:17am    
Ok if i take your word, then can you please explain me what does the inner while loop do in detail?? Also i am not aware of the std::string and std::vector concept. Can you please reconstruct the code given above and explain wht its doing so that i get to know the exact thnong. Moreover, i doen't think that std::string and std::vector is good for large sized files.It causes memory leaks.Can you please suggest an alternative to it coz i will be handling csv files weighing 2GBs and above.
If you think those are appropriate, then please reconstruct the code with std::strings and vectors.

Also the strcpy() throws an error:
the statemnt is:

strcpy(arr[i][j],toks)
and the error thrown is:

line 28: Error: Cannot cast from std::string to char*.

Please suggest.

Thanks
Deepak
CPallini 11-May-11 4:28am    
std::string and std::vector cause memory leaks like fishes ride bycicles.
:-)
Legor 11-May-11 4:38am    
lol'ed

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900