Trying to make a conditional forloop when reading from a file - c++

I am trying to create a for loop that has a conditional statement which reads until an operation is found, ex. (+,-,/,*), but every time I try I get an error:
Unhandled exception at 0x7936F2F6 (ucrtbased.dll) in CIS310 Project 44.exe: An invalid parameter was passed to a function that considers invalid parameters fatal.
while (getline(infile, hex))
{
n = hex.length();//find the length of the line
for (i = 0; hex[i] != '/'||'+'||'-'||'*'; i++,++k) //loop to split the first hexadecimal number
h1 = h1 + hex[i];
for (i++; i < n - 1; i++) //loop to get the second hexadecimal number
h2 = h2 + hex[i];
n1 = convertDecimal(h1); //convert the first hexadecimal number to decimal
n2 = convertDecimal(h2);

Your condition hex[i] != '/'||'+'||'-'||'*' is malformed. C++ requires that you specify both sides of the operator each time, so you will need something more similar to hex[i] != '/' || hex[i] != '+' || ....

You have to check after every ' | | '(OR), like:
hex[i] != '/' || hex[i] != '+' || hex[i] != '-' || hex[i] != '*'

This is a similar code to what you wrote:
while(getline(file,line))
{
string firstPart = "";
unsigned int i;
//We can use the algorithm library to search for them but its ok
for(i=0;(line[i] != '+') || (line[i] != '-') || (line[i] != '*') || (line[i] != '/') || (line[i] != '\0');i++ );
firstPart = line.substr(0,i);
}
now if you tried this, it will cause the same error (or atleast similar to it), if we even try to print every character in the loop
for(/*stuff*/)
cout << line[i];
Then notice this will become an infinite loop, the problem is that you're checking the character line[i] if it wasn't a + or - or * or / all at the same time, fix this by changing the || to &&.
I'll suppose that your file (named testfile.txt) has the content below:
0xAB+0xCD
0x11-0x03
Sample working code:
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int main()
{
ifstream file("testfile.txt");
///Don't forget to check if file has opened
if(!file.is_open())
{
cout << "File didn\'t open :(";
return 0;
}
string line;
while(getline(file,line))
{
string firstPart = "",secondPart = "";
char operation;
unsigned int i;
//We can use the algorithm library to search for them but its ok
for(i=0;(line[i] != '+') && (line[i] != '-') && (line[i] != '*') && (line[i] != '/') && (line[i] != '\0');i++ );
firstPart = line.substr(0,i);
operation = line[i];
secondPart = line.substr(i+1,firstPart.size());
}
file.close();
return 0;
}

Related

out of range error on top 10 frequent words project

I was assigned a project to read a large text file of different articles, and print out the top 10 most frequent words, i managed to remove all unnecessary information from the file and print it into a string, for simplicity i put a small part the list of unigrams and their frequency in a text file (text2.txt), this is essentially the format in which all the unigrams are written: "(unigram)":(it's frequency within that article),"(another unigram)":(it's frequency within that article) and so on
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
void something(string input, int sizeOfDoc, string unigrams[], int freq[]){
char found;
char last_found2;
char last_found;
char next_found;
bool b = false;
int pos = 0;
string word;
unsigned int u=0;
unsigned int f=0;
for(int x = 0; x<sizeOfDoc; x++){
found = input.at(x);
if(x==sizeOfDoc){
next_found= '*';
}else{next_found = input.at(x+1);}
if(x==0){
last_found = '*';
}else{last_found = input.at(x-1);}
if(x==0 || x==1){
last_found2 = '*';
}else{last_found2 = input.at(x-2);}
if((last_found2 >= '1' && last_found2 <= '9') && last_found == ',' && found == '\"' && //
(next_found >='a' && next_found <='z' || next_found >='A' && next_found <='Z')){ //finds first letter of unigram
word = next_found; //a
}
else if((found >='a' && found <='z' || found >='A' && found <='Z') && //
(last_found >='a' && last_found <='z' || last_found >='A' && last_found <='Z')){ //finds middle of unigram
word += found; //b abc word = "abc"
}
else if((last_found2 >='a' && last_found2 <='z' || last_found2 >='A' && last_found2 <='Z') //
&& last_found =='"' && found == ':' && (next_found >= '1' && next_found <= '9')){ //finds frequency
word += last_found2; //adds last letter to word
for(int i=0; i <= u; i++){ //
if(word == unigrams[i]){ //
b = true; //
pos = i; //checks for duplicate, if found, returns position to pos
}
}
if (b==false){
unigrams[u] = word; //adds word to unigrams array
freq[f] = next_found; //adds frequency to freq array
}
else if(b == true){ //
freq[pos]=freq[f]; //increments frequency if duplicate found
}
f++;
u++;
}
}
}
int main() {
string unigrams[1279];
int freq[1279];
string s;
std::string newstring;
ifstream file; //
file.open("text2.txt"); //
while (!file.eof()) { //
getline(file, s); //
newstring += s + "\n"; //reads original text and inputs it into newstring
}file.close();
something(newstring, newstring.size(), unigrams, freq); //calls function
for (int x = 0; x <= 1278; x++) { //
cout << unigrams[x]; //prints unigrams to console
}
}
when i run the code it throws
terminate called after throwing an instance of 'std::out_of_range'
what(): basic_string::at: __n (which is 1278) >= this->size() (which is 1278)
i have tried using vectors instead of arrays with emplace_back, push_back, and directly assigning, all to no avail, there's much more to work on in the project, submission is tomorrow and the more i progress the more complex it gets ):
this is the text im using:
{"others":1,"air":1,"networks,":1,"conventional":1,"Environ.":1,"AHP":1,"Osterwalder,":1,"la":8,"Non-motorized":1,"(SHE).":1,"beer":1,"[7,8]":1,"provider":1,"futurible":1,"13(4),":1,"Agency":1,"24.":1,"concern":1,"eight":1,"facilitated":1,"2009":1,"review":1,"Car,":4,"viability.":1,"cycles":1,"contribute":1,"results,":1,"design":24,"CSIROPub.9780643094529,":1,"ecodesign":2,"reserves":1,"follow:":1,"sp\u00e9cifique":2,"(2017)[20,21,22].":1,"pp.":4,"Costs":1,"diversity":1,"In-depth":1,"Both":1,"\u2013":6,"Grenoble":1,"realistic":2,"Largepurchasecost:":1,"navale":1,"Est,":1,"petits":1,"Support":1,"eliminated":1,"relationship,":1,"progressed,":1,"Imnm":1,"significantly":2,"76":1,"Technical":1,"Tertre,":1,"(Fig.":1,"Freeman,":1,"(1.28>Ib":1,"IT":2,"defined":1,"maturity":1,"experimentation.":1,"review,":2,"interests":1,"tools":1,"Firm":3,"opportunities.":1,"behaviour":1,"2014":1,"fili\u00e8res":1,"feedback":3,"interviews":1,"60":1,"187":1,"d\u00e9fi":1,"strategies":3,"did":1,"Techniques":8,"In":8,"have":5,"issues.":1,"useful":1,"se":1,"QC,":1,"vision":1,"regarding":1,"take":4,"Brezet,":1,"such":2,"circulaire":1,"software":1,"parameter":9,"appliances":3,"wedging":1,"Prod.":6,"domains\u201d.":1,"typologie,":1,"D\u00e9veloppement":3,"real":1,"desACVcomparatives":1}}
its a sample of one list of unigrams and their frequencies, the formatting is horrible as you can see i had to create a million conditions to take out the words without running into issues caused by things like words with quotation marks within them, the original file has 1500 publications and this text is just a small part of one of them, thank you for reading this at least
Your bug is here, specifically because when you call input.at() the maximum argument should be sizeOfDoc - 1.
for(int x = 0; x<sizeOfDoc; x++){
found = input.at(x);
if(x==sizeOfDoc){
next_found= '*';
}else{next_found = input.at(x+1);}
If you consider the case where x == sizeOfDoc - 1, the last line shown will result in calling at with an argument of sizeOfDoc, which is too high. To fix this, change
if(x==sizeOfDoc){
to
if(x==sizeOfDoc - 1){

Recursive function that counts vowels

I have an assignment that asks me to "Write a recursive function vowels(s,low,high) that returns the number of vowels in an input string s[]." We are also limited by "Do not use the C++ string type. Read the characters into an array one at a time using cin.get()." I think I solved the task at hand, but it doesnt compile so I am unsure. If anyone could assist me and show how to fix my error and any errors I've made towards my solution that would be great.
Here is my error message.
***main.cpp:24:37: error: invalid conversion from ‘char*’ to ‘char’ [-fpermissive]
vowelCount = vowels(s, low, high);**
Here is my code:
#include <iostream>
#include <cstring>
using namespace std;
int vowels(char, int, int);
int main()
{
char s[500];
int high,
vowelCount,
low = 0;
cout << "Enter a string of characters: ";
cin.get(s, 500);
high = strlen(s);
vowelCount = vowels(s, low, high);
cout << "The string contains " << vowelCount << " vowels." << endl;
return 0;
}
int vowels(char s[], int low, int high)
{
if (s[low] == 'a' || 'A' || 'e' || 'E' || 'i' || 'I' || 'o' || 'O' || 'u' || 'U') {
return 1 + vowels(s, low + 1, high - 1);
} else {
return vowels(s, low + 1, high - 1);
}
}
We are talking about a so called XY-Problem here.
The teacher wants to cover recursive functions. The vowel counting is just some noise. Unfortunately the given example is a bad one, because it could be implemented by far better with an iterative approach.
The recursion is just adding unnecessary time and space complexity. So, the performance is worse compared to a simple loop in all cases.
If the teacher wants that you learn about recursion, you may want to first look at this description or that more practical example.
After having read this, you will understand that a loop can be simply converted into a recursive function.
I am not sure, why there is a "low" and a "high" parameter. Maybe because of the usage of C-strings stored in a char array (what is nonsense in C++). I doubt that 2 self calls should be established and the function should walk from the begin and the end of the string to the middle. That would even further reduce the performance. So, let us assume the standard case.
Next to your problem with the comparison. What you have written is wrong. The correct comparison in C++ would be:
if ((s[low] == 'a') || (s[low] == 'A') || (s[low] == 'e') ||
(s[low] == 'E') || (s[low] == 'i') || (s[low] == 'I') ||
(s[low] == 'o') || (s[low] == 'O') || (s[low] == 'u') ||
(s[low] == 'U'))
Of course, nobody would write such a long statement, because you can simply calculate, without any comparison, if an ASCII character is a vowel. You could simply write:
if (std::isalpha(s[low]) && ((0x208222 >> (s[low] & 0x1f)) & 1))
and thats it. If you are interested, I can explain the theory later, but not needed for this example.
Then, next, your recursive function is dangerously wrong, because it does not have an end condition. it will run forever or until the stack overflows.
So, you need to rework that, It could be done like this:
#include <iostream>
#include <cstring>
using namespace std;
int vowels(char[], int, int);
int main()
{
char s[500];
int high,
vowelCount,
low = 0;
cout << "Enter a string of characters: ";
cin.get(s, 500);
high = strlen(s);
vowelCount = vowels(s, low, high);
cout << "The string contains " << vowelCount << " vowels." << endl;
return 0;
}
int vowels(char s[], int low, int high)
{
int sum = 0;
if (low != high) {
if ((s[low] == 'a') || (s[low] == 'A') || (s[low] == 'e') ||
(s[low] == 'E') || (s[low] == 'i') || (s[low] == 'I') ||
(s[low] == 'o') || (s[low] == 'O') || (s[low] == 'u') ||
(s[low] == 'U'))
{
++sum;
}
sum += vowels(s,low+1,high);
}
return sum;
}
If we go a little bit more into the direction of C++ and use meaningful variable names and comments, then we could come up with that:
#include <iostream>
#include <cstring>
// A recursive function to count the vowels in a rang of a text
int countVowelsRecursive(char textToEvaluate[], unsigned int startPositionForEvaluation, unsigned int endPositionOfText)
{
// Here we will store the number of vowels. We will use tail recursion
// So, the overall calculation will be done at the end
unsigned int numberOfVowelsInThisRecursion = 0u;
// Now we are evaluating this character from the text
const char currentChar = textToEvaluate[startPositionForEvaluation];
// Check, for the end of recursion condition
if (startPositionForEvaluation != endPositionOfText) {
// Check, if it is a vowel
if ((currentChar == 'a') || (currentChar == 'A') || (currentChar == 'e') ||
(currentChar == 'E') || (currentChar == 'i') || (currentChar == 'I') ||
(currentChar == 'o') || (currentChar == 'O') || (currentChar == 'u') ||
(currentChar == 'U'))
{
// Vowel found. Increase counter by one
++numberOfVowelsInThisRecursion;
}
// Tail recursion. Self call, starting at next position of the string
numberOfVowelsInThisRecursion +=
countVowelsRecursive(textToEvaluate,startPositionForEvaluation + 1, endPositionOfText);
}
// This will be the final result
return numberOfVowelsInThisRecursion;
}
// We will allow a maximal input text length like this
constexpr unsigned int MaxTextLength = 500u;
// Driver code / test function
int main()
{
// Here we will store the text from the user
char text[MaxTextLength]{};
// Give instructions and get the text
std::cout << "Enter a string of characters: ";
std::cin.get(text, MaxTextLength);
// Set text parameters for the evaluation
unsigned int startOfText = 0u;
unsigned int endOfText = static_cast<unsigned int>(strlen(text));
// Calculate the vowels
unsigned int vowelCount = countVowelsRecursive(text, startOfText, endOfText);
// Show result to user
std::cout << "The string contains " << vowelCount << " vowels." << std::endl;
return 0;
}
And if we would be allowed to use C++ then we would write:
#include <iostream>
#include <string>
#include <algorithm>
int main()
{
// Give instructions and get the text
std::cout << "\nEnter a text:\n";
if (std::string text{}; std::getline(std::cin, text))
// Show result to user
std::cout << "\n\nThe string contains "
<< std::count_if(text.begin(), text.end(), [](const char c){ return std::isalpha(c) && ((0x208222 >> (c & 0x1f)) & 1);})
<< " vowels.\n";
return 0;
}
Have fun . . .

removing vowels from c++ string

char arr[5000];
ifstream is("test.txt");
is.get(arr,5000);
int i = 0;
int j = 0;
cout << arr << endl;
char anar[5000];
while (arr[i] != '\0')
{
if (arr[i] == 'i' || arr[i] == 'a' || arr[i] == 'e' ||
arr[i] == 'o' || arr[i] == 'u')
{
++i;
}
else anar[j] = arr[i]; ++j; ++i;
}++j; anar[j] = '\0';
cout << anar << endl;
ofstream os("test.txt");
os.write(anar, sizeof(char));
cout << "written successfully" << endl;
should read the data from a file and delete the vowels from this string. After deleting vowels, it should assign the result to another string. But vowels seem strange characters and the writen file is only one character long.
How big do you think sizeof(char) is? So how many characters is this going to write?
os.write(anar, sizeof(char));
You actually have j characters in your array, so this works
os.write(anar, j);
But since you have a null terminated character array even simpler would be
os << anar;
Some other errors, look at this loop
while (arr[i] != '\0')
{
if (arr[i] == 'i' || arr[i] == 'a' || arr[i] == 'e' ||
arr[i] == 'o' || arr[i] == 'u')
{
++i;
}
else anar[j] = arr[i]; ++j; ++i;
}++j; anar[j] = '\0';
It looks like you are missing {} around the else part of the if statement. You also have an extra ++j after the while loop for some reason. Here's how it should look (I think)
while (arr[i] != '\0')
{
if (arr[i] == 'i' || arr[i] == 'a' || arr[i] == 'e' ||
arr[i] == 'o' || arr[i] == 'u')
{
++i;
}
else
{
anar[j] = arr[i];
++j;
++i;
}
}
anar[j] = '\0';
Notice how much easier these problems are to spot if you get into the habit of consistently indenting your code. You should do this.
BTW there are no C++ strings in your code, only character arrays.
A very good answer has been given by john already. So, the problem is solved.
I would like to recommend to you to learn a little bit about C++ and all the existing libraries. Especially the C++ - algorithms library is very powerful.
Look at the below program:
#include <iostream>
#include <fstream>
#include <algorithm>
#include <iterator>
int main() {
// Open files and check, if they could be opened
if (std::ifstream is("withvowels.txt"); is)
if (std::ofstream os("withoutvowels.txt"); os)
// Copy file and remove vowels
std::copy_if(std::istreambuf_iterator<char>(is), {}, std::ostreambuf_iterator<char>(os), [](const char c) { return !((0x208222 >> (c & 0x1f)) & 1); });
}
So, in essence, we have just 3 statements: 2 times if with initializer. And then one copy_if with a lambda for vowel detection.
If you want to know more about the lambda and vowel detection you can read in one of my other posts here.
EDIT
Op asked, how to read the file into a std::string. I added a new piece of code, where I first read the complete file into a std::string and then erase/remove the vowels. The result is shown on std::cout
Please see:
#include <iostream>
#include <fstream>
#include <algorithm>
#include <iterator>
#include <string>
int main() {
// Open file and check, if it could be opened
if (std::ifstream is("r:\\withvowels.txt"); is) {
// Read the complete file into string variable s
std::string s(std::istreambuf_iterator<char>(is), {});
// Remove all vowels from string
s.erase(std::remove_if(s.begin(), s.end(), [](const char c) { return ((0x208222 >> (c & 0x1f)) & 1); }), s.end());
// Show result
std::cout << s;
}
}

Extracting int from String on Arduino

I have a buffer of random characters streaming into my Arduino from a XBee module. I want to extract the first integer that it sees (will be <= 3-digit int if that makes a difference), then move on with that number and stop looking at the rest of the incoming characters.
For reference, I'm trying to use this as part of a 2-way handshake with a node.js server that doesn't get messed up when other Arduinos are also attempting to handshake or are already connected and sending data.
I think I have a way that might work to extract an int, but it seems really ugly, so I'm thinking there must be a much shorter/cleaner way to go about this. Here's my very long code to do something that's probably pretty simple:
String intString = "";
int intStart = 0;
for (int i = 0; i < msg.length(); i++) {
while (intStart != 2) {
if (intStart == 0) {
if ((msg[i] == '0') || (msg[i] == '1') || (msg[i] == '2') ||
(msg[i] == '3') || (msg[i] == '4') || (msg[i] == '5') ||
(msg[i] == '6') || (msg[i] == '7') || (msg[i] == '8') ||
(msg[i] == '9')) {
intString += msg[i];
intStart = 1;
}
}
// previous int, next is still int
if (intStart == 1) {
if ((msg[i] == '0') || (msg[i] == '1') || (msg[i] == '2') ||
(msg[i] == '3') || (msg[i] == '4') || (msg[i] == '5') ||
(msg[i] == '6') || (msg[i] == '7') || (msg[i] == '8') ||
(msg[i] == '9')) {
intString += msg[i];
intStart = 1;
}
}
// previous int, next is not int
else if ((msg[i] != '0') && (msg[i] != '1') && (msg[i] != '2') &&
(msg[i] != '3') && (msg[i] != '4') && (msg[i] == '5') &&
(msg[i] != '6') && (msg[i] != '7') && (msg[i] == '8') &&
(msg[i] != '9')) {
intStart = 2;
}
}
}
int number = intString.toInt();
Serial.println(number);
Any suggestions/advice is greatly appreciated.
Rather than compare against every number from 0 to 9, use the standard C function isdigit().
String intString = "";
int intStart = 0;
for (int i = 0; i < msg.length(); i++) {
while (intStart != 2) {
if (intStart == 0) {
if (isdigit(msg[i])){
intString += msg[i];
intStart = 1;
}
}
// previous int, next is still int
if (intStart == 1) {
if (isdigit(msg[i])) {
intString += msg[i];
intStart = 1;
}
}
// previous int, next is not int
else if ( isdigit(msg[i]) ) {
intStart = 2;
}
}
}
"Rubber duck debugging":
Let's assume the first char of the msg is a digit:
set intStart to 0
take the first char of the msg
while intStart is not yet 2
if intStart is 0 (it is, we haven't adjusted it) and the first char of the msg is digit (we assumed it is), then append the first char to intString and make intStart = 1
if intStart == 1 (it is, we set it at the prev step) and the first char of the msg is digit (it is still the first, i didn't change), then append the first char to intString (great, now I have it twice) and set intStart=1 (hey, intStart didn't change). Else... well, we can ignore else, we are in the good conditions for then
so back to the step 3, with the intStart==1 and i still 0 and the first char of the msg still a digit.
Should I continue or are you able to do it?
In essence, with the first char of the msg a digit, you'll never get out from while (intStart != 2) until you run out of heap-space due to intString growing by repeating the same fisrt char all over.
Is that what you want?
Is it so hard to explain this to your rubber duck before asking SO?(yes, I understand, Arduino doesn't have a debugger, but you still can use Serial.print)
[Update on the comments]
Sorry if I was unclear, but it doesn't necessarily start with an integer, the integer could be in the middle of the char buffer.
The first sequence of digits in the char buffer of any length (really doesn't have to be restricted to max 3-digit, only if it makes it easier)
So, before stating to collect, we just need to position ourselves on the first digit of the string buffer
int startScan=0;
// no body for the cycle, everything works just from
// the exit condition and increment
for(
;
startScan < msg.length() && ! isdigit(msg[i]); // as long as it's not a digit
startScan++
);
// from this position, start collecting for as long as we have digits
int intValue=0;
String intString;
for(; startScan < msg.length() && isdigit(msg[startScan]); startScan++) {
intString += msg[startScan]; // take it inside the string
// careful with this one, it may overflow if too many digits
intValue = intValue*10 + (msg[startScan]-'0');
}
// if we reached here with an empty intString, we didn't find any digits
If you don't need the intString, just the intValue, don;t use the intString - at most a bool hasDigits to init to false and set to true in place of intString += msg[startScan]; (to act as a signal for the 'no digits encountered' case).
If you don't need the intValue, just wipe out from the code anithing that uses it.
So, if my understating is correct, you have the following problem:
I have a String message which starts by at most 3 decimal digits and ends possibly with other info I don't need. I want that 'at most 3 digits' prefix transformed in an integer for me to use further
If this is you problem, then try this:
int intValue=0;
String intString;
int maxLookInto=(msg.length() > 3 ? 3 : msg.length()); // at most 3 digits
for(int i=0; i<maxLookInto && isdigit(msg[i]); i++) {
// if we got here, we know msg[i] is still a digit, otherwise
// we get out of cycle ealier
intString += msg[i]; // take it inside the string
intValue = intValue*10 + (msg[i]-'0'); // transforming in base 10 in an int
}
// Do what you like with either intString (textual representation of the
// at-most-3-digits or with the same value converted already to a number
// in intValue
If Arduino doesn't have the isdigit function available, you can implement your own like
int isdigit(char c) {
// we are using ASCII encoding for characters, aren't we?
return (c>='0' && c <='9');
}
One way is to use the String object. This has a toInt method.
BTW there is an Arduino specific stack exchange. arduino.stackexchange.com

Why does the for loop keep stopping at else statement?

#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
main()
{
string str1;
char strArray[80];
cout << "Enter string: ";
getline(cin, str1);
transform(str1.begin(), str1.end(), str1.begin(), ::tolower);
for(int i = 0;i < str1.length();i++)
{
if(str1[i] == ' ' || str1[i] == ',' || str1[i] == '.')
{
}
else
{
strArray[i] = str1[i];
}
}
cout << strArray;
return 0;
}
The for loop keeps stopping after it finds a space, comma, or period. Could someone explain to me why this is happening?
The problem is that i keeps incrementing even though you erased a character from the input. It's not actually stopping, just skipping a character. Since strArray now has a hole in it, it's likely that the hole is filled with 0 thus ending the C-string. P.S. this behavior is not guaranteed and you might end up with completely different results on another run of the program.
When you call erase it affects the container, so you need to handle this. erase() returns the iterator to the next element after the deleted one, so you should use that instead:
int i = 0;
for(string::iterator it = str1.begin(); it != str1.end(); )
{
if(*it == ' ' || *it == ',' || *it == '.')
{
it = str1.erase(it);
}
else
{
strArray[i++] = *it++;
}
}
strArray[i] = '\0'; // terminate string
Can you post which string you input for str1? Cause I try run it and it run well without stop. The only proplem I found with your code is that you erase the char in the loop which will lead to wrong result string.
You are erasing characters from the string while still continuing to increment the counter. Remove ++i from the for loop. Put it under the else clause.
for(int i = 0;i < str1.length();)
{
if(str1[i] == ' ' || str1[i] == ',' || str1[i] == '.')
{
str1.erase(i, 1);
}
else
{
strArray[i] = str1[i];
++i;
}
}
strArray[str1.length()] = '\0';