How to Read last line of a delimited File - c++

I have a delimited file with semi-colon delimiter.
I am using the following code to read the last line. I believe the loop runs until the last line and keeps overwriting the lastLine string. So once the last line is reached, the loop breaks and the string is the last line.
while(getline(finlocal, chuy, ';'))
{
getline(finlocal,lastLine, ';');
}
cout<<lastLine; //last line.
But this method does not work properly and efficiently.
Any suggestions on How to know the last line of this delimited file.

You did not explain what is your problem but I guess it doesn't check every line because the getline() inside the loop points to next element, so you lose half of your elements.
while(getline(finlocal, chuy, ';'))
{
}
std::cout<<chuy;
should work
Let's say this is your lines:
1.; <--
2.;
3.;
4.;
...
after the first iteration it goes to:
1.; <--
2.; <--(by while)
3.; <--(by inside getline)
4.;
...
So you lose the 2nd element. Loop goes on and in the end
5.; <-- (by inside loop)
end.; <-- (by while): this assignes the right line to chuy and points to next element
which does not exist and so inside getline() doesn't work

use
while(getline(finlocal, lastLine, ';'));
cout<<lastLine;
or otherwise you will might not get the last line.
you can use
while(getline(finlocal, lastLine, ';'))
{
getline(finlocal,lastLine, ';');
}
cout<<lastLine;
but there is no point in doing getline twice
for a better delimiter in your case i would have used a special sign, say do:
char delimiter = 251;
that char is the sign for square root, and most probably won't be used in chat. still, for each chat message that arrives you need to check if the sign is in use there and if so delete it. now for each message you send, send with the sign at the end of it, and when receiving, split at that character with the getline.
that way you split easily in a place you know the user won't mess.

if the file wouldn't be really big, you may first get the whole file in a string and use string::find_last_of to find the last index of semicolon. Then print the line from that index till end of file.

Related

How does cin works in a while loop when the inputs are given in a single line with white spaces?

Consider this small piece of code:
#include <iostream>
#include<stdio.h>
using namespace std;
int main() {
int a;
while(true){
cin>>a;
cout<<a;
}
return 0;
}
Input
1 2 3 5 7 23
Output
125723
How I thought it will run is:
First iteration
1. Reads the first input ie '1' and stops reading further, right after reading the whitespace.
2.Prints the value 1.
Second iteration
1. Again asks for new input
2. Print that in the second line
But that doesn't happen instead it reads the elements we gave after space
First iteration:
Peek at next character in the stream. It's a digit ('1'), so read it.
Peek at next character in the stream. It's not a digit (' '), so don't read it; store 1 in a and return from >>.
(Output 1.)
Second iteration:
Peek at next character in the stream. It's whitespace (' '), so read and ignore it.
Peek at next character in the stream. It's a digit ('2'), so read it.
Peek at next character in the stream. It's not a digit (' '), so don't read it; store 2 in a and return from >>.
(Output 2.)
And so on ...
The point is that >> does not care about lines. cin is one long input stream of characters (some of which may be '\n'). The only thing you can do is read more characters (and then maybe decide that you don't want to do anything with them).
cin is not necessarily connected to a keyboard. The program that started you gets to decide where cin reads from. It can be a file, a network socket, or interactive user input. In the latter case, reading from cin may block until the user types more input, but it will never cause input to just be dropped.
If you want a sane user interface, always read whole lines and process them afterwards:
std::string line;
while (std::getline(std::cin, line)) {
// do stuff with line
}

Why is the length of a string off by one on the first read of a file?

I am perplexed with the way my program is performing. I am looping the following process:
1) take the name of a course from an input file
2) output the length of the name of the course
The problem is that the first value is always one less than the actual value of the string.
My first string contains 13 characters (including the colon), but nameOfClass.length() returns 12. The next string, the character count is 16 and indeed, nameOfClass.length() returns 16.
Every value after that also returns the expected value, it is only the first that returns the expected value minus 1.
Here's the (reduced) code:
std::ifstream inf("courseNames.txt");
int numberOfClasses = 10;
string nameOfClass;
for (int i = 0; i < numberOfClasses; i++) {
std::getline(inf, nameOfClass,':');
std::cout << nameOfClass.length() << "\n";
}
The file looks like this (courseNames.txt):
Pre-Calculus:
Public-Speaking:
English I:
Calculus I:
...etc. (6 more classes)
This is what I get:
12
16
10
11
Can anyone explain this behavior of the .length() function?
You have a problem, but you have the wrong conclusion. std::getline reads but doesn't output the delimiter, and so the first result is indeed 12.
It also doesn't output the delimiter for any subsequent lines, so why is there always one more? Well, look what is after that :. That's right, a new line!
Pre-Calculus:
^ a new line
So your nameOfClass variable, except for the first string, always stores an extra newline before the other characters.
The fix is easy enough, just ignore the newline after reading the string.
inf.ignore(); // ignore one character
So, not the first result was wrong, it was the only one right :)

confusion about lists and pairs

So I'm experimenting with trying to add first and last names into a double linked list.
I have a various text files of different lengths with the format "string, string", and am using list> to store my data.
I am using this code:
typedef std::list< std::pair<string,string> > listPair;
...
list<pair<string, string> > mylist;
ifstream myFile;
myFile.open("20.txt");
pair<string, string> stuff;
while (myFile >> stuff.first >> stuff.second)
{
mylist.push_back(stuff);
}
listPair::iterator iter = mylist.begin();
for(;iter != mylist.end();iter++)
{
string s = (*iter).first;
cout << s << endl;
string c = (*iter).second;
cout << c << endl;
}
now the problem i'm having is that firstly, the last item in the list is not being added.
like every file just misses the end line, so that's a little confusing.
also, I'm doing a "mylist.size()" to ensure all the names have been added, and it's confusing me because say for a text file containing 99 names, i.e 99 lines of text, it will say (not forgetting it only reads in 98 due to missing the last line) that the list has size 48.
WHY 48?
Is it something to do because I have done pairs, which still would not make sense as if it was not reading in pairs there would actually be double the about, since the pairs are just to take the first and last name as one value.
Mind boggling to me.
once again thanks for all your help!
I have a feeling your file doesn't actually have spaces between the values as you've described, so it looks like this:
one,two
three,four
five,six
seven,eight
nine,ten
If you were to run your program on this, the size of the list will be 2 (floor(number_of_lines/2), which for you would give 48) and the last line won't have been put in the list at all. Why?
Firstly, each call to std::ifstream::operator>>(std::string&) will extract up until it hits some white space. In this case, the first white space on the first line is the \n at the end of it. So on the first iteration, stuff.first will be "one,two" and then the next line will be read into stuff.second, making it "three,four". This is then pushed into the list. The next two lines are read in the same way, giving you the pair {"five,six","seven,eight"}. On the next iteration, the first operator>> will extract "nine,ten" and the second will fail, causing the while condition to end and the last line to be discarded.
Even if you did have spaces, you would end up with commas in the first of every pair, which is certainly not what you want.
The nicer way to approach a problem like this is to use std::getline to extract a line, and then parse that line as appropriate:
std::string line;
std::pair<std::string, std::string> line_pair;
while (std::getline(myFile, line)) {
std::stringstream line_stream(line);
std::getline(line_stream, line_pair.first, ',');
std::getline(line_stream, line_pair.second);
mylist.push_back(line_pair);
}
I also recommend using std::vector unless you have a good reason to use std::list.
Operator >> on ifstream treats newline as yet another token. Hence it will probably read your first and second word as per normal from your first line, but the third token read is the new line on the first line.
Try using getline to 'eat' the newline as well.

I need to pick numbers out of a long string

I have a string of about a thousand digits in a .txt file. I need to evaluate one digit at a time, compare it with adjacent digits, then move down the list and do it again. I'm using C++ and the get() function. Here's what I have so far:
int element[5];
ifstream file;
file.open("theNumber.txt", ios::in);
for(int i=0;i<5;i++)
{
file.seekg(1);
element[i]=file.get();
}
//read first 5 numbers.
Right now my code won't compile, and showing it all would make most of you cry, but I wanted to check to see if This part was correct. Will this give me an array with the first five digits of the number in the file?
Will this give me an array with the first five digits of the number in the file?
No, your seekg call is setting the read position to the second character every time you call it; just throw that call away (get() automatically advances the read position).
You also need to handle the text to binary conversion. Easiest to do like this:
int ch = file.get();
if (ch < '0' || ch > '9')
{
// Handle invalid input or EOF/error...
}
element[i] = ch - '0';
Will this give me an array with the first five digits of the number in the file?
No, sorry. It will give you the second digit of the file, five times over.
There are two versions of seekg: one that sets the file pointer's position from the beginning and one that sets it relative to some other position. The line file.seekg(1); sets the file pointer to absolute position 1: the second byte of the file. Thus your array contains the same digit repeated.
Consider changing the 1 to i in the call, if you want to use that particular seekg overload.
Good luck.
Also, as Brendan and spencercw note, you'll still have to convert the ASCII code.

C++ adding a carriage return at beginning of string when reading file

I have two questions:
1) Why is my code adding a carriage return at the beggining of the selected_line string?
2) Do you think the algorithm I'm using to return a random line from the file is good enough and won't cause any problems?
A sample file is:
line
number one
#
line number two
My code:
int main()
{
srand(time(0));
ifstream read("myfile.dat");
string line;
string selected_line;
int nlines = 0;
while(getline(read, line, '#')) {
if((rand() % ++nlines) == 0)
selected_line = line;
}
// this is adding a \n at the beginning of the string
cout << selected_line << endl;
}
EDIT: OK, what some of you suggested makes a lot of sense. The string is probably being read as "\nmystring". So I guess my question now is, how would i remove the first \n from the string?
What you probably want is something like this:
std::vector<std::string> allParagraphs;
std::string currentParagraph;
while (std::getline(read, line)) {
if (line == "#") { // modify this condition, if needed
// paragraph ended, store to vector
allParagraphs.push_back(currentParagraph);
currentParagraph = "";
else {
// paragraph continues...
if (!currentParagraph.empty()) {
currentParagraph += "\n";
}
currentParagraph += line;
}
}
// store the last paragraph, as well
// (in case it was not terminated by #)
if (!currentParagraph.empty()) {
allParagraphs.push_back(currentParagraph);
}
// this is not extremely random, but will get you started
size_t selectedIndex = rand() % allParagraphs.size();
std::string selectedParagraph = allParagraphs[selectedIndex];
For better randomness, you could opt for this instead:
size_t selectedIndex
= rand() / (double) (RAND_MAX + 1) * allParagraphs.size();
This is because the least significant bits returned by rand() tend to behave not so randomly at all.
Because you don't specify \n as a delimeter.
Your "random" selection is completely wrong. In fact, it will always select the first line:
rand() % 1 is always 0.
There is no way to uniformly select a random line without knowing the number of lines present.
In addition, why are you using # as a delimiter? Getline, by default, gets a line (ending with \n).
The newlines can appear from the second line that you print. This is because, the getline function halts on seeing the # character and resumes the next time it is called from where it left of i.e. a character past the # which as per your input file is a newline. Read the C FAQ 13.16 on effectively using rand().
One suggestion is to read the entire file in one go, store the lines in a vector and then output them as required.
Because # is your delimeter, the \n that exists right after that delimeter will be the beginning of your next line, thus making the \n be in front of your line.
1) You're not adding a \n to selected_line. Instead, by specifying '#' you are simply not removing the extra \n characters in your file. Note that your file actually looks something like this:
line\n
number one\n
#\n
line number two\n
<\pre>
So line number two is actually "\nline number two\n".
2) No. If you want to randomly select a line then you need to determine the number of lines in your file first.
You could use the substr method of the std::string class to remove the \n after you decide which line to use:
if ( line.substr(0,1) == "\n" ) { line = line.substr(1); }
As others have said, if you want to select the lines with uniform randomness, you'll need to read all the lines first and then select a line number. You could also use if (rand() % (++nlines+1)) which will select line 1 with 1/2 probability, line 2 with 1/2*1/3 probability, etc.