Here's the contents of a text file:
SQUARE 2
SQUARE
RECTANGLE 4 5
I'm trying to figure out why my strtok() loop won't take the end of the 2ND "SQUARE" and just make the length = 0. Don't fully understand the concept behind strtok either, I wouldn't mind a lecture about strtok. Here's the code:
#include <cstring>
#include <cstdlib>
#include <iostream>
using std::cout;
using std::endl;
using std::cin;
using std::ios;
#include<iomanip>
using std::setprecision;
#include <fstream>
using std::ifstream;
const int MAX_CHARS_PER_LINE = 512;
const int MAX_TOKENS_PER_LINE = 20;
const char* const DELIMITER = " ";
int main()
{
// create a file-reading object
ifstream fin;
fin.open("geo.txt"); // open a file
if (!fin.good())
return 1; // exit if file not found
//PI
float pi = 3.14159265359;
//DIMENSIONS
float length, width, height, radius;
//AREAS, PERIMETERS, VOLUMES
float areaSquare, periSquare;
float areaRectangle, periRectangle;
float areaCube, volCube;
float areaPrism, volPrism;
float areaCircle, periCircle;
float areaCylinder, volCylinder;
// read each line of the file
while (!fin.eof())
{
// read an entire line into memory
char buf[MAX_CHARS_PER_LINE];
fin.getline(buf, MAX_CHARS_PER_LINE);
// parse the line into blank-delimited tokens
int n = 0; // a for-loop index
// array to store memory addresses of the tokens in buf
const char* token[MAX_TOKENS_PER_LINE] = {0}; // initialize to 0
// parse the line
token[0] = strtok(buf, DELIMITER); // first token
if (token[0]) // zero if line is blank
{
for (n = 1; n < MAX_TOKENS_PER_LINE; n++)
{
token[n] = strtok(0, DELIMITER); // subsequent tokens
if (!token[n] || token[n]==0) break;
}
}
if(strcmp("SQUARE", token[0]) == 0) //1
{
length = atof(token[1])?atof(token[1]):0;
areaSquare = length * length;
periSquare = 4 * length;
cout.setf(ios::fixed|ios::showpoint);
cout << setprecision(2);
cout << token[0] << ' ' << "length="<< token[1] << ' ';
cout << "Area=" << areaSquare << ' ';
cout << "Perimeter=" << periSquare << '\n';
cout.unsetf(ios::fixed|ios::showpoint);
cout << setprecision(6);
}
else if(strcmp("RECTANGLE", token[0]) == 0) //2
{
length = atof(token[1])?atof(token[1]):0;
width = atof(token[2])?atof(token[2]):0;
areaRectangle = length * width;
periRectangle = 2 * length + 2 * width;
cout.setf(ios::fixed|ios::showpoint);
cout << setprecision(2);
cout << token[0] << ' ' << "length="<< token[1] << ' ';
cout << "width=" << token[2] << ' ' ;
cout << "Area=" << areaRectangle << ' ';
cout << "Perimeter=" << periRectangle << '\n';
cout.unsetf(ios::fixed|ios::showpoint);
cout << setprecision(6);
}
else
{
cout << "End of program. Press ENTER to exit.";
cin.ignore(1000,10);
break;
}
}
}
Your segmentation fault is caused by this:
length = atof(token[1])?atof(token[1]):0;
You made the mistake of assuming that token[1] was tokenized. If you look at your 2nd 'SQUARE' you'll find that for that line it will have set token[1] to NULL. You then pass NULL to atof() which understandably errors out.
You're also using strtok() improperly. There is no reason to strcpy() from its result, because strtok() itself is a destructive operation.
So here's a lecture about strtok.
Firstly, it's evil, but so handy that you use it anyway sometimes. Tokenizers can be a pain in the butt to write.
The idea behind strtok was to create an easy tokenizer. A tokenizer is a pain in the butt to write, and the interface for it is actually fairly decent if you don't mind making it really easy to blow your computer up with it. You can use a very small amount of code to parse command line arguments, for example.
However, strtok is destructive to the string you use it on. It will replace the token that it finds with a 0, automatically null-terminating the returned value. That means that you can directly use the returned string without needing to copy it. A string like this:
here are spaces0
Is changed into
here0are0spaces0
where 0 delimits end of string character (0). This is done in place, and you get pointers to here, are, and spaces.
strtok uses static variables - meaning it retains state information between calls. On the first call you pass it a pointer to the string you're trying to tokenize; from then on, you pass it a NULL pointer to signal that you want it to continue where it left off before. It returns the next token, returning NULL when it finds the end of the string.
An strtok loop is very easy to write. This code will tokenize a string for you properly. The following example code is ugly, but I blame being tired.
char *input_string; // Just so we have it
const int MAX_TOKENS = 10; // Arbitrary number
char *tokens[MAX_TOKENS]; // This will do all the storage we need.
tokens[0] = strtok(input_string, " -=\""); // Setup call.
int number_of_tokens = 1; // We've already filled tokens[0], so we have 1 token. We want to start filling from 1.
do {
if (tokens[number_of_tokens] = strtok(NULL," -=\"")) number_of_tokens++;
else break;
} while(number_of_tokens < MAX_TOKENS);
That first line in the loop is common practice for C programmers, but is ugly for readability. Here's what it does:
a) It sets tokens[number_of_tokens] to the return value of strtok.
b) If that is NULL, it terminates the loop (second line).
addendnum: there is an inline test. You can do if (a = 1) and it will return true and set a to 1. You can do if (a = 0) it will return false while setting a to 0. This line takes advantage of that fact, if strtok() returns NULL, well, that's false.
c) If that is not NULL, tokens[number_of_tokens] now contains a pointer to the next token found in the string.
d) Since a token was found, the number of tokens (number_of_tokens) is incremented.
5) It reuses the variable that keeps count of how many tokens there are as an index into the array of pointers that it keeps.
6) It loops infinitely until it either meets the condition of strtok returning NULL, or the while() condition (in this case, there are more than 10 tokens).
If it was given this string:
here are some=words0
It would be
*tokens[0]="here"
*tokens[1]="are"
*tokens[2]="some"
*tokens[3]="words"
*tokens[4] = NULL
number_of_tokens = 4
As you can see, there's no need to copy anything, because that string is replaced in memory as such:
here0are0some0words0
where 0 delimits end of string character (0).
I hope this answers your questions.
Here is a version that works.
Main differences are,
Have changed the array of char * to array of 20 char strings. This guarantees the array elements have memory allocated, in your case they are null pointers and stay this way when strtok returns NULL, you cannot then use a NULL pointer.
The second call to strtok is "strtok(0, DELIMITER)"
but should be "strtok(NULL, DELIMITER)".
I think they are the only diffs, but use the diff utility to check.
#include <cstring>
#include <cstdlib>
#include <iostream>
using std::cout;
using std::endl;
using std::cin;
using std::ios;
#include<iomanip>
using std::setprecision;
#include <fstream>
using std::ifstream;
const int MAX_CHARS_PER_LINE = 512;
const int MAX_TOKENS_PER_LINE = 20;
const char* const DELIMITER = " ";
int main()
{
// create a file-reading object
char *tok;
ifstream fin;
fin.open("geo.txt"); // open a file
if (!fin.good())
return 1; // exit if file not found
//PI
float pi = 3.14159265359;
//DIMENSIONS
float length, width, height, radius;
//AREAS, PERIMETERS, VOLUMES
float areaSquare, periSquare;
float areaRectangle, periRectangle;
float areaCube, volCube;
float areaPrism, volPrism;
float areaCircle, periCircle;
float areaCylinder, volCylinder;
// read each line of the file
while (!fin.eof())
{
// read an entire line into memory
char buf[MAX_CHARS_PER_LINE];
fin.getline(buf, MAX_CHARS_PER_LINE);
// parse the line into blank-delimited tokens
int n = 0; // a for-loop index
// array to store memory addresses of the tokens in buf
// const char* token[MAX_TOKENS_PER_LINE] = {0}; // initialize to 0
char token[MAX_TOKENS_PER_LINE][20];
for (n=0;n<MAX_TOKENS_PER_LINE;n++)
{
token[n][0] = NULL;
}
// parse the line
tok = strtok(buf, DELIMITER); // first token
if (tok == NULL)
break;
strcpy(token[0],tok);
if (token[0]) // zero if line is blank
{
for (n = 1; n < MAX_TOKENS_PER_LINE; n++)
{
tok = strtok(NULL, DELIMITER); // subsequent tokens
if (tok == NULL)
break;
strcpy(token[n],tok);
// if (!token[n] || token[n]==0) break;
}
}
if(strcmp("SQUARE", token[0]) == 0) //1
{
length = atof(token[1])?atof(token[1]):0;
areaSquare = length * length;
periSquare = 4 * length;
cout.setf(ios::fixed|ios::showpoint);
cout << setprecision(2);
cout << token[0] << ' ' << "length="<< token[1] << ' ';
cout << "Area=" << areaSquare << ' ';
cout << "Perimeter=" << periSquare << '\n';
cout.unsetf(ios::fixed|ios::showpoint);
cout << setprecision(6);
}
else if(strcmp("RECTANGLE", token[0]) == 0) //2
{
length = atof(token[1])?atof(token[1]):0;
width = atof(token[2])?atof(token[2]):0;
areaRectangle = length * width;
periRectangle = 2 * length + 2 * width;
cout.setf(ios::fixed|ios::showpoint);
cout << setprecision(2);
cout << token[0] << ' ' << "length="<< token[1] << ' ';
cout << "width=" << token[2] << ' ' ;
cout << "Area=" << areaRectangle << ' ';
cout << "Perimeter=" << periRectangle << '\n';
cout.unsetf(ios::fixed|ios::showpoint);
cout << setprecision(6);
}
else
{
cout << "End of program. Press ENTER to exit.";
cin.ignore(1000,10);
break;
}
}
}
Ok. When your line
const char* token[MAX_TOKENS_PER_LINE] = {0};
creates an array of pointers, but none of them point to anything. The first element is set to 0 (which is a NULL address) and the rest are not initialised. When you run and process line 2 (which has 1 element) token[0] points to 'SQUARE' but token[1] is given the value 0x00 (NULL). This is an invalid memory location. You then process token[1] with the line
length = atof(token[1])?atof(token[1]):0;
and this causes a Segmentation fault because token[1] is a NULL pointer. In my version token[1] is a valid pointer to a NULL string, which sets length to 0. I suggest you compile with the -g flag (eg g++ -g test.cpp -o test). Then call 'gdb test' and use break, run, continue commands to step through the code. You can use the print command to display the contents of variables.
In the first run in gdb just enter 'run'. This will fail, then enter 'bt' which will tell you the failing line, let's call it linenumber.
In the second run enter 'break linenumber' and then 'run' and the execution will stop on the failing line but before it is executed. You can then look at the contents of the variables which will give you a big clue to why it is failing.
Here is some working C++ based closely on your code.
I've revised the I/O handling; fin.getline() reports whether it got a line or not, so it should be used to control the loop; fin.eof() is a red flag warning in my estimation (as is feof(fp) in C).
The core dump occurs because you don't check that you got a length token after the word SQUARE. The revised code checks that it got exactly the correct number of tokens, complaining if not. The code using strtok() has been unified into a single loop; it contains a diagnostic print statement that shows the token just found (valuable for checking what's going on).
I removed a pile of unused variables; each variable is defined and initialized in the calculation blocks.
There are endless possible reservations about using C strings and strtok() in C++ (the printing would be a lot more succinct if all the code were written in C using the C standard I/O functions like printf()). You can find a discussion of the alternatives to strtok() at Strange strtok() error. You can find another discussion on why strtok() is a disaster in a library function at Reading user input and checking the string.
Working code for the 3 lines of data in the question
#include <cstring>
#include <cstdlib>
#include <iostream>
using std::cout;
using std::endl;
using std::cin;
using std::ios;
using std::cerr;
#include<iomanip>
using std::setprecision;
#include <fstream>
using std::ifstream;
const int MAX_CHARS_PER_LINE = 512;
const int MAX_TOKENS_PER_LINE = 20;
const char* const DELIMITER = " ";
int main()
{
// create a file-reading object
const char *fname = "geo.txt";
ifstream fin;
fin.open(fname); // open a file
if (!fin.good())
{
cerr << "Failed to open file " << fname << endl;;
return 1; // exit if file not found
}
// read each line of the file
char buf[MAX_CHARS_PER_LINE];
while (fin.getline(buf, sizeof(buf)))
{
int n = 0;
const char *token[MAX_TOKENS_PER_LINE] = {0};
char *position = buf;
while ((token[n] = strtok(position, DELIMITER)) != 0)
{
cout << "Token " << n << ": " << token[n] << endl;
n++;
position = 0;
}
if (strcmp("SQUARE", token[0]) == 0 && n == 2)
{
float length = atof(token[1])?atof(token[1]):0;
float areaSquare = length * length;
float periSquare = 4 * length;
cout.setf(ios::fixed|ios::showpoint);
cout << setprecision(2);
cout << token[0] << ' ' << "length="<< token[1] << ' ';
cout << "Area=" << areaSquare << ' ';
cout << "Perimeter=" << periSquare << '\n';
cout.unsetf(ios::fixed|ios::showpoint);
cout << setprecision(6);
}
else if (strcmp("RECTANGLE", token[0]) == 0 && n == 3)
{
float length = atof(token[1])?atof(token[1]):0;
float width = atof(token[2])?atof(token[2]):0;
float areaRectangle = length * width;
float periRectangle = 2 * length + 2 * width;
cout.setf(ios::fixed|ios::showpoint);
cout << setprecision(2);
cout << token[0] << ' ' << "length="<< token[1] << ' ';
cout << "width=" << token[2] << ' ' ;
cout << "Area=" << areaRectangle << ' ';
cout << "Perimeter=" << periRectangle << '\n';
cout.unsetf(ios::fixed|ios::showpoint);
cout << setprecision(6);
}
else
{
cout << "Unrecognized data: " << buf << endl;
}
}
}
Related
This question already has answers here:
Print leading zeros with C++ output operator?
(6 answers)
Closed 2 years ago.
I want cout to output an int with leading zeros, so the value 1 would be printed as 001 and the value 25 printed as 025. How can I do this?
With the following,
#include <iomanip>
#include <iostream>
int main()
{
std::cout << std::setfill('0') << std::setw(5) << 25;
}
the output will be
00025
setfill is set to the space character (' ') by default. setw sets the width of the field to be printed, and that's it.
If you are interested in knowing how the to format output streams in general, I wrote an answer for another question, hope it is useful:
Formatting C++ Console Output.
Another way to achieve this is using old printf() function of C language
You can use this like
int dd = 1, mm = 9, yy = 1;
printf("%02d - %02d - %04d", mm, dd, yy);
This will print 09 - 01 - 0001 on the console.
You can also use another function sprintf() to write formatted output to a string like below:
int dd = 1, mm = 9, yy = 1;
char s[25];
sprintf(s, "%02d - %02d - %04d", mm, dd, yy);
cout << s;
Don't forget to include stdio.h header file in your program for both of these functions
Thing to be noted:
You can fill blank space either by 0 or by another char (not number).
If you do write something like %24d format specifier than this will not fill 2 in blank spaces. This will set pad to 24 and will fill blank spaces.
cout.fill('*');
cout << -12345 << endl; // print default value with no field width
cout << setw(10) << -12345 << endl; // print default with field width
cout << setw(10) << left << -12345 << endl; // print left justified
cout << setw(10) << right << -12345 << endl; // print right justified
cout << setw(10) << internal << -12345 << endl; // print internally justified
This produces the output:
-12345
****-12345
-12345****
****-12345
-****12345
In C++20 you'll be able to do:
std::cout << std::format("{:03}", 25); // prints 025
In the meantime you can use the {fmt} library, std::format is based on.
Disclaimer: I'm the author of {fmt} and C++20 std::format.
cout.fill( '0' );
cout.width( 3 );
cout << value;
Another example to output date and time using zero as a fill character on instances of single digit values: 2017-06-04 18:13:02
#include "stdafx.h"
#include <iostream>
#include <iomanip>
#include <ctime>
using namespace std;
int main()
{
time_t t = time(0); // Get time now
struct tm * now = localtime(&t);
cout.fill('0');
cout << (now->tm_year + 1900) << '-'
<< setw(2) << (now->tm_mon + 1) << '-'
<< setw(2) << now->tm_mday << ' '
<< setw(2) << now->tm_hour << ':'
<< setw(2) << now->tm_min << ':'
<< setw(2) << now->tm_sec
<< endl;
return 0;
}
I would use the following function. I don't like sprintf; it doesn't do what I want!!
#define hexchar(x) ((((x)&0x0F)>9)?((x)+'A'-10):((x)+'0'))
typedef signed long long Int64;
// Special printf for numbers only
// See formatting information below.
//
// Print the number "n" in the given "base"
// using exactly "numDigits".
// Print +/- if signed flag "isSigned" is TRUE.
// Use the character specified in "padchar" to pad extra characters.
//
// Examples:
// sprintfNum(pszBuffer, 6, 10, 6, TRUE, ' ', 1234); --> " +1234"
// sprintfNum(pszBuffer, 6, 10, 6, FALSE, '0', 1234); --> "001234"
// sprintfNum(pszBuffer, 6, 16, 6, FALSE, '.', 0x5AA5); --> "..5AA5"
void sprintfNum(char *pszBuffer, int size, char base, char numDigits, char isSigned, char padchar, Int64 n)
{
char *ptr = pszBuffer;
if (!pszBuffer)
{
return;
}
char *p, buf[32];
unsigned long long x;
unsigned char count;
// Prepare negative number
if (isSigned && (n < 0))
{
x = -n;
}
else
{
x = n;
}
// Set up small string buffer
count = (numDigits-1) - (isSigned?1:0);
p = buf + sizeof (buf);
*--p = '\0';
// Force calculation of first digit
// (to prevent zero from not printing at all!!!)
*--p = (char)hexchar(x%base);
x = x / base;
// Calculate remaining digits
while(count--)
{
if(x != 0)
{
// Calculate next digit
*--p = (char)hexchar(x%base);
x /= base;
}
else
{
// No more digits left, pad out to desired length
*--p = padchar;
}
}
// Apply signed notation if requested
if (isSigned)
{
if (n < 0)
{
*--p = '-';
}
else if (n > 0)
{
*--p = '+';
}
else
{
*--p = ' ';
}
}
// Print the string right-justified
count = numDigits;
while (count--)
{
*ptr++ = *p++;
}
return;
}
This question already has answers here:
Print leading zeros with C++ output operator?
(6 answers)
Closed 2 years ago.
I want cout to output an int with leading zeros, so the value 1 would be printed as 001 and the value 25 printed as 025. How can I do this?
With the following,
#include <iomanip>
#include <iostream>
int main()
{
std::cout << std::setfill('0') << std::setw(5) << 25;
}
the output will be
00025
setfill is set to the space character (' ') by default. setw sets the width of the field to be printed, and that's it.
If you are interested in knowing how the to format output streams in general, I wrote an answer for another question, hope it is useful:
Formatting C++ Console Output.
Another way to achieve this is using old printf() function of C language
You can use this like
int dd = 1, mm = 9, yy = 1;
printf("%02d - %02d - %04d", mm, dd, yy);
This will print 09 - 01 - 0001 on the console.
You can also use another function sprintf() to write formatted output to a string like below:
int dd = 1, mm = 9, yy = 1;
char s[25];
sprintf(s, "%02d - %02d - %04d", mm, dd, yy);
cout << s;
Don't forget to include stdio.h header file in your program for both of these functions
Thing to be noted:
You can fill blank space either by 0 or by another char (not number).
If you do write something like %24d format specifier than this will not fill 2 in blank spaces. This will set pad to 24 and will fill blank spaces.
cout.fill('*');
cout << -12345 << endl; // print default value with no field width
cout << setw(10) << -12345 << endl; // print default with field width
cout << setw(10) << left << -12345 << endl; // print left justified
cout << setw(10) << right << -12345 << endl; // print right justified
cout << setw(10) << internal << -12345 << endl; // print internally justified
This produces the output:
-12345
****-12345
-12345****
****-12345
-****12345
In C++20 you'll be able to do:
std::cout << std::format("{:03}", 25); // prints 025
In the meantime you can use the {fmt} library, std::format is based on.
Disclaimer: I'm the author of {fmt} and C++20 std::format.
cout.fill( '0' );
cout.width( 3 );
cout << value;
Another example to output date and time using zero as a fill character on instances of single digit values: 2017-06-04 18:13:02
#include "stdafx.h"
#include <iostream>
#include <iomanip>
#include <ctime>
using namespace std;
int main()
{
time_t t = time(0); // Get time now
struct tm * now = localtime(&t);
cout.fill('0');
cout << (now->tm_year + 1900) << '-'
<< setw(2) << (now->tm_mon + 1) << '-'
<< setw(2) << now->tm_mday << ' '
<< setw(2) << now->tm_hour << ':'
<< setw(2) << now->tm_min << ':'
<< setw(2) << now->tm_sec
<< endl;
return 0;
}
I would use the following function. I don't like sprintf; it doesn't do what I want!!
#define hexchar(x) ((((x)&0x0F)>9)?((x)+'A'-10):((x)+'0'))
typedef signed long long Int64;
// Special printf for numbers only
// See formatting information below.
//
// Print the number "n" in the given "base"
// using exactly "numDigits".
// Print +/- if signed flag "isSigned" is TRUE.
// Use the character specified in "padchar" to pad extra characters.
//
// Examples:
// sprintfNum(pszBuffer, 6, 10, 6, TRUE, ' ', 1234); --> " +1234"
// sprintfNum(pszBuffer, 6, 10, 6, FALSE, '0', 1234); --> "001234"
// sprintfNum(pszBuffer, 6, 16, 6, FALSE, '.', 0x5AA5); --> "..5AA5"
void sprintfNum(char *pszBuffer, int size, char base, char numDigits, char isSigned, char padchar, Int64 n)
{
char *ptr = pszBuffer;
if (!pszBuffer)
{
return;
}
char *p, buf[32];
unsigned long long x;
unsigned char count;
// Prepare negative number
if (isSigned && (n < 0))
{
x = -n;
}
else
{
x = n;
}
// Set up small string buffer
count = (numDigits-1) - (isSigned?1:0);
p = buf + sizeof (buf);
*--p = '\0';
// Force calculation of first digit
// (to prevent zero from not printing at all!!!)
*--p = (char)hexchar(x%base);
x = x / base;
// Calculate remaining digits
while(count--)
{
if(x != 0)
{
// Calculate next digit
*--p = (char)hexchar(x%base);
x /= base;
}
else
{
// No more digits left, pad out to desired length
*--p = padchar;
}
}
// Apply signed notation if requested
if (isSigned)
{
if (n < 0)
{
*--p = '-';
}
else if (n > 0)
{
*--p = '+';
}
else
{
*--p = ' ';
}
}
// Print the string right-justified
count = numDigits;
while (count--)
{
*ptr++ = *p++;
}
return;
}
I'm having a little trouble with my code. It's pretty much supposed to open two files, and compare the first twenty line of the file "StudentAnswers.txt" [inputted as a char into a char array] against a char value in (each line of another file) "CorrectAnswers.txt" in another array at the same position (index). It's like a linear search, but the same position in the arrays. Then a report should be displayed, detailing which question the student missed, the given answer, the correct answer, and if the student passed (got >= 70%) or not, like the following:
Report for Student X:
2 (A/D), 3 (C/D), 5(D/A)
This student passed the exam!
Then it should clear the SAArray, and feed the next twenty lines from StudentAnswers.txt, and start the process all over again. I guess the program has to determine the number of students from (lines of 'StudentAnswers.txt' file / 20).
I'm having trouble displaying the report, and having the array clear itself after the program. I'm guessing this can be done with a while loop and an accumulator for the number of students (to be determined by above equation).
Also, Visual Studio seems to go to "Missed __ questions for a total of ___ %", and then keep looping -858993460.
Any help would be appreciated.
#include <iostream>
#include <fstream>
#include <string>
#include <array>
#include <algorithm>
using namespace std;
void GradeReturn(char[], char[], int, int, int);
string PassFail(float);
int main()
{
ifstream SA("StudentAnswers.txt");
ifstream CA("CorrectAnswers.txt");char CAArray[20];
char SAArray[20];
// char SA2Array[20];
bool isCorrect;
int correct;
int incorrect;
int counter;
correct = 0;incorrect = 0;
counter = 0;
cout << endl;
if (!SA.fail())
{
cout << "'StudentAnswers.txt' file opened successfully." << endl;
cout << "'CorrectAnswers.txt' file opened successfully." << endl << endl;
int a = 0;
int b = 0;
while (a < 20)
{
CA >> CAArray[a];
a++;
} // while loop to feed char into the array
while (b < 20)
{
SA >> SAArray[b];
b++;
}
} // while loop to feed char into array
CA.close(); // closing "CorrectAnswers.txt"
SA.close(); // closing "StudentAnswers.txt"
GradeReturn(&CAArray[counter], &SAArray[counter], correct, incorrect, counter);
return 0;
}
void GradeReturn(char CAArray[], char SAArray[], int correct, int incorrect, int counter)
{
float percent;
float hundred;
int student;
int catcher[20];
int writeCatcher; int starter;
int catcher_size;
student = 0;
writeCatcher = 0;
catcher_size = ((sizeof catcher) / 4);
while (counter < 20)
{
if ((CAArray[counter]) == (SAArray[counter]))
{
correct++;
cout << "Good job!" << endl;
} // correct handling
else
{
incorrect++;
cout << "You got question " << counter << " wrong." << endl;
counter >> catcher[writeCatcher];
writeCatcher++;
} // incorrect handling
counter++;
} // while loop to determine if a student got a question right or wrong
static_cast <float> (incorrect); // float conversion
cout << endl; // for cleanliness
percent = ((static_cast <float> (correct)) / 20); // percentage
hundred = percent * 100;
PassFail(percent);
if (PassFail(percent) == "pass")
{
student++;
cout << "Report for Student " << student << ":" << endl;
cout << "-----------------------------" << endl;
cout << "Missed " << incorrect << " questions out of 20 for ";
cout << hundred << " % correct." << endl << endl;
starter = 0;
while (starter < (sizeof catcher)
{
if(1=1)
{
catcher_size
}
else
{
cout << "";
starter++;
}
}
}
else if (PassFail(percent) == "fail")
{
student++;
cout << "Missed " << incorrect << " questions out of 20 for ";
cout << hundred << " % correct." << endl << endl;
while (starter < catcher_size)
{
if ((catcher[starter]) == -858993460)
{
starter++;
}
else
{
cout << "";
starter++;
}
}
}
return;
}
string PassFail(float percent)
{
if (percent >= 0.70) // if <pass>
{
return "pass";
}
else // if <fail>
{
return "fail";
}
cout << endl;
}
To get a loop you should keep streams open instead of closing them after reading 20 lines.
As pseudo code that would be:
a = 0;
while(streams_not_empty)
{
CA >> CAArray[a];
SA >> SAArray[a];
++a;
if (a == 20)
{
GradeReturn(&CAArray[counter], &SAArray[counter], correct, incorrect, counter);
a = 0; // Reset a
}
}
CA.close(); // closing "CorrectAnswers.txt"
SA.close(); // closing "StudentAnswers.txt"
You would also need to pass correct, incorrect, counter by reference so that the GradeReturn can change their value and their by do the accumulation.
Like:
void GradeReturn(char CAArray[], char SAArray[], int& correct, int& incorrect, int& counter)
Further you shouldn't rely on being able to read exactly Nx20 lines from the files every time. A file could have, e.g. 108 (5x20 + 8) lines, so you code should be able to handle the with only 8 lines. In other words, don't hard code 20 in your function like while (counter < 20). Instead pass the number of lines to be handled and do while (counter < number_to_handle).
Something like this as pseudo code:
a = 0;
while(streams_not_empty)
{
CA >> CAArray[a];
SA >> SAArray[a];
++a;
if (a == 20)
{
GradeReturn(&CAArray[counter], &SAArray[counter], correct, incorrect, counter, a);
// ^
a = 0; // Reset a
}
}
if (a != 0)
{
// Process the rest
GradeReturn(&CAArray[counter], &SAArray[counter], correct, incorrect, counter, a);
}
CA.close(); // closing "CorrectAnswers.txt"
SA.close(); // closing "StudentAnswers.txt"
One problem you have is you're trying to compare C-style strings with the == operator. This will compare them essentially as if they were pointers to char, i.e. compare whether they point at the same location in memory, not compare the contents of the string. I urge you to look up array-decay and c-string variables to understand more.
Specifically, if (PassFail(percent) == "pass") isn't going to do what you want it to. strcomp doc, strncmp doc using std::string variables instead of c-style strings would all work, but it would be better simply to compare percent to a value, i.e. if(percent >= 0.70 directly instead of calling PassFail and comparing a string.
There are many other issues here also, you at one point call PassFail but do nothing with the return value. The only side affect of PassFail is cout << endl, if that's what you intend, it's a poor decision and hard to read way to put a newline on the console.
Try asking your compiler for more warnings, that's often helpful in finding these types of issues. -Wall -Wextra work for gcc, you may have to read your compiler manual...
I am looking to find a C++ fstream equivalent function of C fgets. I tried with get function of fstream but did not get what I wanted. The get function does not extract the delim character whereas the fgets function used to extract it. So, I wrote a code to insert this delim character from my code itself. But it is giving strange behaviour. Please see my sample code below;
#include <stdio.h>
#include <fstream>
#include <iostream>
int main(int argc, char **argv)
{
char str[256];
int len = 10;
std::cout << "Using C fgets function" << std::endl;
FILE * file = fopen("C:\\cpp\\write.txt", "r");
if(file == NULL){
std::cout << " Error opening file" << std::endl;
}
int count = 0;
while(!feof(file)){
char *result = fgets(str, len, file);
std::cout << result << std::endl ;
count++;
}
std::cout << "\nCount = " << count << std::endl;
fclose(file);
std::fstream fp("C:\\cpp\\write.txt", std::ios_base::in);
int iter_count = 0;
while(!fp.eof() && iter_count < 10){
fp.get(str, len,'\n');
int count = fp.gcount();
std::cout << "\nCurrent Count = " << count << std::endl;
if(count == 0){
//only new line character encountered
//adding newline character
str[1] = '\0';
str[0] = '\n';
fp.ignore(1, '\n');
//std::cout << fp.get(); //ignore new line character from stream
}
else if(count != (len -1) ){
//adding newline character
str[count + 1] = '\0';
str[count ] = '\n';
//std::cout << fp.get(); //ignore new line character from stream
fp.ignore(1, '\n');
//std::cout << "Adding new line \n";
}
std::cout << str << std::endl;
std::cout << " Stream State : Good: " << fp.good() << " Fail: " << fp.fail() << std::endl;
iter_count++;
}
std::cout << "\nCount = " << iter_count << std::endl;
fp.close();
return 0;
}
The txt file that I am using is write.txt with following content:
This is a new lines.
Now writing second
line
DONE
If you observe my program, I am using fgets function first and then using the get function on same file. In case of get function, the stream state goes bad.
Can anyone please point me out what is going wrong here?
UPDATED: I am now posting a simplest code which does not work at my end. If I dont care about the delim character for now and just read the entire file 10 characters at a time using getline:
void read_file_getline_no_insert(){
char str[256];
int len =10;
std::cout << "\nREAD_GETLINE_NO_INSERT FUNCITON\n" << std::endl;
std::fstream fp("C:\\cpp\\write.txt", std::ios_base::in);
int iter_count = 0;
while(!fp.eof() && iter_count < 10){
fp.getline(str, len,'\n');
int count = fp.gcount();
std::cout << "\nCurrent Count = " << count << std::endl;
std::cout << str << std::endl;
std::cout << " Stream State : Good: " << fp.good() << " Fail: " << fp.fail() << std::endl;
iter_count++;
}
std::cout << "\nCount = " << iter_count << std::endl;
fp.close();
}
int main(int argc, char **argv)
{
read_file_getline_no_insert();
return 0;
}
If wee see the output of above code:
READ_GETLINE_NO_INSERT FUNCITON
Current Count = 9
This is a
Stream State : Good: 0 Fail: 1
Current Count = 0
Stream State : Good: 0 Fail: 1
You would see that the state of stream goes Bad and the fail bit is set. I am unable to understand this behavior.
Rgds
Sapan
std::getline() will read a string from a stream, until it encounters a delimiter (newline by default).
Unlike fgets(), std::getline() discards the delimiter. But, also unlike fgets(), it will read the whole line (available memory permitting) since it works with a std::string rather than a char *. That makes it somewhat easier to use in practice.
All types derived from std::istream (which is the base class for all input streams) also have a member function called getline() which works a little more like fgets() - accepting a char * and a buffer size. It still discards the delimiter though.
The C++-specific options are overloaded functions (i.e. available in more than one version) so you need to read documentation to decide which one is appropriate to your needs.
my program compiles without error and appears to run through all of the steps correctly. It is supposed to make a php call and return data. tcpdump does show the request going out so popen is being executed, but the receiving party never updates.
The only discrepancy I can find, is that the command variable appears to be missing data.
# .trol.o
market max price is 0.00638671 at position 0
php coin.php 155 0.006387
0.00638672
the second line in the output is the command I am sending to popen
cout << command << endl; -> php coin.php 155 0.006387
that number is supposed to be the same as the one under it 0.00638672
The number 6 and the number 2 have been chopped off somehow.
How do I get the correct data into my popen command?
code:
void mngr(){
//vector defs
vector<std::string> buydat;
vector<std::string> markdat;
vector<std::string> pricedat;
vector<std::string> purchaseid;
vector<double> doublePdat;
vector<double> doubleMdat;
doublePdat.reserve(pricedat.size());
doubleMdat.reserve(markdat.size());
char buybuff[BUFSIZ];
char command[70];
char sendbuy[12];
buydat = getmyData();
markdat = getmarketbuyData();
//string match "Buy" and send results to new vector with pricedat.push_back()
for(int b = 2; b < buydat.size(); b+=7){
if ( buydat[b] == "Buy" ) {
pricedat.push_back(buydat[b+1]);
}
}
transform(pricedat.begin(), pricedat.end(), back_inserter(doublePdat), [](string const& val) {return stod(val);});
transform(markdat.begin(), markdat.end(), back_inserter(doubleMdat), [](string const& val) {return stod(val);});
auto biggestMy = std::max_element(std::begin(doublePdat), std::end(doublePdat));
std::cout << "my max price is " << *biggestMy << " at position " << std::distance(std::begin(doublePdat), biggestMy) << std::endl;
auto biggestMark = std::max_element(std::begin(doubleMdat), std::end(doubleMdat));
std::cout << "market max price is " << *biggestMark << " at position " << std::distance(std::begin(doubleMdat), biggestMark) << std::endl;
if (biggestMy > biggestMark){
cout << "Biggest is Mine!" << endl;
}
else if (biggestMy < biggestMark){
//cout << "Biggest is market!";
*biggestMark += 0.00000001;
sprintf(sendbuy,"%f",*biggestMark);
sprintf(command, "php coin.php 155 %s",sendbuy);
FILE *markbuy = popen(command, "r");
if (markbuy == NULL) perror ("Error opening file");
while(fgets(buybuff, sizeof(buybuff), markbuy) != NULL){
size_t h = strlen(buybuff);
//clean '\0' from fgets
if (h && buybuff[h - 1] == '\n') buybuff[h - 1] = '\0';
if (buybuff[0] != '\0') purchaseid.push_back(buybuff);
}
cout << command << endl;
cout << *biggestMark << endl;
}
}
I would try to use long float format instead of float as the type of biggestMark should be evaluated as iterator across doubles. I mean try to change sprintf(sendbuy,"%f",*biggestMark); to sprintf(sendbuy,"%lf",*biggestMark);. Hope this would help.