Reading into an Array Multiple Times - c++

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...

Related

How to reset std::count return value

std::count returns a value and I need this value to reset to 0 for all characters in the variable 'counter' after executing the inner for loop. Goal is to count how many times a character appears. If this character appears twice in the string, add one to variable 'd'. If it appears three times, add one to variable 'e'.
Not sure what else to try or if there is potentially a better function to achieve my result.
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <cstring>
int main() {
std::string data;
std::vector<std::string> myString;
std::vector<char> myChar;
int d = 0, e = 0;
std::ifstream inFile;
inFile.open("C:\\Users\\Administrator\\Desktop\\c++ files\\input2.txt");
if (!inFile) {
std::cout << "oops";
}
for (int i = 0; i < 1; i++) {
inFile >> data;
std::copy(data.begin(), data.end(), std::back_inserter(myChar)); //copy from string data to vector myChar via back inserter.
char counter = 'a';
for (int i = 0; i < 26; i++) {
int myCount = std::count(myChar.begin(), myChar.end(), counter);
if (myCount == 2) {
d++;
}
else if (myCount == 3) {
e++;
}
std::cout << "Counter : " << counter << " myCount : " << myCount << "\n";
counter++;
}
}
std::cout << "d is: " << d << "\n";
std::cout << "e is: " << e << "\n";
return 0;
}
input file -- https://adventofcode.com/2018/day/2
The program works correctly on first inner for loop, but second and after return values that are too high (albeit correct) for the 'myCount' variable.
std::count doesn't just give you a random value, it gives you a specific value based on the contents of the range you give it. You can't change that behaviour, not should you want to.
Instead, look at that range. Why does std::count gives values that you don't expect? They are either "too high" or they are "correct" and cannot be both; fortunately they are the latter.
This is because you repeatedly std::back_insert to the vector inside your loop. As the loop progresses, you keep counting the old characters from the last time!
If you first cleared myChar then you wouldn't have the problem. Or, ideally, bring the declaration of myChar inside the loop.
A few fixes
1) On error the program should end, not continue:
if (!inFile)
{
std::cout << "oops";
return 1;
}
2) a)myChar is accumulating all the chars of all previously read words, so it has to be cleared before use with every pass of the loop, best to move it's declaration into the block required;
b) if you're using a counter just to count but not using it, better to iterate over the data - in this case get rid of i and iterate with chars checked_char:
while (inFile >> data)
{
std::vector< char > myChar;
std::copy(data.begin(),
data.end(),
std::back_inserter(myChar)); //copy from string data to vector myChar via back inserter.
for (char checked_char = 'a'; checked_char <= 'z'; ++checked_char)
{
int myCount = std::count(myChar.begin(), myChar.end(), checked_char);
if (myCount == 2)
{
d++;
}
else if (myCount == 3)
{
e++;
}
std::cout << "Counter : " << checked_char << " myCount : " << myCount << "\n";
}
}

What is the most efficient way of outputting strings stored in a stack but with the letters of the strings reversed in order?

What the program does is allows the user to input a bunch of strings, stores them in a stack, and then outputs all of those strings in the stack but in reverse order and flipped. Is there a more efficient way of doing what I did? Maybe using a different data structure (other than a stack)? I though a stack would be best because the strings stored in the stacks need to be outputted in the opposite order that they were inputted.
Here's the code:
#include <iostream>
#include <stack>
using namespace std;
int main() {
stack <string> elements;
string element;
cout << "Hello, welcome to Flippy-McBackwardson! \n\n" << endl;
cout << "Please enter a bunch of strings (type END to terminate your list): " << endl;
do {
getline(cin, element);
elements.push(element);
} while (element != "END");
elements.pop();
cout << "\nFlippy Backward Version: " << endl;
for (int x = elements.size()-1; x >= 0; x--){
for (int i = elements.top().length()-1; i >= 0; i--){
cout << elements.top()[i];
}
elements.pop();
cout << endl;
}
return 0;
}
You can use reverse iterators (accessible using std::rbegin() and std::rend()) which should be cleaner and safer as they don't involve tricky subscripting math that is prone to off-by-one errors (at the very least).
while(!elements.empty())
{
std::for_each(std::rbegin(elements.top()), std::rend(elements.top()),
[](char c){ std::cout << c; });
std::cout << '\n';
elements.pop();
}

Mean and Mode of vector array - How can I make a smaller improvement in the function

Doing an exercise to find the mean and mode of a list of numbers input by a user. I have written the program and it works, but I'm wondering if my function 'calcMode' is too large for this program. I've just started looking into functions which is a first attempt. Would it be better to write smaller functions? and if so what parts can I split? Im pretty new to C++ and also looking if I can improve this code. Is there any changes I can make to make this run more efficient?
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int calcMean(vector<int> numberList)
{
int originNumber = numberList[0];
int nextNumber;
int count = 0;
int highestCount = 0;
int mean = 0;
for (unsigned int i = 0; i <= numberList.size() - 1; i++)
{
nextNumber = numberList[i];
if (nextNumber == originNumber)
count++;
else
{
cout << "The Number " << originNumber << " appears " << count << " times." << endl;
count = 1;
originNumber = nextNumber;
}
}
if (count > highestCount)
{
highestCount = count;
mean = originNumber;
}
cout << "The Number " << originNumber << " appears " << count << " times." << endl;
return mean;
}
int main()
{
vector<int> v;
int userNumber;
cout << "Please type a list of numbers so we can arrange them and find the mean: "<<endl;
while (cin >> userNumber) v.push_back(userNumber);
sort(v.begin(), v.end());
for (int x : v) cout << x << " | ";
cout << endl;
cout<<calcMean(v)<<" is the mean"<<endl;
return 0;
}
One thing to watch out for is copying vectors when you don't need to.
The function signature
int calcMode(vector<int> numberList)
means the numberList will get copied.
int calcMode(const & vector<int> numberList)
will avoid the copy. Scott Meyer's Effective C++ talks about this.
As an aside, calling is a numberList is misleading - it isn't a list.
There are a couple of points that are worth being aware of in the for loop:
for (unsigned int i = 0; i <= numberList.size()-1; i++)
First, this might calculate the size() every time. An optimiser might get rid of this for you, but some people will write
for (unsigned int i = 0, size=numberList.size(); i <= size-1; i++)
The size is found once this way, instead of potentially each time.
They might even change the i++ to ++i. There used to a potential overhead here, since the post-increment might involve an extra temporary value
One question - are you *sure this gives the right answer?
The comparison nextNumber == originNumber is looking at the first number to begin with.
Try it with 1, 2, 2.
One final point. If this is general purpose, what happens if the list is empty?
Would it be better to write smaller functions?
Yes, you can make do the same job using std::map<>; which could be
a much appropriate way to count the repetition of the array elements.
Secondly, it would be much safer to know, what is the size of the
array. Therefore I suggest the following:
std::cout << "Enter the size of the array: " << std::endl;
std::cin >> arraySize;
In the calcMode(), you can easily const reference, so that array
will not be copied to the function.
Here is the updated code with above mentioned manner which you can refer:
#include <iostream>
#include <algorithm>
#include <map>
int calcMode(const std::map<int,int>& Map)
{
int currentRepetition = 0;
int mode = 0;
for(const auto& number: Map)
{
std::cout << "The Number " << number.first << " appears " << number.second << " times." << std::endl;
if(currentRepetition < number.second )
{
mode = number.first; // the number
currentRepetition = number.second; // the repetition of the that number
}
}
return mode;
}
int main()
{
int arraySize;
int userNumber;
std::map<int,int> Map;
std::cout << "Enter the size of the array: " << std::endl;
std::cin >> arraySize;
std::cout << "Please type a list of numbers so we can arrange them and find the mean: " << std::endl;
while (arraySize--)
{
std::cin >> userNumber;
Map[userNumber]++;
}
std::cout << calcMode(Map)<<" is the mode" << std::endl;
return 0;
}
Update: After posting this answer, I have found that you have edited your function with mean instead of mode. I really didn't get it.
Regarding mean & mode: I recommend you to read more. Because in general, a data set can have multiple modes and only one mean.
I personally wouldn't split this code up in smaller blocks, only if i'd want to reuse some code in other methods. But just for this method it's more readable like this.
The order of excecution is aroun O(n) for calc which is quite oke if you ask me

Determining if user input is palindrome with C++

First of all, I very much appreciate any help you are willing to provide. I am new to C++ and have been scouring this website as well as other resources for the solution to my problem.
Further, this was indeed a portion of a homework assignment. However, the assignment has been turned in (upsettingly, without getting this code to work). It would be great to get an explanation for what the problem in my specific code is and how to fix my current code, rather than the just rewritten code with a different way to approach to problem. I certainly found plenty of ways to solve this problem on this wonderful site!
I am getting no errors with my code, however the reversal output is not showing the reversed character array. This results in my little program here always showing "Your string is not a palindrome! :(" no matter what the input is.
#include <iostream>
#include <string>
using namespace std;
int isPalindrome(char *input, char *input2);
char reverseString(char *input);
int main ()
{
char input[50];
char input2[50];
cout << "Please enter a string of characters no larger than 50." << endl;
cin.getline(input, 50);
reverseString(input);
cout << "The reversed string is " << input2 << endl;
int result;
result = isPalindrome(input, input2);
if(result == 0)
cout << "Your string is a palindrome!" << endl;
else
cout << "Your string is not a palindrome! :( " << endl;
return 0;
}
int isPalindrome(char* first, char* second)
{
if (*first == *second)
return 0;
else
return 1;
}
char reverseString(char* input2)
{
int size = sizeof(input2);
for (int i = 0; i < (size/2); i ++)
swap(input2[i], input2[size-i-1]);
return *input2;
}
Again, I appreciate any help you can provide! I apologize if this is a simple error that I am overlooking and should have been able to find elsewhere.
Checking for a palindrome does not take this much effort.
bool isPalindrome(const char* s) // this function is self-contained.
{ // the caller does not need to provide
size_t n = strlen(s); // any pre-computed value.
if (n == 0)
return false;
const char* e = s + n - 1;
while (s < e)
if (*s++ != *e--)
return false;
return true;
}
int main ()
{
char input[50];
cout << "Please enter a string of characters no larger than 50." << endl;
cin.getline(input, 50);
bool result = isPalindrome(input);
cout << "Your string is"
<< ((result) ? " " : " not ")
<< "a palindrome!\n";
return (result) ? 1 : 0;
}
In your reverseString function:
char reverseString(char* input2)
{
int size = sizeof(input2); // <-- ?? sizeof(char*) != strlen(input2)
size_t size = strlen(input2); // <-- should read.
for (int i = 0; i < (size/2); i ++)
swap(input2[i], input2[size-i-1]);
return *input2; // what's this? returning first char? why?
}

C++ - How to count how many times occur the same string in a list

I searched a lot on google and stackoverflow but I couldn't find my answer. I'm actually reading a C++ book (C++ Primer 5th Edition) and they're asking me to do an exercise.
"Write a program that reads several transactions and counts how many transactions occur for each ISBN" (Console Project)
This is my code atm :
Sales_item currentItem, item;
if (cin >> currentItem)
{
int cnt = 1;
while (cin >> item)
{
if (currentItem.isbn() == item.isbn())
{
++cnt;
}
else
{
cout << currentItem.isbn() << " occurs " << cnt << " times " << endl;
cnt = 1;
currentItem = item;
}
}
cout << item.isbn() << " occurs " << cnt << " times " << endl;
}
I won't explain how work the transactions so I'm gonna ask it in another way.
I type in my console 6(or more) random strings as exemple:
101A
102A
101A
101A
103A
102A
I want the result the result (output) to be:
101A occurs 3 times.
102A occurs 2 times.
103A occurs 1 times.
How would you do that?
Using std::map instead of list will be easier.
int main()
{
map<string,int> stringMap;
for (int i=0;i<3;i++)
{
cout<<"Enter string: ";
string s;
cin>>s;
if(stringMap.find(s)!=stringMap.end())
{
stringMap[s]++;
}
else
{
stringMap[s]=1;
}
}
for (map<string,int>::const_iterator itr = stringMap.cbegin(); itr!=stringMap.cend(); ++itr)
{
if(itr->second > 1)
cout<<itr->first << " occurs "<<itr->second<<" times"<<endl;
else
cout<<itr->first << " occurs "<<itr->second<<" time"<<endl;
}
return 0;
}
There are multiple approaches to a question like this, so the best would depend on your constraints. My approach would be:
while(GetInputString(str)) {
myStruct* ptr = existingList.Find(str);
if (!ptr) {
existingList.Add(str);
} else {
ptr->IncrementCount();
}
}
I've tried not to solve your problem for you - hopefully the answer gives you a template to work with...
#include <iostream>
#include <vector>
#include "Sales_item.h"
using namespace std;
void book_transactions(string isbn, int count);
int main()
{
vector<Sales_item> book_vec;
Sales_item book;
int total_transactions = 1;
// Reads in Sales_item objects
while (cin >> book)
{
book_vec.push_back(book);
}
// Compares ISBNs in vector, if two isbns are equal total transactions increases
for (int i = 0; i < book_vec.size() - 1; i++)
{
if (book_vec[i].isbn() == book_vec[i+1].isbn())
{
total_transactions = book_vec[i].get_units_sold() + book_vec[i+1].get_units_sold();
book_transactions(book_vec[i].isbn(), total_transactions);
}
}
}
void book_transactions(string isbn, int count)
{
cout << "ISBN: " << isbn << " " << "Transactions: " << count << endl;
}
I went into the Sales_item.h class and added a method to get the number of units sold. There was already a class member (unsigned return type) under private. I just created a getter and then created a vector of Sales_items. This solution seems to work. I'm sure the intended problem is not for an N set of sales_items, however this should work as a "duct-tape" solution. There is no need to play around with pointers at this point. A simple vector array and an algorithm to compare adjacent objects works just fine.