C++ cin.getline to char array - c++

I have declared an array: names[1000];
and another array, data[1000];, to store the data temporarily
and later used an ifstream to read data from an XML file.
then later, I used cin.getline(data, 300) to put the data into data[] array.
but when I assign data[] array to names[] array, an error occurs:
invalid operands of types char[1000] and char[1000] to binary operator>>
code:
char data[1000];
char names[1000];
ifstream openFile("myfile.xml");
if(!openFile)
{
cout<<"File not found! please re-enter filename"<<endl;
}
while (openFile.getline (data, 300))
{
if (data[0] == '<' && data[1] == 'n') // to only check the <name> xml tag
{
cout<<data<<endl;
data >> names;
}
}
Any idea why I cant assign data array to names array?
Thanks!

">>" operator is usually defined for streams, but data is just an array.
if you want to copy the content, use strncpy from string.h:
strncpy(names, data, 1000);
if you want to treat your string/array as stream, try stringstream.
BTW, you may want to use C++ string instead of character arrays -- it's more convenient (but not so efficient).

The operator >> is not used for assignment ! You could assign using strncpy like this
strncpy(names, data, 1000);
and add the include
#include <string.h>

Because you can't assign arrays. At all.
You may want to take a look at std::vector or std::string, which can be assigned among other cool things.
If you want to stick to char arrays, you can do the following :
std::copy(std::begin(data), std::end(data), std::begin(names));
Or (to avoid copying trash after the 300th element) :
std::copy(std::begin(data), std::begin(data) + 301, std::begin(names));

Related

Read undefined number of variables from file line

I am making a simple database system in C++.
The table data is stored in a file, where each line represents a table row, where all data is separated by spaces.
I want to read ncols elements in same line, where ncols is not always the same, and store each read value in data[x].
data variable declaration is char** data.
void Table::LoadTableRows(Table::TableStruct *table,char *dbname) {
ifstream fp;
Table::RowStruct *p = (Table::RowStruct*) malloc(sizeof(Table::RowStruct));
char *filename;
int x;
filename = (char*) malloc((strlen(table->tablename)+strlen(dbname)+strlen("Data"))*sizeof(char));
strcpy(filename,dbname);
strcat(filename,table->tablename);
strcat(filename,"Data");
fp.open(filename);
while(!fp.eof()) { //goes through all file lines
Table::RowStruct *newrow = (Table::RowStruct*) malloc(sizeof(Table::RowStruct)); //allocates space for a new row
//initializes element
newrow->prev = NULL;
newrow->next = NULL;
newrow->data = (char**) malloc(table->ncols*30*sizeof(char)); //allocates space to store the row data
for(x=0;x<table->ncols;x++) {
newrow->data[x] = (char*) malloc(30*sizeof(char)); //allocates space for individual data element
fp >> newrow->data[x];
}
for(p=table->rows;p->next!=NULL;p=p->next) {}
newrow->prev = p;
p->next = newrow;
}
fp.close();
}
I've tried this code, but it crashed as I expected.
I do not fully understand what you want to do. There is missing information. Anyway. I will try to help.
I guess that you are new to C++. You are using a lot of C functions. And your program looks completely like C, with some additional C++ features. That you should not do. You are especially using malloc and raw pointers. This you must not do at all.
Try to learn C++ step by step.
Let me first show you what I mean with C-Style programming. I took your program and added comments with hints.
// Do not pass arguments by pointer, pass by reference
// For invariants, pass as const T&
// Do not use "char *". Should be at least const. But do not use at all
// Use std::string (so pass "const std::string& dbname") as argument
void Table::LoadTableRows(Table::TableStruct *table,char *dbname) {
// Always initialize varibles. Use universal initialization, with {}
ifstream fp;
// Never use malloc. Use new.
// Do not use raw ptr. use std::unique_ptr. Initialize with std::make_unique
// Do not use C-Style cast. Use static_cast
Table::RowStruct *p = (Table::RowStruct*) malloc(sizeof(Table::RowStruct));
// Use std::string
char *filename;
int x;
// Again. No malloc, no C-Style cast
// Do not use C-Sytle string functions
filename = (char*) malloc((strlen(table->tablename)+strlen(dbname)+strlen("Data"))*sizeof(char));
// Do not use C-Sytle string functions
strcpy(filename,dbname);
// Do not use C-Sytle string functions
strcat(filename,table->tablename);
// Do not use C-Sytle string functions
strcat(filename,"Data");
// Check, if open works, Open file through constructor, then it will be closed by destructor
fp.open(filename);
while(!fp.eof()) { //goes through all file lines
// Do not use malloc and C-Style cast
Table::RowStruct *newrow = (Table::RowStruct*) malloc(sizeof(Table::RowStruct)); //allocates space for a new row
//initializes element
// Do not use NULL, but nullptr
newrow->prev = NULL;
newrow->next = NULL;
// Do not use malloc and C-Style cast
newrow->data = (char**) malloc(table->ncols*30*sizeof(char)); //allocates space to store the row data
// Do not use x++ but ++x
for(x=0;x<table->ncols;x++) {
// Do not use malloc and C-Style cast
newrow->data[x] = (char*) malloc(30*sizeof(char)); //allocates space for individual data element
// Check for out of bounds
fp >> newrow->data[x];
}
// Do not use selfmade linked list. Use STL container
for(p=table->rows;p->next!=NULL;p=p->next) {}
newrow->prev = p;
p->next = newrow;
}
fp.close();
}
You see, there is a lot of C in it and not so much C++.
The modern C++ makes much use of containers and algorithms.
A full fledged example for C++ is below. It is hard to understand for beginners. But try to analyze and you will get a hang of it.
#include <vector>
#include <string>
#include <iostream>
#include <fstream>
#include <iterator>
#include <algorithm>
#include <sstream>
using AllWordsInOneLine = std::vector<std::string>;
using AllLines =std::vector<AllWordsInOneLine>;
struct Line // ! This is a proxy for the input_iterator !
{ // Input function. Read on line of text file and split it in words
friend std::istream& operator>>(std::istream& is, Line& line) {
std::string wholeLine; std::getline(is, wholeLine); std::istringstream iss{ wholeLine }; line.allWordsInOneLine.clear();
std::copy(std::istream_iterator<std::string>(iss), std::istream_iterator<std::string>(), std::back_inserter(line.allWordsInOneLine));
return is;
}
operator AllWordsInOneLine() const { return allWordsInOneLine; } // cast to needed result
AllWordsInOneLine allWordsInOneLine{}; // Local storage for all words in line
};
int main()
{
std::ifstream inFileStream{ "r:\\input.txt" }; // Open input file. Will be closed by destructor
if (!inFileStream) { // ! operator is overloaded
std::cerr << "Could not open input file\n";
}
else {
// Read complete input file into memory and organize it in words by lines
AllLines allLines{ std::istream_iterator<Line>(inFileStream), std::istream_iterator<Line>() };
// Make exact ncols entries.
const size_t ncols = 6; // whatever ncols may be. Empty cols will be filled with ___ (or whatever you like)
std::for_each(allLines.begin(), allLines.end(), [ncols](AllWordsInOneLine& awil) {awil.resize(ncols, "___"); });
// copy result to std::cout
std::for_each(allLines.begin(), allLines.end(), [](AllWordsInOneLine & awil) {std::copy(awil.begin(), awil.end(), std::ostream_iterator<std::string>(std::cout, " ")); std::cout << '\n'; });
}
return 0;
}
Please see especially that the whole file, with all lines split into words, will be read in one line of code in function main.
An additional one-liner converts this into a vector with exactly ncols elements (words). This regardless if there were more or less than ncols words per line in the source file.
Hope I could help at least a little bit.
char *filename;
filename = (char*) malloc((strlen(table->tablename)+strlen(dbname)+strlen("Data"))*sizeof(char));
strcpy(filename,dbname);
strcat(filename,table->tablename);
strcat(filename,"Data");
Here's your first problem. You haven't allocated space for the terminating nul byte at the end of the string. I'm not sure why you're using C-style strings instead of std::string, but C-style strings use a zero byte at the end to mark the end of the string.
fp.open(filename);
while(!fp.eof()) { //goes through all file lines
You are misusing eof. It can't predict that a future read will succeed, it's not a future-predicting function but a past reporting function.
newrow->data = (char**) malloc(table->ncols*30*sizeof(char)); //allocates space to store the row data
This is puzzling. The type is char **, which means you're allocating a pointer to a pointer. Yet you allocate space for 30 characters. Why allocate 30 characters for a pointer?
fp >> newrow->data[x];
You don't check if this read succeeds. That's never a good thing and makes your program impossible to debug.
These are the major issues that immediately stand out.

File I/O end of line

I am trying to read text from a file into an array and then output the contents of each array index to the output file. I need the data to be read/stored until it reaches the end of line, at which point it should re-start reading/storing and re-using the array for temporary storage only to be output to the output file.
I cannot use the getline function because the idea is that later I will incorporate the use of some model classes to store the individual words as member variables of the classes. I will need to have the words separated to know which words get saved as which variables. For this reason I need to be able to just identify the corresponding index position and get it's contents.
I know my syntax is incorrect so I was hoping someone knew a correct syntax for recognizing the end of line.
this is what I've tried so far:
ifstream fin;
//open file...
char next[20]; //creating an word array to hold the characters of a word.
fin >> next;
while (!fin == '\n') //<------ THIS IS WHAT I THINK THE PROBLEM IS.
//I KNOW ITS INCORRECT BUT DO NOT KNOW THE CORRECT WAY.
{
//input words, store to array, and output to file
fin >> next;
}
You should use a std::string instead of a char array to handle words of any size. Streams also have an implicit conversion to void* (bool in C++11 or later) to test if the stream is still valid.
std::ifstream fin(filename);
std::string word;
while(fin >> word) {
//do something with word
}

C++ multidimension array storing strings

I wish to store for example 10 words into a multi-d array. This is my code.
char array[10][80]; //store 10 words, each 80 chars in length, get from file
int count = 0;
while ( ifs >> word ){ //while loop get from file input stream <ifstream>
array[count++][0] = word;
}
when i compile, there's error. "invalid conversion from ‘char*’ to ‘char’ ". ifs return a char pointer. How can i succesffuly store into array?
As this is C++, I would use the STL containers to avoid some char* limitations. word would have type std::string, array would have type std::vector<std::string> and you would push_back instead of assigning. The code looks like this:
#include <string>
#include <vector>
std::string word;
std::vector<std::string> array;
while(ifs >> word) {
array.push_back(word);
}
This is better than char* for a few reasons: you hide the dynamic allocation, you have words with real variable size(up to memory size), and you don't have any issues if you need more than 10 words.
Edit: as mentioned in the comments, if you have a compiler that supports C++11, you can use emplace_back and std::move instead, which will move the string instead of copying it (emplace_back alone will construct the string inplace.)
You should define a pointer to array I think, that can access each value of array blocks one by one (or the way you want). You can also try dynamic allocation. Those are pointer things so then it'll be comparable easily.
word is char*(string), but array[count++][0] is store a char, you can change "array[count++][0] = word;" to "strcpy(array[count++], word);"
char array[10][80]; //store 10 words, each 80 chars in length, get from file
int count = 0;
while ( ifs >> word ){ //while loop get from file input stream <ifstream>
strcpy(array[count++], word);
}

Use C++ strings in file handling

How to use C++ strings in file handling? I created a class that had C++ string as one of its private data members but that gave an error while reading from the file even if I am not manipulating with it at the moment and was initialised with default value in constructor. There is no problem while writing to the file. It works fine if I use C string instead but I don't want to. Is there a way to solve this?
class budget
{
float balance;
string due_name,loan_name; //string objects
int year,month;
float due_pay,loan_given;
public:
budget()
{
balance=0;
month=1;
due_name="NO BODY"; //default values
loan_name="SAFE";
year=0;
balance = 0;
due_pay=0;
loan_given=0;
}
.
.
.
};
void read_balance() //PROBLEM AFTER ENTERING THIS FUNCTION
{
system("cls");
budget b;
ifstream f1;
f1.open("balance.dat",ios::in|ios::binary);
while(f1.read((char*)&b,sizeof(b)))
{ b.show_data();
}
system("cls");
cout<<"No More Records To Display!!";
getch();
f1.close();
}
String is non-POD data-type. You cannot read/write from/in string by read/write functions.
basic_istream<charT,traits>& read(char_type* s, streamsize n);
30 Effects: Behaves as an unformatted input function (as described in
27.7.2.3, paragraph 1). After constructing a sentry object, if !good() calls setstate(failbit) which may throw an exception, and return.
Otherwise extracts characters and stores them into successive
locations of an array whose first element is designated by s.323
Characters are extracted and stored until either of the following
occurs: — n characters are stored; — end-of-file occurs on the input
sequence (in which case the function calls setstate(failbit | eofbit),
which may throw ios_base::failure (27.5.5.4)). 31 Returns: *this.
There is nothing about, how members of std::string placed. Look at, or use boost::serialiation. http://www.boost.org/doc/libs/1_50_0/libs/serialization/doc/index.html And of course you can write size of string and then write data and when read - read size, allocate array of this size, read data in this array and then create string. But use boost is better.
While reading the string members (due_name,loan_name) of your class budget your code literally fills them byte by byte. While it makes sense for floats and ints it won't work for strings.
Strings are designed to keep 'unlimited' amount of text, therefore their constructors, copy constructors, concatenations and so on must ensure to allocate the actual piece of memory to store the text and expand it if necessary (and delete upon destruction). Filling strings this way from disk will result in invalid pointers inside your string objects (not pointing to the actual memory which contains the text), actually no text will be actually read this way at all.
The easiest way to solve this is to not use C++ strings in that class. Work out the maximum length for each of the strings you will be storing, and make a char array that is one byte longer (to allow for the 0-terminator). Now you can read and write that class as binary without worrying about serialization etc.
If you don't want to do that, you cannot use iostream::read() on your class. You will need member functions that read/write to a stream. This is what serialization is about... But you don't need the complexity of boost. In basic terms, you'd do something like:
// Read with no error checking :-S
istream& budget::read( istream& s )
{
s.read( (char*)&balance, sizeof(balance) );
s.read( (char*)&year, sizeof(year) );
s.read( (char*)&month, sizeof(month) );
s.read( (char*)&due_pay, sizeof(due_pay) );
s.read( (char*)&loan_given, sizeof(loan_given) );
size_t length;
char *tempstr;
// Read due_name
s.read( (char*)&length, sizeof(length) );
tempstr = new char[length];
s.read( tempstr, length );
due_name.assign(tempstr, length);
delete [] tempstr;
// Read loan_name
s.read( (char*)&length, sizeof(length) );
tempstr = new char[length];
s.read( tempstr, length );
loan_name.assign(tempstr, length);
delete [] tempstr;
return s;
}
ostream& budget::write( ostream& s )
{
// etc...
}
Notice above that we've serialized the strings by writing a size value first, and then that many characters after.

How to use length indicator in a C++ program

I want to make a program in C++ that reads a file where each field will have a number before it that indicates how long it is.
The problem is I read every record in object of a class; how do I make the attributes of the class dynamic?
For example if the field is "john" it will read it in a 4 char array.
I don't want to make an array of 1000 elements as minimum memory usage is very important.
Use std::string, which will resize to be large enough to hold the contents you assign to it.
If you just want to read in word by word from the file, you can do:
vector<string> words;
ifstream fin("words.txt");
string s;
while( fin >> s ) {
words.push_back(s);
}
This will put all the words in the file into the vector words, though you will lose the whitespace.
In order to do this, you need to use dynamic allocation (either directly or indirectly).
If directly, you need new[] and delete[]:
char *buffer = new char[length + 1]; // +1 if you want a terminating NUL byte
// and later
delete[] buffer;
If you are allowed to use boost, you can simplify that a bit by using boost::shared_array<>. With a shared_array, you don't have to manually delete the memory as the array wrapper will take care of that for you:
boost::shared_array<char> buffer(new char[length + 1]);
Finally, you can do dynamic allocation indirectly via classes like std::string or std::vector<char>.
I suppose there is no whitespace between records, or you would just write file >> record in a loop.
size_t cnt;
while ( in >> cnt ) { // parse number, needs not be followed by whitespace
string data( cnt, 0 ); // perform just one malloc
in.get( data[0], cnt ); // typically perform just one memcpy
// do something with data
}