You are here:

C++/File operation

Advertisement


Question
hello
i am new to C++ and need your help in this code.

i am trying to copy a text file in an array of string a[100] with the getline function but same is not working. below is my code.

#include<iostream>
#include<fstream>
#include<string>

using namespace std;
//template<class T>
//struct b {
//char a[255];
//};


void main()

{
string a[100];
//struct b lines[100];
ifstream input;
input.open("c:\file2.txt");
int i=0;

if(!input)
cout<<"file not opened";
else
cout<<"file opened successfully"<<endl;


while(input)
{
input.getline(a[i],255,"\n");
i++;
}
cout<<a[i];
}

i am receiveing the following errors
error C2664: 'class std::basic_istream<char,struct std::char_traits<char> > &__thiscall std::basic_istream<char,struct std::char_traits<char> >::getline(char *,int,char)' : cannot convert parameter 1 from 'class std::basic_
string<char,struct std::char_traits<char>,class std::allocator<char> >' to 'char *
at line
input.getline(a[i],255,"\n");
kindly advice please

regards


Answer
I suggest you go back and read the small print for the std:istream::getline
functions, here are the signatures of these functions:

  istream& getline (char* s, streamsize n );
  istream& getline (char* s, streamsize n, char delim );

(namespace and class qualifications omitted for brevity)

You will notice in both cases that they take a char * value and not
std::string & value for their first parameter (they also take a single char
value and not a string value for their third parameter). Hence you cannot
use the std::istream class getline functions in the way you are trying to.

This is indicated by the error message, if you follow it through. I know
this is tricky when template type expansions are involved and is a
recognised problem with C++ compilers. So I have broken the error message up
and reduced the template parameter expansions to char, blah, blah to help
with seeing what the compiler is really on about:

error C2664:

'class std::basic_istream<char, blah, blah>::getline(char *,int,char)':

cannot convert parameter 1 from

'class std::basic_string<char, blah, blah>'

to

'char *

at line

input.getline(a[i],255,"\n");

The important part you need to look out for is what the error really is. In
this case it is indicated by the text:

  cannot convert parameter 1

followed by the type of the item you provided for the offending parameter
(the from type) and what type it was expecting (the to type), and finally
the compiler displays the line that it is having problems with.

That is the compiler cannot convert the string type object indicated by a[i]
to the expected char * type required by the first parameter to the getline
member function.

The class templates std::basic_istream and std::basic_string are the class
templates that std::istream and std::string are formed from. The only thing
to note about their appearance in the error message is that the template
parameters in the error message mention char as the character type in both
cases.

OK so now you understand what is wrong, what can you do to fix it?

Well first you could use an array of character arrays:

  const unsigned int NumberOfStrings(100);
  const unsigned int MaxStringLength(254);
  const unsigned int CharArraySize(MaxStringLength+1);

  char a[NumberOfStrings][CharArraySize];

  // ...

  input.getline(a[i], CharArraySize,'\n');

Note that I have defined a set of constant values for the sizes involved.
This is because just using literal values like 100 and 255 (so called 'magic
numbers') are fairly meaningless and tend to need repeating. This makes
maintenance more difficult, especially if done by someone else. In your code
for example we now require the value 255 twice: once for the size of each
character array, and again for the limit of the number of characters to be
read by input.getline. If you increase the latter value to handle reading
longer lines and forget to increase the former to make space for them then
your revised program is likely to crash.

Notice that to be accurate the maximum number of characters read is one less
than that passed to input.getline, and the size of a char array used for
C-style strings is one more than the number of characters required to
include space for the terminating zero character present at the end of
C-style strings. Hence the size of a char array for a maximum C-string
character length of n is n+1 and the value passed to input.getline to
specify n as the maximum number of characters to read is also n+1. This is
why I created two constants MaxStringLength, which represents the value of
n, and CharArraySize, which represents the value of n+1. So if you wish to
increase the maximum line length to say 511 you just change the value of
MaxStringLength to 511 and re-build the program.

Also note that you do not need to explicitly state the delimiter as being
the new line character as this is the default so:

  input.getline(a[i], CharArraySize);

would do just as well. However if you do specify a delimiter it is a single
character value (e.g. '\n') and _not_ a C-string or string literal value
(e.g. "\n"), as is present in your original code.

The problem with this solution is that you need to work with fixed sized
arrays and upper bounds to the line lengths that can be read. Obviously it
would be better to find a solution that used std::string as the container
for read lines which automatically grow as required and find some getline
functionality which supported this behaviour.

This brings us to the second option which is to use the stand alone
std::getline functions (i.e. they are not a member of any class):

 istream& getline( istream& is, string& str);
 istream& getline( istream& is, string& str, char delim);

(namespace and class qualifications omitted for brevity)

Again there are two versions: one that takes a delimiter character and
another that does not which defaults to using new line as the delimiter.
These are defined as part of the std::string interface and as such are
declared in the <string> header.

These versions take a std::istream object reference as their first parameter
and a std::string reference as their second. So all you would need to do to
your original code is to replace the line:

  input.getline(a[i],255,"\n");

with:

  getline( input, a[i], '\n' );
or:
  getline( input, a[i] );

Now what happens if your file has more than 100 lines? You have _no_ checks
on the number of lines read, and this is still fixed at 100. Reading the
101st line will cause undefined behaviour as you overwrite the memory after
a[]. If you are lucky you will cause your program to crash, if not then you
may get odd behaviour. The program may finish in this case before you notice
such problems being so short, but in a larger program such errors can cause
odd bugs that can take a while to track down.

Likewise the writing of a[i] to cout _after_ the while loop will also try to
access the 101st element for a file of 100 lines, and will always access the
contents of the next string in the array after the last one read which, if i
is still in range will be an empty string.

I suggest that in the same way as std::string extends automatically as
required, unlike a built in array of char, you use a collection of strings
that does likewise rather than using a built in array of strings. The
obvious choice here is a std::vector of strings:

  #include<vector>

  typedef std::vector< std::string >   StringVector;

  using namespace std;

  int main()
  {
      StringVector a;

  // ...

I declared a type alias for a vector of strings by using typedef. This is
not necessary but does create type name without all the template trappings
(which also tend to be shorter) and is common practice. Your program will
compile without any further changes, but will most likely crash when run.
This is because when using a vector like this it is created empty with no
elements in it at all. To extend it we have to call push_back to add a new
element to the end of the vector. This means we have to modify your loop
code somewhat:

  while (input)
  {
      string in_str;
      getline(input, in_str);
      a.push_back( in_str );
      cout<<a[i];
      i++;
  }

You will notice that I have moved the writing of the last added value to a
to cout into the loop before i is incremented. You might wish to add extra
formatting to this statement (such as writing spaces or new lines between
each string written).

In fact it would probably be better to only perform the push_back etc. if
input is still OK:

  while (input)
  {
      string in_str;
      getline(input, in_str);
      if (input)
      {
         a.push_back( in_str );
         cout<<a[i];
         i++;
      }
  }

Hope this is of use to you.
I suggest you go back and read the small print for the std:istream::getline
functions, here are the signatures of these functions:

  istream& getline (char* s, streamsize n );
  istream& getline (char* s, streamsize n, char delim );

(namespace and class qualifications omitted for brevity)

You will notice in both cases that they take a char * value and not
std::string & value for their first parameter (they also take a single char
value and not a string value for their third parameter). Hence you cannot
use the std::istream class getline functions in the way you are trying to.

This is indicated by the error message, if you follow it through. I know
this is tricky when template type expansions are involved and is a
recognised problem with C++ compilers. So I have broken the error message up
and reduced the template parameter expansions to char, blah, blah to help
with seeing what the compiler is really on about:

error C2664:

'class std::basic_istream<char, blah, blah>::getline(char *,int,char)':

cannot convert parameter 1 from

'class std::basic_string<char, blah, blah>'

to

'char *

at line

input.getline(a[i],255,"\n");

The important part you need to look out for is what the error really is. In
this case it is indicated by the text:

  cannot convert parameter 1

followed by the type of the item you provided for the offending parameter
(the from type) and what type it was expecting (the to type), and finally
the compiler displays the line that it is having problems with.

That is the compiler cannot convert the string type object indicated by a[i]
to the expected char * type required by the first parameter to the getline
member function.

The class templates std::basic_istream and std::basic_string are the class
templates that std::istream and std::string are formed from. The only thing
to note about their appearance in the error message is that the template
parameters in the error message mention char as the character type in both
cases.

OK so now you understand what is wrong, what can you do to fix it?

Well first you could use an array of character arrays:

  const unsigned int NumberOfStrings(100);
  const unsigned int MaxStringLength(254);
  const unsigned int CharArraySize(MaxStringLength+1);

  char a[NumberOfStrings][CharArraySize];

  // ...

  input.getline(a[i], CharArraySize,'\n');

Note that I have defined a set of constant values for the sizes involved.
This is because just using literal values like 100 and 255 (so called 'magic
numbers') are fairly meaningless and tend to need repeating. This makes
maintenance more difficult, especially if done by someone else. In your code
for example we now require the value 255 twice: once for the size of each
character array, and again for the limit of the number of characters to be
read by input.getline. If you increase the latter value to handle reading
longer lines and forget to increase the former to make space for them then
your revised program is likely to crash.

Notice that to be accurate the maximum number of characters read is one less
than that passed to input.getline, and the size of a char array used for
C-style strings is one more than the number of characters required to
include space for the terminating zero character present at the end of
C-style strings. Hence the size of a char array for a maximum C-string
character length of n is n+1 and the value passed to input.getline to
specify n as the maximum number of characters to read is also n+1. This is
why I created two constants MaxStringLength, which represents the value of
n, and CharArraySize, which represents the value of n+1. So if you wish to
increase the maximum line length to say 511 you just change the value of
MaxStringLength to 511 and re-build the program.

Also note that you do not need to explicitly state the delimiter as being
the new line character as this is the default so:

  input.getline(a[i], CharArraySize);

would do just as well. However if you do specify a delimiter it is a single
character value (e.g. '\n') and _not_ a C-string or string literal value
(e.g. "\n"), as is present in your original code.

The problem with this solution is that you need to work with fixed sized
arrays and upper bounds to the line lengths that can be read. Obviously it
would be better to find a solution that used std::string as the container
for read lines which automatically grow as required and find some getline
functionality which supported this behaviour.

This brings us to the second option which is to use the stand alone
std::getline functions (i.e. they are not a member of any class):

 istream& getline( istream& is, string& str);
 istream& getline( istream& is, string& str, char delim);

(namespace and class qualifications omitted for brevity)

Again there are two versions: one that takes a delimiter character and
another that does not which defaults to using new line as the delimiter.
These are defined as part of the std::string interface and as such are
declared in the <string> header.

These versions take a std::istream object reference as their first parameter
and a std::string reference as their second. So all you would need to do to
your original code is to replace the line:

  input.getline(a[i],255,"\n");

with:

  getline( input, a[i], '\n' );
or:
  getline( input, a[i] );

Now what happens if your file has more than 100 lines? You have _no_ checks
on the number of lines read, and this is still fixed at 100. Reading the
101st line will cause undefined behaviour as you overwrite the memory after
a[]. If you are lucky you will cause your program to crash, if not then you
may get odd behaviour. The program may finish in this case before you notice
such problems being so short, but in a larger program such errors can cause
odd bugs that can take a while to track down.

Likewise the writing of a[i] to cout _after_ the while loop will also try to
access the 101st element for a file of 100 lines, and will always access the
contents of the next string in the array after the last one read which, if i
is still in range will be an empty string.

I suggest that in the same way as std::string extends automatically as
required, unlike a built in array of char, you use a collection of strings
that does likewise rather than using a built in array of strings. The
obvious choice here is a std::vector of strings:

  #include<vector>

  typedef std::vector< std::string >   StringVector;

  using namespace std;

  int main()
  {
      StringVector a;

  // ...

I declared a type alias for a vector of strings by using typedef. This is
not necessary but does create type name without all the template trappings
(which also tend to be shorter) and is common practice. Your program will
compile without any further changes, but will most likely crash when run.
This is because when using a vector like this it is created empty with no
elements in it at all. To extend it we have to call push_back to add a new
element to the end of the vector. This means we have to modify your loop
code somewhat:

  while (input)
  {
      string in_str;
      getline(input, in_str);
      a.push_back( in_str );
      cout<<a[i];
      i++;
  }

You will notice that I have moved the writing of the last added value to a
to cout into the loop before i is incremented. You might wish to add extra
formatting to this statement (such as writing spaces or new lines between
each string written).

In fact it would probably be better to only perform the push_back etc. if
input is still OK:

  while (input)
  {
      string in_str;
      getline(input, in_str);
      if (input)
      {
         a.push_back( in_str );
         cout<<a[i];
         i++;
      }
  }

Hope this is of use to you.

C++

All Answers


Answers by Expert:


Ask Experts

Volunteer


Ralph McArdell

Expertise

I am a software developer with more than 15 years C++ experience and over 25 years experience developing a wide variety of applications for Windows NT/2000/XP, UNIX, Linux and other platforms. I can help with basic to advanced C++, C (although I do not write just-C much if at all these days so maybe ask in the C section about purely C matters), software development and many platform specific and system development problems.

Experience

My career started in the mid 1980s working as a batch process operator for the now defunct Inner London Education Authority, working on Prime mini computers. I then moved into the role of Programmer / Analyst, also on the Primes, then into technical support and finally into the micro computing section, using a variety of 16 and 8 bit machines. Following the demise of the ILEA I worked for a small company, now gone, called Hodos. I worked on a part task train simulator using C and the Intel DVI (Digital Video Interactive) - the hardware based predecessor to Indeo. Other projects included a CGI based train simulator (different goals to the first), and various other projects in C and Visual Basic (er, version 1 that is). When Hodos went into receivership I went freelance and finally managed to start working in C++. I initially had contracts working on train simulators (surprise) and multimedia - I worked on many of the Dorling Kindersley CD-ROM titles and wrote the screensaver games for the Wallace and Gromit Cracking Animator CD. My more recent contracts have been more traditionally IT based, working predominately in C++ on MS Windows NT, 2000. XP, Linux and UN*X. These projects have had wide ranging additional skill sets including system analysis and design, databases and SQL in various guises, C#, client server and remoting, cross porting applications between platforms and various client development processes. I have an interest in the development of the C++ core language and libraries and try to keep up with at least some of the papers on the ISO C++ Standard Committee site at http://www.open-std.org/jtc1/sc22/wg21/.

Education/Credentials

©2016 About.com. All rights reserved.