Reading lines of txt file into array prints only the last element - c++

First of all, I didn't code in C++ for more then 8 years, but there is a hobby project I would like to work on where I ran into this issue.
I checked a similar question: Only printing last line of txt file when reading into struct array in C
but in my case I don't have a semicolon at the end of the while cycle.
Anyway, so I have a nicknames.txt file where I store nicknames, one in each line.
Then I want to read these nicknames into an array and select one random element of it.
Example nicknames.txt:
alpha
beta
random nickname
...
Pirate Scrub
Then I read the TXT file:
int nicknameCount = 0;
char *nicknames[2000];
std::string line;
std::ifstream file("nicknames.txt");
FILE *fileID = fopen("asd.txt", "w");
while (std::getline(file, line))
{
nicknames[nicknameCount++] = line.data();
// (1)
fprintf(fileID, "%i: %s\n", nicknameCount - 1, nicknames[nicknameCount - 1]);
}
int randomNickIndex = rand() % nicknameCount;
// (2)
for (int i = 0; i < nicknameCount; i++)
fprintf(fileID, "%i: %s\n", i, nicknames[i]);
fprintf(fileID, "Result: %s\n", nicknames[randomNickIndex]);
fprintf(fileID, "Result: %i\n", randomNickIndex);
fclose(fileID);
exit(0);
What then I see at point (1) is what I expect; the nicknames. Then later at point (2) every single member of the array is "Pirate Scrub", which is the last element of the nicknames.txt.
I think it must be something obvious, but I just can't figure it out. Any ideas?

line.data() returns a pointer to the sequence of characters. It is always the same pointer. Every time you read a new line, the contents of line are overwritten. To fix this, you will need to copy the contents of line.
Change:
char *nicknames[2000];
to
char nicknames[2000][256];
and
nicknames[nicknameCount++] = line.data();
to
strcpy(nicknames[nicknameCount++], line.data());
However, using a vector to store the lines is probably better, since this is C++

Your nicknames array does not contain copies of the strings, all the nicknames are pointers to the same data owned by line.
Instead of char* nicknames[2000] i would recommend you use
std::vector<std::string> nicknames;
and then inside the loop:
nicknames.push_back(line);

This:
char *nicknames[2000];
is an array of 2000 pointers to char. Nowhere in your code you are actually storing the strings from the file. This
nicknames[nicknameCount++] = line.data();
merely stores pointers to the lines internal buffer in the array. In the next iteration this buffer is overwritten with contents of the next line.
Forget about all the C i/o. Mixing C and C++ is advanced and you don't need it here. If you want to store a dynamically sized array of strings in C++, that is a std::vector<std::string>:
std::vector<std::string> lines;
std::string line;
while (std::getline(file, line))
{
lines.push_back(line);
}
Also for writing to the output file you should use an std::ofstream.

Related

Vector.push_back() adds same Element while reading from File

My Code is reading a set of Elements from a file and adds those to a Vector. The for-loop reads all the elements and via push_back they are added to the vector. Works perfectly fine on paper BUT: In the end all the Elements in the Vector are equal and always the last read element.
I am 100 percent certain the Elements listed in the File aren't the same (because of the good old NotePad++). Ive tried to c-out the read in elements to check if there is a problem with the f_read function. The Program outputted the Elements perfectly fine and in the right order. I am guessing the error isn't with the file or the f_read function.
FILE* f = fopen(filepath, "rb");
unsigned char header[19];
fread_s(header, sizeof(header), sizeof(unsigned char), 19, f);
vector<char*> myVector;
int size = 28 * 28;
char temp[28 * 28];
for (int i = 0; i < 2; i++) {
fread_s(temp, 28*28, sizeof(unsigned char), size, f);
myVector.push_back(temp);
}
( the 19 bits i am reading into the "info" char array are the header)
I Expect the Vector to contain all the Read Elements in the right order.
As mentioned in the comments, you are pushing the pointers back not the actual strings. To get the actual strings you can do this:
void readFileToVec()
{
ifstream file;
file.open ("rb");
vector<string> v;
string word;
while (file >> word)
{
v.push_back(word);
}
}
This will work if the elements are all strings and are seperated by a space,tab, or newline. If your words are seperated by anything other than one of these three (for example a list of words separated by commas) then you can use getline and specify the separator.
In any case, reading about C++ streams and the difference between C-style strings and STL strings would be worthwhile if you intend to do this sort of thing with C++ again. You are using C Strings and old school FILE which is part of the C Library while C++ provides you with utilities to make your life easier. File streams and C++ strings are great examples of such utilities.

Reading file into two arrays

I'm trying to write my own vocabulary with a test for my little brother, but I have a problem when I want to read data from file into two arrays - first with English words, and second with Polish words. File looks alike
black - czarny
red - czerwony etc.
And my function:
void VOC::readout()
{
fstream file;
VOC *arr = new VOC;
string line;
file.open("slowka.txt");
if(file.good())
{
int i=0;
while(!file.eof())
{
getline(file, line);
size_t pos = line.find(" - ");
int position = static_cast<int>(pos);
file>>arr[i].en;
file>>arr[i].pl;
++i;
}
}
}
I thought it could be a good idea to insert a line into first array until the function finds " - ", and after that insert the rest of line into second array, but I have some problems with that. Could someone help me? I know I can solve it by using std::vector but I care to do that by using arrays.
If you insist on using plain arrays, you'll first have to count the number of lines in your file and then allocate enough memory. Arrays -- unlike std::vector objects -- won't grow automatically but have a fixed size.
That being said, note that using !file.eof() is not the best way to read a stream until the end is reached. You can use the simpler
std::string line;
while (std::getline(file, line)) {
// ...
}
idiom instead, which also takes care of error conditions. See this question (and corresponding answers) for more information on that.

Merging two text files gives wierd results

I need to merge two text files by putting them in a vector array and then writing them in a new text file.
After merging them.The new file has extra characters.
FE:
f1.txt ("text1")
f2.txt ("text2.")
f12.txt ("text1˙text2.˙W64")
Content of the buffer: "text1 text2. W64"
Here is the code:
int main(){
enum errorcode{FNF,FNC};
vector<char> buffer;
char ime[255];
cin>>ime;//first file
ifstream ud1(ime,ios::in);
if(ud1.is_open()){
while(!ud1.eof())buffer.push_back(ud1.get());
ud1.close();
}
else {cout<<"File not found.";return FNF;}
cin>>ime;//second file
ifstream ud2(ime,ios::in);
if(ud2.is_open()){
while(!ud2.eof())buffer.push_back(ud2.get());
ud2.close();
}
else {cout<<"File not found.";return FNF;}
cin>>ime;//new file
ofstream id(ime,ios::out);
if(id.is_open()){
for(int i=0;i<buffer.capacity();i++)id.put(buffer[i]);
id.close();
}
else {cout<<"File not created.";return FNC;}
return 0;
}
I guess this is because of notepad or files themselves.
Can you please tell me reason for this.
you are using Vector capacity: Returns the size of the storage space currently allocated for the vector, expressed in terms of elements.
You must use vector size: Returns the number of elements in the vector. This is the number of actual objects held in the vector, which is not necessarily equal to its storage capacity.
About the ˙
please look at istream::get return value:
Return Value
The first signature returns the character read, or the end-of-file value (EOF) if no characters are available in the stream (note that in this case, the failbit flag is also set).
So, you could change the loop to this:
while(!ud1.eof()){
int tmpChar = ud1.get();
if( !ud1.eof() )
buffer.push_back(tmpChar);
}

C++ Reading from a text file into a const char array

I want to read in lines from a text file into a 2-d char array but without the newline character.
Example of .txt:
TCAGC
GTAGA
AGCAG
ATGTC
ATGCA
ACAGA
CTCGA
GCGAC
CGAGC
GCTAG
...
So far, I have:
ifstream infile;
infile.open("../barcode information.txt");
string samp;
getline(infile,samp,',');
BARCLGTH = samp.length();
NUMSUBJ=1;
while(!infile.eof())
{
getline(infile,samp,',');
NUMSUBJ++;
}
infile.close(); //I read the file the first time to determine how many sequences
//there are in total and the length of each sequence to determine
//the dimensions of my array. Not sure if there is a better way?
ifstream file2;
file2.open("../barcode information.txt");
char store[NUMSUBJ][BARCLGTH+1];
for(int i=0;i<NUMSUBJ;i++)
{
for(int j=0;j<BARCLGTH+1;j++)
{
store[i][j] = file2.get();
}
}
However, I do not know how to ignore the newline character. I want the array to be indexed so that I can access a sequence with the first index and then a specific char within that sequence with the second index; i.e. store[0][0] would give me 'T', but I do not want store[0][5] to give me '\n'.
Also, as an aside, store[0][6], which I think should be out of bounds since BARCLGTH is 5, returns 'G',store[0][7] returns 'T',store[0][8] returns 'A', etc. These are the chars from the next line. Alternatively, store[1][0],store[1][1], and store[1][2] also return the same values. Why does the first set return values, shouldn't they be out of bounds?
As you're coding in C++, you could do like this instead:
std::vector<std::string> barcodes;
std::ifstream infile("../barcode information.txt");
std::string line;
while (std::getline(infile, line))
barcodes.push_back(line);
infile.close();
After this the vector barcodes contains all the contents from the file. No need for arrays, and no need to count the number of lines.
And as both vectors and strings can be indexed like arrays, you can use syntax such as barcodes[2][0] to get the first character of the third entry.

How can ofstream write NULL to a file in binary mode?

I am maintaining a C++ method which one of my clients is hitting an issue with. The method is supposed to write out a series of identifiers to a file delimited by a new line. However on their machine somehow the method is writing a series of NULL's out to the file. Opening the file in a binary editor shows that it contains all zeros.
I can't understand why this is happening. I've tried assigning empty strings and strings with the first character set to 0. There is no problem creating the file, just writing the identifiers to it.
Here is the method:
void writeIdentifiers(std::vector<std::string> IDs, std::string filename)
{
std::ofstream out (filename.c_str(), std::ofstream::binary);
if (out.is_open())
{
for (std::vector<std::string>::iterator it = IDs.begin();
it != IDs.end();
it++)
{
out << *it << "\n";
}
}
out.close();
}
My questions: is there any possible input you can provide that method which will create a file which has NULL values in it?
Yeah, the following code quite clearly writes a series of NULL bytes:
std::vector<std::string> ids;
std::string nullstring;
nullstring.assign("\0\0\0\0\0\0\0\0\0\0", 10);
ids.push_back(nullstring);
writeIdentifiers(ids, "test.dat");
Because the std::string container stores the string length, it can't necessarily be used in the same way as an ordinary C (null-terminated) string. Here, I assign a string containing 10 NULL bytes. Those are then output because the string length is 10.