2

I'm currently having an issue navigating a txt file so I can read it into an array. The program compiles fine, but when I run it returns in terminal:

terminate called after throwing an instance of 'std::out_of_range'
  what():  basic_string::erase: __pos (which is 18446744073709551615) > this->size() (which is 14)
Aborted (core dumped)

Here is the code:

#include<cstdlib>
#include<cmath>
#include<fstream>
#include<sstream>
#include<iomanip>
#include<iostream>
#include<string>
#include<cstring>
#include<cassert>
#include<ctime>
#include<cctype>
#include<algorithm>
#include<locale.h>
#include<stdio.h>
#include<functional>
#include<math.h>

using namespace std;

int main(int argc, char**argv)
{
    int r=0;
    int p=0;
    int c=0;
    string names[20];
    double scores[20][10];

    ifstream infile;
    infile.open("./bowlers.txt");

    for(int r=1;r<=10;r++)
    {
        getline(infile,names[r]);

        p=names[r].find_first_of("\n") ;
        names[r].erase(p,2);

        for(c=1;c<=5;c++)
        {
        infile>>scores[r][c];
        }
        infile.ignore(100,'\n');
    }
    infile.close();

    for(int r=1;r<=10;r++)
    {
        cout<<fixed<<setprecision(2)<<endl;
        cout<<names[r]<<endl;

    }  

    return 0;
}

The txt file I'm using looks like this:

charles
123
321
222
rose
432
515
123
Greg
123
553
136

So here is what I have found out in researching this problem myself:

  1. EOLs are handled differently by Unix and Windows.
  2. Part of my problem is that:

        p=names[r].find_first_of('\n') ;
        names[r].erase(p,2);
    

    Is causing the issue, because \n is never found it returns -1, and you can't .erase -1?

I have tried using every conceivable of \r, \n, \r\n, etc. and I always recieve roughly the same output. I have also tried changing the encoding of the .txt file. The only difference is in (which is 14). The number will fluctuate depending on how I encode the .txt file. Also I have opened the .txt file in vim and :set list to see the newline characters. So I know they are there.

This is just partial code of a much larger project for school, and I am not very experienced with c++ yet. Can anyone point me in the right direction? I feel like once I get this portion of my code figured out I should be able to finish the project.

NOTE: the txt file is just an example so do not put too much thought into the size of my arrays or the parameters in my for loop. I have triple checked the sizes of my arrays to make sure there were no issues with me trying to read into a row that didnt exist.

  • 2
    The answer here is the same answer as "why is water wet" and "why is the sky blue". The whole purpose of `getline()` is to read the next line from the input stream, and save it in the `std::string` *excluding the newline*. You will never get an `\n` in the string populated by `std::getline()`, ***by definition***. That's how `std::getline()` works. – Sam Varshavchik Oct 15 '17 at 02:42
  • Maybe you're confused because you're used to `fgets()` in C, which keeps the newline in the string. But C++ `getline()` doesn't do that. – Barmar Oct 15 '17 at 02:45
  • 18446744073709551615 is 2 to the 64th power minus 1. This is the maximum unsigned 64 bit integer. A common way to get that is to feed -1 into a variable expecting an unsigned 64 bit integer. Odds are good that somewhere you try to look off the beginning of one of the elements of `names`. – user4581301 Oct 15 '17 at 02:46
  • Perhaps I should also add that my instructor in a similar example used `\r` and had it work perfectly for him using Netbeans on a Windows machine. Not sure if this clarifies further or not. – Misplaced Southerner Oct 15 '17 at 02:51
  • So @SamVarshavchik if `getline()` doesn't get `\n`. How would I manipulate the following lines to ignore the scores and also grab them for the `scores` array? I'm not looking for someone to do my homework for me but I believe I need a nudge in the right direction because I think I'll need to completely change my code. Is that correct? – Misplaced Southerner Oct 15 '17 at 02:58
  • @ColinCampbell On Windows, text files are opened in “text mode”, meaning that the CRLF (\r\n) is transformed by the underlying text device to a single LF (\n) for you. On *nixen there is no such thing as text mode, so those CRs (\ns) get passed right through. Your professor kind of used a bait-n-switch on you. He should be using *nix text files on *nix (lines end in LF), and Windows text files on Windows (lines end in CRLF). – Dúthomhas Oct 15 '17 at 03:06
  • The getline function will discard the the newline character. So you won't find "\n" in the line. – James Dong Oct 15 '17 at 03:15
  • @Dúthomhas Hmmm, I tried saving the .txt as windows, but that didn't seem to help either. I believe I will just put a pin in doing this on a Linux machine and do it on Windows with Netbeans. Short-term I will need the grade, but long-term I will have to come back and figure this out on Linux. Thank you. – Misplaced Southerner Oct 15 '17 at 03:17
  • Crud, I said in my above comment that the \ns get passed through. \ns are LFs. The CRs (\rs) get passed through. If you are programming on Windows, you'd need to open your file in “binary” mode to see the CR (\r) characters. Sorry for the typo! – Dúthomhas Oct 15 '17 at 03:42
  • "ignore the scores and also grab them for the scores array" -- this statement makes no logical sense, whatsoever. If something gets ignored, it gets ignored. Nothing else happens, certainly it doesn't get "grabbed", in some unspecified, vague, nebulous way. One mandatory prerequisite towards having any possibility of getting your question answered is to ask a question that can actually be logically parsed. – Sam Varshavchik Oct 15 '17 at 03:42
  • As far as I ca see, you are searching for something that contributes nothing to your project. What is exactly you need the new line for?! – CroCo Oct 15 '17 at 03:46
  • @SamVarshavchik My apologies, perhaps I should say it like this: I want my `names` array to only contain the names in the txt file, and I want the numbers in the .txt file to be loaded into the `scores` array. Does that clarify at all? – Misplaced Southerner Oct 15 '17 at 03:48
  • Yes, that's more clear. However what's still not clear is why the presence/absence of a trailing newline has anything to do with anything. Every line in a text file ends with a newline. So, every line's newline needs to get stripped off, before looking at what the line contains, and this is true whether a line contains a name or a score, in your case. Who cares about newlines. `std::getline()` will deal with them. Read one line at a time, with `std::getline()`, if it has a name, create a new record for it; if it's a score, add the score to the most recent created record. Task complete. – Sam Varshavchik Oct 15 '17 at 03:58

2 Answers2

1

Always check the return of value of find functions. Example:

size_t p = names[r].find_first_of("\n");
if (p != string::npos)
    names[r].erase(p, 2);

If \n is not found, the return value is string::npos (it could be 0xFFFFFFFF or 0xFFFFFFFFFFFFFFFF) which is invalid index. Attempting to access that index results in error.

As noted in comments, names[r] does not contain \n in this case. p is always string::npos and this operation is not required.

for(c=1;c<=5;c++)
{
infile>>scores[r][c];
}

You only have 3 integers below each name, so you should count to 3, not 5. This code should work:

for(int r = 1; r <= 10; r++)
{
    getline(infile, names[r]);
    for(int c = 1; c <= 3; c++)
        infile >> scores[r][c];
    infile.ignore(100, '\n');
}

Or you can add more error checking, example if (!(infile >> scores[r][c])) break;

Barmak Shemirani
  • 30,904
  • 6
  • 40
  • 77
0

I don't know why you need to detect the newline. If you are looking for extracting names and numbers, you can do the following

string word;
int i(0); 
while ( infile >> word ){
    if(!(i%4)){
        //name 
        std::cout << word << endl;
    }else{
        //number
    }
    ++i;
}

Take advantage of knowing the exact format of your file. The file is already in a perfect and easy way to be manipulated. Also, if you don't know the size of your data. I encourage you to use vector over a fixed-size array.

CroCo
  • 5,531
  • 9
  • 56
  • 88