Sorry for the somewhat beginner question, but I've been at this for a couple of days and can't figure out a solution.
I'm basically reading integers from a file, these files should have a set amount of numbers, for the purpose of this question let us say 40. I can return an error fine when the file has less than or more than 40 integers. However, if there happens to be a non-numeric character in there, I'm struggling to figure out how to return an error.
This is what I'm currently doing:
int number = 0;
int counter = 0;
while(inputstream >> number)
{
// random stuff
counter++;
}
if (counter < 40)
return error;
It is at this point I'm a bit confused where to go. My while loop will terminate when the input stream is not an int, but there are two cases when this could happen, a non-integer character is in there, or the end of file has been reached. If we're at eof, my error message is fine and there were less than 40 integers. However, we could also be at less than 40 if it encountered a non-int somewhere. I want to be able to determine the difference between the two but struggling to figure out how to do this. Any help would be appreciated. Thanks!
you can input a line inside loop and try to convert it to integer so if the conversion fails means a non-integer and immediately break the loop returning a error telling that a non-integer found.
otherwise continue read until the end of file then check whether values are less or more 40 checking whether the loop reads all the content or broke because of non-integer value:
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
enum ERRORFLAG{INIT, LESS_40, MORE_40, NON_INT}; // enumerate error
int main()
{
ifstream in("data.txt");
string sLine; // input one line for each read
int value; // value that will be assigned the return value of conversion
int count = 0; // counter for integer values
ERRORFLAG erFlag = INIT; // intialize the error flag
while(getline(in, sLine)) // iterate reading one line each time
{
if( !(value = atoi(sLine.c_str()) ) ) // conversion from string to integer so if the conversion failed else body will be executed
{
erFlag = NON_INT; // setting the error flag to non-int and break
break;
}
else
count++; // otherwise continue reading incrementing count
}
if(INIT == erFlag) // check whether the loop finishes successfully or a non-int caused it to break
{
if( count < 40) // checking whether number of ints less than 40
erFlag = LESS_40; //
else
if(count > 40) // or more than 40
erFlag = MORE_40;
}
// printing the error
switch(erFlag)
{
case LESS_40:
cout << "Error: less than 40 integers << endl";
break;
case MORE_40:
cout << "Error: More than 40 integers << endl";
break;
case NON_INT:
cout << "Error: non-intger found!" << endl;
break;
default:
cout << "Undefined Error" << endl;
}
in.close();
std::cout << std::endl;
return 0;
}
#include <iostream>
using namespace std;
int main() {
int count = 0;
int x;
istream& is = cin; // works with every class that inherits this one
while (is >> x) ++count;
if (is.eof()) {} // end of file reached
else {} // a bad value has been read
cout << "Read count " << count << '\n';
}
this program works fine: first read the file checking for a non-digits and non-white space characters and if you find break immediately setting the error flag.
keep in mind that white spaces like single and tab space will not be considered as invalid because they are used in your file as separators so any character other than digit or white space will break the loop returning an error.
if no error occurred (no invalid character found) and reaching the end of file then read again the file pushing the integer values into a vector which is a good idea without needing a counter then check the size of vector if it is less or more than 40 issuing an error otherwise print the content of vector:
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
#include <vector>
enum ERRORFLAG{INIT, LESS_40, MORE_40, NON_INT};
int main()
{
ifstream in("data.txt");
char c;
string sLine;
int value;
vector<int> vec;
ERRORFLAG erFlag = INIT;
while(in >> c)
{
if(!isspace(c) && !isdigit(c))
{
erFlag = NON_INT;
break;
}
}
in.clear();
in.seekg(0, ios::beg); // jumping back the the beginning of the file moving the get pointer to the beginning
while(in >> value)
vec.push_back(value);
if(NON_INT == erFlag)
cout << "non-int found!" << endl;
else
{
if(vec.size() < 40)
cout << "les than 40 integers!" << endl;
else
if(vec.size() > 40)
cout << "more than 40 integers found!" << endl;
else
for(int i(0); i < vec.size(); i++)
cout << vec[i] << ", ";
}
std::cout << std::endl;
return 0;
}
Related
The problem goes something like this. Given the number "n", on the next "n" lines you will find texts similar to " Worker "x" has worked "y" hours this month". "x" and "y" are numbers from 1 to 10000. If a worker appears multiple times in the list, sum up the hours.
At the end print the index number of the worker with the most hours worked.
For ex:
3
Worker 23 worked 5 hours.
Worker 5 worked 10 hours.
Worker 23 worked 7 hours.
The output will be: 23.
I read de file like this:
#include <iostream>
#include <fstream>
#include <cstring>
using namespace std;
char s[50];
int n;
int main() {
ifstream fin("date.in");
fin >> n;
fin.getline(s, 50);
for (int i = 0; i < n; ++i) {
fin.getline(s, 50);
}
return 0;
}
My problem is that I cannot differentiate "x" from "y". Can anyone give me an idea to how to start the problem?
You can decompose a line with std::istringstream.
Example:
std::string line = "Worker 23 worked 5 hours.";
std::string word;
int id = 0;
int time = 0;
std::istringstream is(line);
if (is >> word >> id >> word >> time)
{
...
do whatever with 'id' and 'time'
...
}
There are two main approaches to solve this problem.
If you are learning C++ and do not know the language and its algorithm that good, then you need to go the hard way and do everything by yourself.
If you are an experienced developer, then you can use the full power of C++. This will result in a compact solution, but you need to learn a while to understand it.
Whatever, I will show you a poetntial soultion for both approaches.
Number 1. We will use only every basic C++ language and as less library functions as possible. We will even not use a std::string or a std::vector, which is basically a "must have" here.
What will be do?
Open the file
Read line by line in a string (char array)
Scan the char array character by character to find the numbers (id, hours)
Check, if a worker already existed. If yes, add up, if no, create a new one. Use 2 arrays for that.
Last but not least, find the max element
This is a horrible work with a lot of programming . . .
Please see:
#include <iostream>
#include <fstream>
#include <iomanip>
// The format of the line is know. A length of 100 will be sufficient for the given use cases.
const int MaxLineLength = 50;
int main() {
// Open the source file
std::ifstream fin("r:\\date.in");
// Check, if the file could be opened
if (fin) {
// This will contain the content of one line in the source file
char lineText[MaxLineLength];
// Now, read the number of lines that we shall process
int numberOfLinesToProcess = 0;
// Read a number, check, if that works and check for a valid range (>0)
if ((fin >> numberOfLinesToProcess >> std::ws) and (numberOfLinesToProcess > 0)) {
// We will now waste some space and create space for numberOfLinesToProcess
// although we know, that there moste likely are duplicates
int* workerID = new int[numberOfLinesToProcess];
int* numberOfHours = new int[numberOfLinesToProcess];
// We need a counter to detect the differnt workers
int differentWorkersCount = 0;
// Now read line by line from the source file
for (int lineIndex = 0; lineIndex < numberOfLinesToProcess and fin; ++lineIndex) {
// Read one complete line fomr the file
fin.getline(lineText, MaxLineLength);
// Check, if the input function worked OK
if (fin) {
// Here will store the temporary extracted values
int wID = 0, whours = 0;
// We are looking for a number and will skip the text
bool waitForNumberToStart = true;
char* startOfNumber = nullptr;
bool waitForWorkerID = true;
// Now, iterate over the string and find digits, the worker ID
for (char* cptr = &lineText[0]; *cptr != '\0'; ++cptr) {
// Check, if we found a digit
if (waitForNumberToStart) {
if (*cptr >= '0' and *cptr <= '9') {
// First time taht we saw a digit. Remember start position in string
startOfNumber = cptr;
// Now we wait no longer for a number to start. We wait for the end
waitForNumberToStart = false;
}
}
else {
// We found a digit and now wait for the end of the number, so a none-digit
if (not (*cptr >= '0' and *cptr <= '9')) {
if (waitForWorkerID) {
wID = std::atoi(startOfNumber);
waitForWorkerID = false;
}
else {
whours = std::atoi(startOfNumber);
break;
}
waitForNumberToStart = true;;
}
}
}
// Now, we have read both numbers. The worker IS and the working hours.
// Check, if we saw this worker already
bool workerFound = false;
for (int i = 0; i < differentWorkersCount and not workerFound; ++i) {
// Check, if such a worker already exists
if (workerID[i] == wID) {
// Yes, worker did exists, add working hours.
numberOfHours[i] += whours;
workerFound = true;
}
}
// If the worker did not yet exist, then we create a new one
if (not workerFound) {
// Cearte a new worker at the end of the array
workerID[differentWorkersCount] = wID;
numberOfHours[differentWorkersCount] = whours;
// Now we have one worker more
++differentWorkersCount;
}
}
else {
// Problem while reading
std::cout << "\nError: Problem while reading\n";
break;
}
}
// So, now we have found all workers and summed up all hours
// Search for max working time
int indexForMax = 0;
int maxWorkingTime = 0;
// Check all workers
for (int i = 0; i < differentWorkersCount; ++i) {
// Did we find a new max?
if (numberOfHours[i] >= maxWorkingTime) {
// If yes, then remeber this new may and remeber the workers index
maxWorkingTime = numberOfHours[i];
indexForMax = i;
}
}
// Now, we found everything and can show the result
std::cout << "Max: Worker: " << workerID[indexForMax] << " with: " << numberOfHours[indexForMax] << " hours\n";
// Release memory
delete[] workerID;
delete[] numberOfHours;
}
else {
// Invalid file content
std::cout << "\nError: Invalid file content\n";
}
}
else {
// The file could not be opened. Show error message
std::cout << "The source file yould not be opened\n";
}
}
Yes, indeed, the above works of course, but you really need to write a lot of code.
Modern C++ will help you here a lot. For many problems, we have ready-to-use functions.
Then the code will be rather compact in the end.
Please see:
#include <iostream>
#include <fstream>
#include <iomanip>
#include <unordered_map>
#include <algorithm>
using Sum = std::unordered_map<int, int>;
namespace rng = std::ranges;
int main() {
// Open file and check, if it could be opened
if (std::ifstream ifs{ "r:\\date.in" }; ifs) {
Sum data{};
// Read number of entries to operate on. We will ignore this value, we do not need it
if (int numberOfEntries{}; (ifs >> numberOfEntries >> std::ws)) {
// For reading the text and throwing away
std::string dummy;
// Read all lines and add up
for (int id{}, hours{}; ifs >> dummy >> id >> dummy >> hours >> dummy; data[id] += hours);
}
// Get max
const auto& [worker, hours] = *rng::max_element(data, {}, &Sum::value_type::second);
// Show result
std::cout << "Max: Worker: " << worker << " with: " << hours << " hours\n";
}
else std::cerr << "\n*** Error: Could not open souirce file\n";
}
Also many intermediate solutions are possible.
This depends on how much you learned already.
I would like to create a c++11 program that takes in 10 positive integers and gives the user the total. In the event of a negative number or a char input, the exception should be thrown and the user must re enter their value.
The program below works with negative numbers. However, when I enter a character like "a", the program goes into an infinite loop and I cannot figure out why.
Any and all help will be appreciated
#include <iostream>
int main(){
int array[10] = {0};
int total = 0;
for(int i =0; i < 10; i++){
std::cout<<"Number "<< i+1 << ": " <<std::endl;
std::cin >> array[i];
try{
if(array[i] < 0 || std::cin.fail())
throw(array[i]);
}
catch(int a){
std::cout<< a <<" is not a positive number! "<<std::endl;
i-=1; // to go back to the previous position in array
}
}
for(int k = 0; k < 10; k++)
total+=array[k];
std::cout<<"Total: " <<total<<std::endl;
}
If you get invalid input there are two things to thing you need to do:
Clear the stream status. This is done using the clear function.
Remove the invalid input from the buffer. This is usually done using the ignore function.
As for your program, you don't need exceptions here, just using unsigned integers and checking the status is enough:
unsigned int array[10] = { 0 };
...
if (!(std::cin >> array[i])
{
std::cout << "Please input only non-negative integers.\n";
// First clear the stream status
std::cin.clear();
// Then skip the bad input
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
// Make sure the index isn't increased
--i;
}
To use exceptions similar to what you do now, the solution is almost exactly the same as above:
unsigned int array[10] = { 0 };
...
if (!(std::cin >> array[i])
{
throw i;
}
catch (int current_index)
{
std::cout << "The input for number " << current_index + 1 << " was incorrect.\n";
std::cout << "Please input only non-negative integers.\n";
// First clear the stream status
std::cin.clear();
// Then skip the bad input
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
// Make sure the index isn't increased
--i;
}
Do not forget to include limits header file while using following line in your code :
std::cin.ignore(std::numeric_limits::max(), '\n');
because numeric_limits template is defined in this header file !
for(int i=0;i<50;i++,size++)
{
cin >> inputnum[i];
cout << size;
if(inputnum[i] == '.')
{
break;
}
}
The break breaks the input stream but the size keeps outputting.
The output of size is 012345678910111213...474849.
I tried putting size++ inside the loop but it made no difference. And size afterwards will be equal to 50, which means it went through the full loop.
I forgot to explain that I added the cout << size within the loop to debug/check why it outputted to 50 after the loop even if I only inputted 3 numbers.
I suspect that inputnum is an array of int (or some other numeric type). When you try to input '.', nothing actually goes into inputnum[i] - the cin >> inputnum[i] expression actually fails and puts cin into a failed state.
So, inputnum[i] is not changed when inputting a '.' character, and the break never gets executed.
Here's an slightly modified version of your code in a small, complete program that demonstrates using !cin.good() to break out of the input loop:
#include <iostream>
#include <ostream>
using namespace std;
int main()
{
int inputnum[50];
int size = 0;
for(int i=0;i<50;i++,size++)
{
cin >> inputnum[i];
if (!cin.good()) {
break;
}
}
cout << "size is " << size << endl;
cout << "And the results are:" << endl;
for (int i = 0; i < size; ++i) {
cout << "inputnum[" << i << "] == " << inputnum[i] << endl;
}
return 0;
}
This program will collect input into the inputnum[] array until it hits EOF or an invalid input.
What is inputnum ? Make sure t's a char[]!! with clang++ this compiles and works perfectly:
#include <iostream>
int main() {
int size = 0;
char inputnum[60];
for(int i=0;i<50;i++,size++) {
std::cin >> inputnum[i];
std::cout << size;
if(inputnum[i] == '.') {
break;
}
}
return 0;
}
(in my case with the following output:)
a
0a
1s
2d
3f
4g
5.
6Argento:Desktop marinos$
Your code seams OK as long as you're testing char against char in your loop and not something else.. Could it be that inputnum is some integral value ? if so, then your test clause will always evaluate to false unless inputnum matches the numerical value '.' is implicitly casted to..
EDIT
Apparently you are indeed trying to put char in a int[]. Try the following:
#include <iostream>
int main() {
using namespace std;
int size = 0;
int inputnum[50];
char inputchar[50];
for(int i=0;i<50;i++,size++) {
cin >> inputchar[i];
inputnum[i] = static_cast<int>(inputchar[i]); // or inputnum[i] = (int)inputchar[i];
cout << size << endl; // add a new line in the end
if(inputchar[i] == '.') break;
}
return 0;
}
Then again this is probably a lab assignment, in a real program I'd never code like this. Tat would depend on the requirements but I'd rather prefer using STL containers and algorithms or stringstreams. And if forced to work at a lower-level C-style, I'd try to figure out to what number '.' translates to (simply by int a = '.'; cout << a;`) and put that number directly in the test clause. Such code however might be simple but is also BAD in my opinion, it's unsafe, implementation specific and not really C++.
This is my code.
#include <iostream>
using namespace std;
typedef struct
{
int polski;
int wf;
int matma;
}oceny;
int funkcja_liczaca(int suma, int ile_liczb, int ktory_przedmiot, oceny &temporary);
int main()
{
int suma = 0;
int temp[3];
int ile_liczb_zostalo_wprowadzonych = 0;
oceny database;
string teksty[3] = {"polski: ", "wf: ", "matma: "};
for (int i=0; i!=3; i++)
{
cout << teksty[i] << endl;
while(temp[i]!=0)
{
cin >> temp[i];
if(cin.good()) //floating point exception here. the code don't even step into this one.
{
{
suma = temp[i] + suma;
ile_liczb_zostalo_wprowadzonych++;
if(temp[i]==0){ile_liczb_zostalo_wprowadzonych--;}
}
}else cout << "error";
};
funkcja_liczaca(suma, ile_liczb_zostalo_wprowadzonych, i, database);
suma = 0;
ile_liczb_zostalo_wprowadzonych = 0;
}
cout << "output of struct members in main() \n";
cout << database.polski << endl;
cout << database.wf << endl;
cout << database.matma << endl;
return 0;
}
int funkcja_liczaca(int suma, int ile_liczb, int ktory_przedmiot, oceny &temporary)
{
if(ktory_przedmiot==0){temporary.polski=suma/ile_liczb;cout << temporary.polski << endl;}
if(ktory_przedmiot==1){temporary.wf=suma/ile_liczb;cout << temporary.wf << endl;}
if(ktory_przedmiot==2){temporary.matma=suma/ile_liczb;cout << temporary.matma << endl;}
}
It counts arithmetic average of inputed numbers untill user input 0 which ends loop. then the arithmetic average of thoose numbers is counted in the funkcja_liczaca() and it's saved into the members of struct oceny.
everything works fine but i want to implement something like "stream" check while inputing from keyboard to prevent inputing bad variables into integer type variable.
so inputing 'g' into temp[i] is causing floating point exception. the question is why? cin.good() and cin.fail() is not working.
When you want to deal with errors in the input stream, it's better to read the input line by line as a string and then attempt to extract your data from the string. If extraction of the data from the string is successful, proceed to process the data. Otherwise, attempt to read the next line of text. Here's the core logic for that.
while ( true )
{
cout << teksty[i] << endl;
std::string line;
if ( !getline(cin, line) )
{
// Problem reading a line of text.
// Exit.
exit(EXIT_FAILURE);
}
// Construct a istringstream object to extract the data.
std::istringstream istr(line);
if ( istr >> temp[i] )
{
// Extracting the number was successful.
// Add any additional checks as necessary.
// Break out of the while loop.
break.
}
// Bad input. Continue to the next iteration of the loop
// and read the next line of text.
}
Basically, below is my main.cpp and when I try to run it with Qt's debugger, I get the "EXC_BAD_ACCESS" error ("Could not access memory") along with an arrow next to the first line of main (where it says Puzzle puzzle;). I thought it might be a problem with my Puzzle class, but when I moved that line elsewhere, I still got the bad access error with the debugger leaving the same yellow arrow by the first line of main. What's causing this error? My program ran fine half an hour ago, and then started throwing this error and I haven't even modified the code since it last worked. Also, this is one of my first projects in C/C++, so I'm not totally familiar with garbage collection. Could it be something to do with memory leaks or bad memory allocation?
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include "piece.h"
#include "puzzle.h"
#include "state.h"
using namespace std;
//function prototypes
Puzzle initPuzzle(string*, int);
int countWords(string);
//count the number of words (separated by white space) in a string
int countWords(string s){
int words = 0;
char * temp = new char[s.size() + 1];
copy(s.begin(), s.end(), temp);
temp[s.size()] = '\0';
temp = strtok (temp, " ");
while (temp != NULL){
words++;
temp = strtok (NULL, " ");
}
delete(temp);
return words;
}
//first checks validity of input
//if error(s), display appropriate message & exit program
//otherwise, returninstance of puzzle class from input file
//params: lines = array of strings, each of which is a line from input... size = # of elems in 'lines'
Puzzle initPuzzle(string * lines, int size){
//create instance of puzzle
//if bad piece found, throw it out
//if first piece (Z) is invalid, the next piece becomes goal piece
//if there are 0 valid pieces, display error to user and exit program
Puzzle ret;
int rows, cols;
if(size < 2){
//not enough lines for valid input
cout << "Error: Input too short" << endl << "Exiting program..." << endl;
exit(0);
}
istringstream iss(lines[0]);
if((iss >> rows >> cols) && countWords(lines[0])==2){
ret.rows=rows;
ret.cols=cols;
} else {
cout << "Error: Invalid first line" << endl << "Exiting program..." << endl;
exit(0);
}
if(rows < 1 || cols < 1){
cout << "Error: Invalid dimensions" << endl << "Exiting program..." << endl;
exit(0);
}
//now check the rest of the lines (ie the pieces)
for(int i=1; i<size; i++){
Piece newPiece;
int startRow, startCol, width, height;
char direction;
istringstream iss(lines[i]);
if(countWords(lines[i])==5 && (iss >> startRow >> startCol >> width >> height >> direction)){
//row is formatted correctly, create instance of Piece
newPiece = Piece(startRow, startCol, width, height, direction); //validate this piece later... if valid, add to pieces
} else {
//invalid row... entire input is invalid
cout << "Error: Invalid row(s)" << endl << "Exiting program..." << endl;
exit(0);
}
//now validate temporary piece...
//first make sure piece doesn't fall outside of grid
if(newPiece.startRow < 1 || newPiece.startCol < 1 || newPiece.startRow-1 > (rows - newPiece.height) ||
newPiece.startCol-1 > (cols - newPiece.width)){
//newPiece goes over the edge of the puzzle grid
cout << "Piece goes beyond grid... Throwing it out" << endl;
continue;
}
if(newPiece.direction != 'b' && newPiece.direction != 'h' && newPiece.direction != 'v' && newPiece.direction !='n'){
//newPiece has invalid direction
cout << "Piece has invalid direction... Throwing it out" << endl;
continue;
}
if(ret.pieceCount!=0 && ret.pieceOverlap(newPiece)){
//current piece overlaps existing one
cout << "Piece overlaps another piece... Throwing it out" << endl;
continue;
}
//if loop iteration reaches this point, piece is valid and can be added to puzzle
cout << "Piece is good!" << endl;
ret.addPiece(newPiece);
}
if(ret.pieceCount == 0){
//all pieces were invalid
cout << "Error: Puzzle has no pieces" << endl << "Exiting program..." << endl;
exit(0);
}
//now assign id's to the pieces...
for(int i=0; i<ret.pieceCount; i++){
if(i==0){
ret.pieces[i].id = 'Z';
} else {
ret.pieces[i].id = i;
}
}
return ret;
}
int main()
{
Puzzle puzzle; //single instance of puzzle class... initialized later after input & piece verification
string inputFile; //name of input file... provided by user
string line; //single line from input file
string * inputLines = new string[9000]; //array of lines from the input file
ifstream infile;
int size = -1; //size of inputLines array, initialized to -1
cout << "Enter name of input file: ";
cin >> inputFile;
infile.open(inputFile.c_str());
if(infile){
while(infile){
size++;
getline(infile,line);
inputLines[size] = line;
}
infile.close();
} else {
cout << "Error: Input file could not be opened" << endl << "Exiting program" << endl;
exit(0);
}
puzzle = initPuzzle(inputLines, size); //now check the input for validity, and if valid, initialize puzzle
return 0;
}
One mistake (two really) is within the function countWords():
temp is created using new[] but is deallocated use delete, it must be delete[] (new -> delete and new[] -> delete[] and avoid explicit dynamic memory management whenever possible)
the value of temp is not the value it was originally assigned which it must be when delete[]ing
Explicit dynamic memory allocation can be avoided completely by using a std::istringstream instead to count the words:
std::istringstream in(s);
std::string ignored;
while (in >> ignored) words++;
Other points:
prefer std::vector to explicit dynamic memory allocation management:
std::vector<std::string> inputLines; // and use 'push_back()'.
always check result of input operations immediately to ensure success:
if (cin >> inputFile)
{
ifstream infile(inputFile);
if (infile)
{
std::string line;
while (std::getline(infile, line)) lines.push_back(line);
}
}