Segmentation fault when reading text file - c++

I am trying to run a code sorting the ages of Titanic survivors from a text file. It compiles fine, but the program terminates simply saying "segmentation fault" when I choose option B (option A is not written yet.)
Here is a small sample of the text file for reference.
29 1stClass TRUE
0.9 1stClass TRUE
2 1stClass FALSE
30 1stClass FALSE
I've isolated the error to the chunk where the file is processed (//actual processing), but I'm not sure what exactly is wrong.
#include <iostream>
#include <iomanip>
#include <vector>
#include <string>
#include <fstream>
#include <ctype.h>
void sortSurvivors();
void sortAgesLiving();
int main()
{
char options;
std::cout << "Titanic Data \nOptions \nA) Display count of people who lived and died... \nB) Display count of people who lived by age... \nPlease select option (A-B)...";
std::cin >> options;
switch (options)
{
case 'A':
sortSurvivors();
break;
case 'B':
sortAgesLiving();
break;
}
}
void sortSurvivors()
{
}
void sortAgesLiving()
{
std::ifstream inputFile;
std::string filename = "TitanicData.txt";
std::string age;
std::string classBoat;
std::string survival;
bool survived;
int eldest = 0;
//pre-sort processing
while (inputFile >> age >> classBoat >> survival)
{
int ageConv = stoi(age);
//G is for the ghetto fix I am pulling here, because I recieve an error when using "TRUE" as a string
char gchar = 'G';
survival += gchar;
if (survival == "TRUEG")
{
survived = true;
}
else
{
survived = false;
}
if (eldest < ageConv)
{
eldest = ageConv;
}
}
//initialize vector
std::vector<int> survivorVector;
for (int i = 0; i < eldest; i++)
{
survivorVector.push_back(0);
}
inputFile.open(filename);
//actual processing (ERROR HERE)
if (inputFile)
{
while (inputFile >> age >> classBoat >> survival)
{
int ageConv = stoi(age);
if (survived = true)
{
survivorVector[ageConv] = survivorVector[ageConv] + 1;
}
for (int j = 0; j <= eldest; j++)
{
std::cout << j << "\t" << survivorVector[j] << "\n";
}
}
// Close the file.
inputFile.close();
}
else
{
std::cout << "I don't know what broke, but uhhhhhhhhhh oops.";
}
}
As per usual I'm sure it's something dumb I overlooked.

In sortAgesLiving(), you have forgotten to open the file before starting your pre-sort processing. As a consequence, your first reading loop will fail to read anything at all. Therefore eldest will stay 0.
You then construct a vector and populate it. But since the loop is based on eldest, the vector survivorVector will stay empty.
When you finally open the file and read it, the first line will be considered as a survivor since you accidentally overwrite the boolean with true (i.e. if (survived = true) instead of if (survived == true) or simply if (survived). You'll then try to access the vector out of bounds.
Even if you correct this error, at the first survivor you'll get again out of bounds. Accessing a vector out of bounds is UB and one of the many possible symptoms can be segmentation fault.
Miscellaneous advices (not related to your issues):
You have an ambiguous age of 0.9. Converting it to an int will cause it to be 0. Is this ok, or do you need to round this up?
If it's rounding up, you could make the age variable a double and read it directly without conversion. You could then convert it mathematically to an integer age rounding it up or truncating it, as needed. If you're sure to have only integers, you could make the variable an int and not worry at all.
It is unsafe to trust a value in a file to directly index a vector. What if between the two reading phases, an additional line would have been added by someone else to the file with a value higher than eldest ? What if the value read would be negative? Better always check that it's in an acceptable range before using a value as an index. It can save you hours of debugging and your customers some nightmares.
Finally, the two-phase read is not necessary: you could just read the age, and after having checked that it's positive and smaller than 150 years (quite optimistic), you could, if needed, resize your vector if the age is equal or larger than the current vector size. Why? Imagine you work one day for US census with files having millions of lines: the fewer passes over the file, the better ;-)

Related

g++ printed undesired 00000, why?

Below is my code;
int main(){
ifstream infile;
infile.open(fin);
ofstream outfile;
outfile.open(fout);
char c;
int input_order = 0;
string comp_str = "";
vector <string> pfx_str;
srand(time(NULL));
if (infile.fail())
{
cout << "cannot open file!" << endl;
return 0;
}
while (!infile.fail())
{
cout << input_order << endl;
c = infile.get();
if (c == '\n')
{
if (strcmp(comp_str.c_str(), "") != 0)
{
pfx_str.push_back(comp_str);
}
int num = rand() % pfx_str.size();
while (num == 0)
{
num = rand() % pfx_str.size();
}
for (int i = 0; i < num; i++)
{
outfile << "/" << pfx_str.at(i);
}
outfile << "\n";
input_order++;
pfx_str.clear();
}
else if (c == '/')
{
if (comp_str != "")
{
pfx_str.push_back(comp_str);
}
comp_str = "";
}
else
{
comp_str = comp_str + c;
}
}
infile.close();
outfile.close();
return 0;
}
For small set which consist of 10k inputs, it works.
However, for big set such as using 1600k inputs, it prints out 00000, and does not work. What makes it happened? and how to make it correctly working?
(Previously, I used this code for 1600k input and it works correctly....)
In compile, I used g++ -std=gnu++0x .....
I googled this issue but could not find out the right answer.. And also I could not figure out what this issue comes from....
Thanks,
+
This code is for randomly cutting the input.
This is the example of 1 input set; (to show the input pattern)
/aa/ab/bc/aaa/
Here, I consider 'aa', 'ab', 'bc', and 'aaa' as one component.
And I want randomly cut this input as components unit.
this is the brief step of the code;
1. generate the random number(except 0)
2. ex) I use the above input and the random number is 2.
then I cut this input and only '2' components is left, which is /aa/ab/
(repeat this procedure for each inputs in input text file
=> this input; /aa/ab/bc/aaa/
(inside, it generate random number 2)
output to be printed in output file; /aa/ab/
There is nothing wrong with your code.
I updated your code so that it compiles with g++:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std;
int main(){
ifstream infile;
infile.open("fin.txt"); // substituted a real file name in here to test
ofstream outfile;
outfile.open("fout.txt"); // ditto here
...the rest is the same as what you put above.
named it test.cpp, and compiled it with:
g++ -lm test.cpp -o test.exe
I wrote a Ruby script to make the input file according to the test set you specified in the comments:
#!/usr/bin/env ruby
File.open( 'fin.txt', 'w') do |f|
1600000.times do
f << "/aa/ab/bc/aaa/\n"
end
end
I then ran the compiled program test.exe, and you totally owe me a beer for watching your line numbers go that high. This is time spent from my life for you that I will never get back. 😂
and got an expected fout.txt with
/aa
/aa/ab
/aa
/aa/ab
/aa/ab/bc
/aa/ab/bc
/aa/ab
/aa/ab/bc
/aa/ab
etc.
My guess is that your system's constraints are causing a system fault. I ran this on a typical desktop machine with a ton of memory, etc. If you're running it on an embedded system, it may not have similar resources. At this point, I was tempted to test on a Raspberry Pi, but I'm burning too much time on this already. Just know that there is nothing wrong with your code.
In the future, try to figure out what your coding problem is. Stack Overflow is super forgiving if you've really tried and can't figure out the solution, but you need to show what you've tried and what happened as a result. For problems that started out like this, where you think that you're trying to figure out an algorithmic problem, always give the data set and the unexpected result that occurred.
Good luck!

Calculating sum and mean in a list pulled from a file

I suppose before even requesting help, I should mention that this one of the various exercises in my Computer Science course to help us further understand how to pull inputs from files and such. Any help is appreciated, whether it's direct "give it all away" answers or pseudocode-type responses. So, here's the prompt.
Write a C++ program that reads lists from a file and reports the name, size, mean if appropriate, and second largest number if appropriate for each list; and reports the list name and sum of the list with the largest sum. The file to be read consists of: a file sentinel, a number not equal to any of the list sentinels... then zero or more of: a list sentinel, a number not equal to any value in this list; the list name, readable as a C++ STL string; zero or more list values separated by whitespace; the list sentinel then the file sentinel.
It includes an example, and here's what the example shows.
31 17.3 FIRST 26.2 -11.2 8.1 17.3 0.0 SECOND 0.0 31
EXPECTED OUTPUT:
First size is 3, mean is 7.7, second largest value is 8.1. Second size is 0. First has the largest sum of the list(s) with 23.1.
Sorry, I know it's all sort of confusing. I'm pretty confused myself, but that's a college course for ya. So, anyways, here is what I've got so far!
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int main()
{
// loop once per file
// stop if filename string is "end" or "done"
while (true) {
string filename;
cout << "Enter filename or \"end\" to quit: ";
cin >> filename;
if (filename == "end" || filename == "done")
{
break;
}
// open the file
ifstream infile(filename);
cout << "Processing " << filename << ", please wait...\n";
// processing just if file is opened
if (infile)
{
// count the number of lists
int numLists = 0;
int x = 0;
infile >> x;
while (!infile.fail())
{
++numLists;
// read one list
while (x != 31)
{
infile >> x;
}
infile >> x;
}
cout << filename << " contains " << numLists << " lists.\n";
}
}
}
So, my output is working so far concerning determining how many lists are in each file, however, I've tried several things to get the sum and the mean of the lists, but I just don't know how to store the values per list, per file. I've thought about arrays, or vectors, but I just really don't know. I'd appreciate any help anyone could offer. Please!
Here's some code to read the lists in the way the questions asks. I have not tested this code, so apologies for any mistakes, but it does correspond to the question.
ifstream in(filename);
// read the file sentinel first
double file_sentinel;
in >> file_sentinel;
for (;;)
{
// read next sentinel
int sentinel;
in >> sentinel;
// if it's a file sentinel then we are done
if (sentinel == file_sentinel)
break;
// it must be a list sentinel so read the list starting with the name
string name;
in >> name;
// start of list processing
...
for (;;)
{
// read the next item in the list
double item;
in >> item;
// check if the item is actually the list sentinel, if so the we're done with this list
if (item == sentinel)
break;
// now process list item
...
}
// now process whole list
...
}
There's no error checking (not sure if that is necessary) and I've left some blanks for the processing the question asks you to do. Hopefully it will give you a start.

C++ in Xcode pausing

this is my first SO post.
I am very new to programming, and with C++ I thought I might try and make a program that allows the user to submits a block of text (max 500 characters), allows them to enter a 4 letter word and the program return with the amount of times it picks that word up in the text.
I am using X-code and it keeps making a green breakpoint and pausing the program at the 'for' loop function. my code is shown below:
#include <iostream>
#include <string>
#include <math.h>
#define SPACE ' '(char)
using namespace std;
//Submit text (maximum 500 characters) and store in variable
string text;
string textQuery(string msgText) {
do {
cout << msgText << endl;
getline(cin, text); } while (text.size() > 500);
return text;
}
//Query word to search for and store as variable
string word;
string wordQuery(string msgWord) {
cout << msgWord << endl;
cin >> word;
return word;
}
//Using loop, run through the text to identify the word
int counter = 0;
bool debugCheck = false;
int searchWord() {
for (int i = 0; i < text.size(); i++) {
char ch_1 = text.at(i);
char ch_2 = text.at(i + 1);
char ch_3 = text.at(i + 2);
char ch_4 = text.at(i + 3);
cout << i;
if(ch_1 == word.at(0) &&
ch_2 == word.at(1) &&
ch_3 == word.at(2) &&
ch_4 == word.at(3) )
{
counter++;
debugCheck = true;
}
}
return counter;
}
//cout the result
int main() {
string textUserSubmit = textQuery("Please submit text (max 500 characters): ");
string wordUserSubmit = wordQuery("Please select a word to search for: ");
int counterResponse = searchWord();
cout << debugCheck << endl;
cout << "The number of times is: " << counterResponse << endl;
return 0;
}
I get the error at the for loop. Any other advice about how i can make my program work for different words, multiple lengths of words and also how i can highlight the words in text would be helpful.
I really would appreciate if someone could aid me with my problem. Thanks!
I get the error at the for loop.
You should describe the error you get. I happen to have access to Xcode so I can run your code and see what happens, but you should try to spare that of people from whom you want help.
In this case you should describe how the debugger stops the program at the line:
char ch_4 = text.at(i + 3);
includes the message: "Thread 1: signal SIGABRT" and the console output shows
libc++abi.dylib: terminating with uncaught exception of type std::out_of_range: basic_string
Your problem is this: the for loop checks to make sure that i is in the correct range for the string text before using it as an index, but then you also use i+1, i+2, and i+3 as indices without checking that those values are also valid.
Fix that check and the program appears to run fine (given correct input).
Some miscellaneous comments.
Use more consistent indentation. It makes the program easier to read and follow. Here's how I would indent it (using the tool clang-format).
#define SPACE ' '(char) looks like a bad idea, even if you're not using it.
using namespace std; is usually frowned on, though as long as you don't put it in headers it usually won't cause too much trouble. I still could though, and because you probably won't understand the resulting error message you may want to avoid it anyway. If you really don't like writing std:: everywhere then use more limited applications such as using std::string; and using std::cout;.
global variables should be avoided, and you can do so here by simply passing textUserSubmit and wordUserSubmit to searchWord().
there's really no need to make sure text is less than or equal to 500 characters in length. You're using std::string, so it can hold much longer input.
You never check how long word is even though your code requires it to be at least 4 characters long. Fortunately you're using at() to index into it so you don't get undefined behavior, but you should still check. I'd remove the check in textQuery and add one to wordQuery.

Converting Decimal to binary using only the basics

I have to write a program that converts a decimal number between (1.0 - 99.99) into binary. My teacher said We can ONLY use the things we learned in class so far, witch include: Loops, input/output files, strings, if/else, cmath, and user defined functions. Ive started to program how to convert an integer to binary first before taking on the decimal. I am able to calculate the binary value but I am spitting out my binary in reverse order. So my teacher said to send the remainders(eg: bit values) to a file and read them as a string. Which is were I am now. Still with the same problem. How do I print these values from a string in reverse order?
My attempt so far:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
int number;
int remainder;
string bitValues;
ofstream outFile;
ifstream inFile;
//inFile.open("C:\\Users\\David\\Desktop\\BinaryIn.txt"); // pay no mind to this.
outFile.open("C:\\Users\\David\\Desktop\\BinaryOut.txt");
cout << "Enter a integer to be converted to binary: ";
cin >> number;
while(number != 0)
{
remainder = number % 2;
outFile << remainder << " "; // I send it to the outFile
number /= 2;
}
outFile.close(); // I close because I need to read from it now.
inFile.open("C:\\Users\\David\\Desktop\\BinaryOut.txt"); //I did this so I can read from the same outFile
getline(inFile, bitValues); //had to look up getline(), was not covered in class
// I just came up with this idea, is this Valid??????????
int posisiton = 10;
while(posisiton >= 0)
{
cout << bitValues[posisiton]; // I ever done something like this but It worked!
posisiton--;
}
int pause;
cin >> pause;
return 0;
}
Do you know how to write a recursive function? By doing the recursive call before outputting the remainder (as opposed to afterwards), you will get the effect you want.
1) Forget the file (I don't know how that was going to help. Maybe you misunderstood the teacher). Just write a simple function to reverse the string.
for(int i=0; i<bitValues.length()/2; ++i) {
char t = bitValues[i];
bitValues[i] = bitValues[bitValues.length()-1-i];
bitValues[str.length()-1-i] = t;
}
2) Or instead of using modulo, use a flag to get the bits in order the first time. This isn't code because it more directly relates to your homework, than a simple string reversal.
resize the string to be big enough to hold all the bits
set a mask to have a 1 in the 31st position
for each position in the string
use `&` with the mask to find if it's a one or zero
set the character depending on the bit
shift the mask right one.

Confusion about correct design of a program C++

I made a small program that generates primes and lets the user check a number and see if it's a prime or not. Problem is, I'm not sure how to properly design it. This is the program:
#include <iostream>
#include <vector>
typedef unsigned long long bigint;
std::vector<bool> sieve(size_t size)
{
std::vector<bool> primelist(size);
primelist[0] = false;
primelist[1] = false;
for (bigint i = 2; i < size; ++i) { primelist[i] = true; }
for (bigint i = 2; i * i < size; ++i)
{
if (primelist[i])
{
for (bigint j = i; j * i < size; ++j)
primelist[i*j] = false;
}
}
return primelist;
}
int main()
{
bigint range;
bigint number;
std::vector<bool> primes;
std::cout << "Enter range: " << std::endl;
std::cin >> range;
primes = sieve(range);
while (1)
{
std::cout << "Enter number to check: " << std::endl;
std::cin >> number;
if (primes[number])
std::cout << "Prime" << std::endl;
else
std::cout << "Not prime" << std::endl;
}
return 0;
}
The basic flow I want to achieve is: Input range, /handle input/, input number to check, /handle input/
I also want to give the user an option to change the range at any given time, by writing a command like "change range number"
I have a few problems with this:
I want the program to be under control if the user inputs a range bigger than unsigned long long, and if the user basically exceeds any limit(like for example if the range he input was 100 then if he checks for 101) an exception will be caught. I know this needs to be implemented using try/catch/throw, but I have no idea how to do that while keeping the option to change the range and without making my code spaghetti code.
Also, I want the errors to be of enum type(I read that enums are good for exceptions), something like
enum errors
{
OUT_OF_RANGE = 1, //Out of the range specified by the user
INCORRECT_VALUE, //If user input "one" instead of 1
RANGE_SIGNED, //If user inputs a signed value for range
NUM_LIMITS //Number exceeds unsigned long long
};
I have no idea how to use exception handling, not to mention using it with enums. How the hell do I keep this program safe and running, while keeping away from spaghetti code?
I am extremely confused. If someone could help me design this program correctly and maintain readability and efficiency, it will really improve my future program designs.
Thanks for reading!
You asked a lot.
You want to validate user input. Users should not be able to enter huge numbers, non-integers, and so on.
I'm going to start off by answering that this is absolutely not a scenario that exceptions should be used for. Exceptions are used to handle exceptional circumstances. These are ones you can't anticipate or really deal with.
A user enters a number that's too big? You can handle that. Tell them that their number is too big, please enter a number between 1 and X.
A user enters the word apple? You can handle that. Tell them that they can only enter integers.
One way of doing this would be to make a ValidateInput function. You can have it return a number (or an enum, they're basically the same thing) to tell you whether there was an error.
In order to do the validation, you will most likely have to receive input as an std::string and then validate it before turning it into a number. Getting input as an unsigned int or similar integral type doesn't really allow you to check for errors.
This adds a bit of work, since you need to manually validate the input manually. There are libraries with functions to help with this, such as boost::lexical_cast, but that's probably too much for you right now.
Below is some very basic psuedo code to illustrate what I mean. It's only meant to give you an idea of what to do, it won't compile or do the work for you. You could extend it further by making a generic function that returns a message based on an error code and so on.
enum error_code {
SUCCESS, // No error
OUT_OF_RANGE, // Out of the range specified by the user
INCORRECT_VALUE, // If user input "one" instead of 1
RANGE_SIGNED, // If user inputs a signed value for range
NUM_LIMITS // Number exceeds unsigned long long
};
// This function will check if the input is valid.
// If it's not valid, it will return an error code to explain why it's invalid.
error_code ValidateInput(const std::string& input) {
// Check if input is too large for an unsigned long long
if (InputIsTooLarge)
return NUM_LIMITS;
// Check if input is negative
if (InputIsNegative)
return RANGE_SIGNED;
// Check if input is not an integer
if (InputIsNotInteger)
return INCORRECT_VALUE;
// If we make it here, no problems were found, input is okay.
return SUCCESS;
}
unsigned long long GetInput() {
// Get the user's input
std::string input;
std::cin >> input;
// Check if the input is valid
error_code inputError = ValidateInput(input);
// If input is not valid, explain the problem to the user.
if (inputError != SUCCESS) {
if (inputError == NUM_LIMITS) {
std::cout << "That number is too big, please enter a number between "
"1 and X." << std::endl;
}
else if (inputError == RANGE_SIGNED) {
std::cout << "Please enter a positive number." << std::endl;
}
else if (inputError == INCORRECT_VALUE) {
std::cout << "Please enter an integer." << std::endl;
}
else {
std::cout << "Invalid input, please try again." << std::endl;
}
// Ask for input again
return GetInput();
}
// If ValidateInput returned SUCCESS, the input is okay.
// We can turn it into an integer and return it.
else {
return TurnStringIntoBigInt(input);
}
}
int main() {
// Get the input from the user
unsigned long long number = GetInput();
// Do something with the input
}
I like Dauphic's answer, particularly because it illustrates breaking down the problem into bits and solving them individually. I would, however, do GetInput a bit differently:
unsigned long long GetInput() {
// Get the user's input
std::string input;
error_code inputError;
// Repeatedly read input until it is valid
do {
std::cin >> input;
inputError = ValidateInput(input);
if (inputError == NUM_LIMITS) {
std::cout << "That number is too big, please enter a number between "
"1 and X." << std::endl;
}
// ...handle all other cases similarly
} while(inputError != SUCCESS);
// If ValidateInput returned SUCCESS, the input is okay.
// We can turn it into an integer and return it.
return TurnStringIntoBigInt(input);
}
The recursive solution is nice, but has the drawback of, well, being recursive and growing the stack. Probably that's not a big deal in this case, but it is something to watch out for.
As for how to write ValidateInput, basically you're going to be scanning the string for invalid characters and if none are found, testing if the value will fit in your chosen integer type until reading it into a variable with e.g. >>.
note: this solution has a serious flaw in that it doesn't check the state of std::cin. If the user were to pass EOF, i.e. press ^D, the program would get stuck in the loop, which is not good behavior.
Instead of a vector of bool you'd better use a bitset
With that, you can use the Eratosthene method to determine if a number is prime or not.