I'm working on a project in my data stucts book and for some reason even though it's nearly verbatim from what's written I'm still getting a runtime error that I can't identify.
Here is my code:
int main()
{
cout << "Enter an expression, to check if its balanced.\n";
string exp;
while (getline(cin, exp) && (exp != ""))
{
cout << exp;
if(is_balanced(exp))
{
cout << " is balanced.";
}
else
{
cout << " is not balanced.";
}
cout << "Enter another expression: \n";
}
return 0;
}
bool is_balanced(const string& expression) // pass reference to the input expression
{
//create stack to hold parantheses
stack<char> s;
bool balanced = true; //to hold return value
string::const_iterator iter;
expression.begin(); // sets a read-only iterator at the beginning of the expression
while (balanced && (iter != expression.end())) //while 'balanced' and not at end of expression cont. looping
{
char nx_ch = *iter;
if (is_open(nx_ch))
{
s.push(nx_ch);
}
else if (is_closed(nx_ch))
{
if (s.empty())
{
balanced = false;
}
else
{
char tp_ch = s.top(); // if the stack isn't closed set the char as tp for comparisson
s.pop(); // remove top char
balanced = OPEN.find(tp_ch) == CLOSE.find(nx_ch);
}
}
++iter;
}
if(!s.empty())
{
balanced = false;
return balanced && s.empty();
}
else
{
return balanced && s.empty();
}
}
The error occurs at this line: if(is_balanced(exp))
In the main and reads:
Debug Assertion Failed! ... Expression: string iterators incompatible
Everything I've read about the error says it happens when you compare to iterators, but that doesn't make sense if I can't even get it through the constructor. Any help to better understand this would be wonderful. Thanks in advance.
string::const_iterator iter; does not initialise the iterator.
Then you are reading its value in iter != expression.end().
The behaviour on doing that is undefined.
Did you mean string::const_iterator iter = expression.begin();?
This isn't how you set a variable:
string::const_iterator iter;
expression.begin(); // sets a read-only iterator at the beginning of the expression
This is how you set a variable:
string::const_iterator iter = expression.begin(); // sets a read-only iterator at the beginning of the expression
Related
I am trying to write a code that has two functions: one that determines whether the string is an isogram or not and another one to print the outcome (true or false) to the console (for the purpose of solving the task).
Some of the things are not working correctly though. And I wonder where I need to improve the code (probably all over...). I would appreciate any advice :)
#include <iostream>
#include<string>
#include<bits/stdc++.h>
#include<iomanip>
bool find_Isogram (std::string str)
{
std::sort(str.begin(), str.end()); //sorted the string for the for loop (e.g. eHllo)
int length = str.length();
for (int i = 0; i < length; i++)
{
if (str.at(i) == str.at(i+1))
{
return false;
break;
}
else
{
return true;
}
}
}
void print_result()
{
std::string str;
if (!find_Isogram (str))
{
std::cout << "false" << std::endl;
}
else
{
std::cout << "true" << std::endl;
}
}
int main()
{
find_Isogram ("gdtub");
print_result();
return 0;
};
````````````````````````````````````````````````````
There are some problems here:
1) You always check an empty string:
print_result will just check an empty string, but it's redundant anyway.
void print_result()
{
std::string str; // empty string
if (!find_Isogram (str)) // finding isogram on empty string
{
std::cout << "false" << std::endl;
}
...
}
It can be simplified with std::boolalpha that allows you to print a bool as "true" or "false" (instead of 1 or 0). main would become
int main()
{
std::cout << std::boolalpha << find_Isogram ("gdtub"); // prints true or false
};
2) Isogram check always ends after first character
Take a look at the condition in find_Isogram. It has a return-statement in the if and else, so you always return after checking the first character.
The idea to detect duplicate characters this way is correct (except for the off-by-one-error already mentioned by others). But you want to return true; only after checking all of the characters, e.g. outside the loop:
bool find_Isogram (std::string str)
{
std::sort(str.begin(), str.end()); //sorted the string for the for loop (e.g. eHllo)
int length = str.length();
for (int i = 0; i < length - 1; i++)
{
if (str.at(i) == str.at(i+1))
{
return false; // whoops duplicate char, stop here
}
}
return true; // no duplicates found, it's an isogram
}
For some further C++-magic, you could simplify it even more with standard library functions :D
bool find_Isogram (std::string str)
{
std::sort(str.begin(), str.end());
return std::unique(str.begin(), str.end()) == str.end();
}
The condition where you check the consecutive characters for equality is wrong. It will yield true for strings like ABAB. You instead need to use a map with count of each character that has appeared.
Something like:
std::map<char, int> map_of_chars;
for(int i = 0; i < length; i++) {
map_of_chars[str.at(i)] = map_of_chars[str.at(i)] + 1;
}
If any value in the map is more than 1 return false;
Another implementation would be using the return value of std::unique():
std::sort(str.begin(), str.end());
auto intial_size = str.size();
std::unique(str.begin(), str.end());
if(str.size() == initial_size) {
/is an isogram
}
else {
//is not an isogram
}
for the program I'm writing I need to find the location of a specific word inside a string array, but I don't know how. This is the code I made but it doesn't work:
int location;
string input;
cout << "Type your name" << endl;
cin >> input;
for (int i = 0; i <= count; i++)
{
call_DB[i].name;
if (call_DB[i].name == input)
{
location = i;
}
}
What is wrong with that code and what can I do to fix it? Thank you.
Try std::find_if, which searches the array for item satisfying provided predict function.
auto iter = std::find_if(call_DB, call_DB + count, [&input](const YOUR_CALL_DB_TYPE& item ){ return item.name == input; });
if(iter == call_DB + count)
printf("Not found\n");
else
printf("Found: %s\n", iter->name.c_str());
If such item is found, the index is the distance from array base, size_t index = iter - call_DB;
You can get an iterator to the array element using std::find_if:
auto it = std::find_if(std::begin(db), std::end(db), [&name](auto const& x) {
return x.name == name;
});
And if you want the index into the array:
auto index = it - std::begin(db);
Below is a palindrome function and a helper function that implements a reverse operation. For some reason, even though the types appear to be the same, the if comparision statement never evaluates to true. Both variables s and comp seem to be strings, and I even tried calling stoi() on both to turn them into ints but comp for some reason throws an error:
terminate called after throwing an instance of 'std::invalid_argument'
what(): stoi
Aborted (core dumped)
I'm not sure what the problem is.
bool palindrome (int n)
{
string s = to_string(n);
cout << s << endl;
string comp = rev(s);
cout << comp << endl;
if (s == comp)
{
cout << s << " " << comp << endl;
return 1;
}
return 0;
}
string rev (string &s)
{
string return_string("");
for (string::iterator it = s.end(); it != (s.begin() - 1); it--)
{
return_string += *it;
}
return return_string;
}
Your rev function has two errors. First, it computes (s.begin() - 1). Second, on its first iteration it dereferences s.end(). Both are undefined behavior.
You'll have fewer problems using reverse iterators:
string rev( const string& s )
{
string return_string;
for ( auto it = s.rbegin(); it != s.rend(); ++it )
{
return_string += *it;
}
return return_string;
}
for (string::iterator it = s.end(); it != (s.begin() - 1); it--) is certainly wrong. In the first iteration, you'll be dereferencing s.end(), and then one before s.begin() . This is undefined behavior (even s.begin() - 1 is).
Use reverse iterators:
for(auto it = s.rbegin(); it != s.rend(); ++it)
for (string::iterator it = s.end(); it != (s.begin() - 1); it--)
{
return_string += *it;
}
end() points one beyond the end of the container, so when you dereference it you are accessing memory you don't own.
Also, trying to access more memory you don't own by decrementing begin() to an address before the start of your container.
Try using a reverse iterator, reverse end and reverse begin, like so:
string rev(const string &s)
{
string return_string("");
for (string::const_reverse_iterator rit = s.rbegin(); rit != s.rend(); ++rit)
{
return_string += *rit;
}
return (return_string);
}
I am currently trying to count the number of words in a file. After this, I plan to make it count the words between two words in the file. For example. My file may contain. "Hello my name is James". I want to count the words, so 5. And then I would like to count the number of words between "Hello" and "James", so the answer would be 3. I am having trouble with accomplishing both tasks.
Mainly due to not being exactly sure how to structure my code.
Any help on here would be greatly appreciated. The code I am currently using is using spaces to count the words.
Here is my code:
readwords.cpp
string ReadWords::getNextWord()
{
bool pWord = false;
char c;
while((c = wordfile.get()) !=EOF)
{
if (!(isspace(c)))
{
nextword.append(1, c);
}
return nextword;
}
}
bool ReadWords::isNextWord()
{
if(!wordfile.eof())
{
return true;
}
else
{
return false;
}
}
main.cpp
main()
{
int count = 0;
ReadWords rw("hamlet.txt");
while(rw.isNextWord()){
rw.getNextWord();
count++;
}
cout << count;
rw.close();
}
What it does at the moment is counts the number of characters. I'm sure its just a simple fix and something silly that I'm missing. But I've been trying for long enough to go searching for some help.
Any help is greatly appreciated. :)
Rather than parse the file character-by-character, you can simply use istream::operator<<() to read whitespace-separated words. << returns the stream, which evaluates to true as a bool when the stream can still be read from.
vector<string> words;
string word;
while (wordfile >> word)
words.push_back(word);
There is a common formulation of this using the <iterator> and <algorithm> utilities, which is more verbose, but can be composed with other iterator algorithms:
istream_iterator<string> input(wordfile), end;
copy(input, end, back_inserter(words));
Then you have the number of words and can do with them whatever you like:
words.size()
If you want to find "Hello" and "James", use find() from the <algorithm> header to get iterators to their positions:
// Find "Hello" anywhere in 'words'.
const auto hello = find(words.begin(), words.end(), "Hello");
// Find "James" anywhere after 'hello' in 'words'.
const auto james = find(hello, words.end(), "James");
If they’re not in the vector, find() will return words.end(); ignoring error checking for the purpose of illustration, you can count the number of words between them by taking their difference, adjusting for the inclusion of "Hello" in the range:
const auto count = james - (hello + 1);
You can use operator-() here because std::vector::iterator is a “random-access iterator”. More generally, you could use std::distance() from <iterator>:
const auto count = distance(hello, james) - 1;
Which has the advantage of being more descriptive of what you’re actually doing. Also, for future reference, this kind of code:
bool f() {
if (x) {
return true;
} else {
return false;
}
}
Can be simplified to just:
bool f() {
return x;
}
Since x is already being converted to bool for the if.
To count:
std::ifstream infile("hamlet.txt");
std::size_t count = 0;
for (std::string word; infile >> word; ++count) { }
To count only between start and stop:
std::ifstream infile("hamlet.txt");
std::size_t count = 0;
bool active = false;
for (std::string word; infile >> word; )
{
if (!active && word == "Hello") { active = true; }
if (!active) continue;
if (word == "James") break;
++count;
}
I think "return nextword;" should instead be "else return nextword;" or else you are returning from the function getNextWord every time, no matter what the char is.
string ReadWords::getNextWord()
{
bool pWord = false;
char c;
while((c = wordfile.get()) !=EOF)
{
if (!(isspace(c)))
{
nextword.append(1, c);
}
else return nextword;//only returns on a space
}
}
To count all words:
std::ifstream f("hamlet.txt");
std::cout << std::distance (std::istream_iterator<std::string>(f),
std::istream_iterator<std::string>()) << '\n';
To count between two words:
std::ifstream f("hamlet.txt");
std::istream_iterator<std::string> it(f), end;
int count = 0;
while (std::find(it, end, "Hello") != end)
while (++it != end && *it != "James")
++count;
std::cout << count;
Try this:
below the line
nextword.append(1, c);
add
continue;
I have to implement a vector using an array in C++ that is used to count the number of unique words from the input. It reads the input and then adds to the words to a struct which contains its count and the unique word and then this is added to the vector. I have successfully implemented insert. The problem is that I can't get the inserting/ incrementing unique word count to work (elements aren't added to the vector). Here is my code:
#include <stdio.h>
#include <iostream>
#include <unistd.h>
#include "MyVector.h"
using namespace std;
struct wordCount{
string val;
int count;
};
int main(int argc, char** argv) {
enum { total, unique,individual } mode = total;
for (int c; (c = getopt(argc, argv, "tui")) != EOF;) {
switch(c) {
case 't': mode = total; break;
case 'u': mode = unique; break;
case 'i': mode = individual; break;
}
}
argc += optind;
argv += optind;
string word;
Vector<wordCount> words;
Vector<wordCount>::iterator it;
int count = 0;
while (cin >> word) {
count++;
if(mode == unique || mode == individual){
for(it=words.begin();it != words.end();it++){
if((it-1)->val <= word && it->val >= word){
// Found word, increment its count
if(it->val == word){
it->count++;
break;
}
// Otherwise insert the new unique word
else{
cout << "adding unique word" << endl;
wordCount* wc;
wc = new wordCount;
wc->val = word;
wc->count = 1;
words.insert(it,*wc);
break;
}
}
}
}
}
switch (mode) {
case total: cout << "Total: " << count << endl; break;
case unique: cout << "Unique: " << words.size() << endl; break;
case individual:
for(it=words.begin();it!=words.end();it++){
cout << it->val << ": " << it->count << endl;}
break;
}
}
It's hard to say anything without seeing your implementation of
Vector. If we assume it adheres to the standard container
conventions (and doesn't have an error in trying to do so): you
iterate starting with it.begin(), but immediately access
it-1. That's undefined behavior for a standard container. (I
don't know what it will do with your implementation ofVector`,
but it would take some tricky code to make it work.)
At a higher level, there seems a basic inconsistency: you're
keeping the vector sorted, but still using linear search. If
you're using linear search, there's no point in keeping the
vector sorted; just use:
Vector<wordCount>::iterator it = words.begin();
while ( it != words.end() && *it != word ) {
++ it;
}
if ( it == words.end() ) {
// not found, append to end...
} else {
// found, do whatever is appropriate...
}
(although I'd probably append to end, recover the iterator to
the newly inserted element, and treat it as if it were found).
Alternatively, if you're keeping the vector sorted, use a binary
search, not a linear search.
In either case, put the search in a separate function. (If this
wasn't homework, I'd say just use std::vector and either
std::find_if or std::lower_bound.)
Also, why the new in the innermost else? A more reasonable
approach would be to provide a constructor for wordCount
(which sets the count to 0), and do something like:
if ( ! found ) {
it = words.insert( wordCount( word ) );
}
++ it->count;
The definition of found will depend on whether you're using
binary search or not. In terms of the standard, this would be
either:
Vector<wordCount>::iterator it
= std::find_if( words.begin(), words.end(), MatchWord( word );
if ( it == words.end() ) {
it = words.insert( words.end(), wordCount( word ) );
}
++ it-count;
or
Vector<wordCount>::iterator it
= std::lower_bound( words.begin(), words.end(), word, CompareWord() );
if ( it == words.end() || it->val != word ) {
it = words.insert( wordCount( word ) );
++ it->count;
You should probably strive for something similar, with
a separate lookup function, returning either end, or the
position for the insertion when the value isn't found.
This keeps the various concerns clearly separated, and avoids
the excessive nesting in your code. (You should probably try to
avoid break in general, and in multiply nested ifs, it is
completely inacceptable—you'll notice that one of the
other people answering missed them, and misunderstood the
control flow because of it.)
Well, why don't you use a map? That's exactly what it's for, mapping from one thing to another. From a string (the word) to an int (the number of occurences) in your case. Or do you have to use a vector?
Try to use a std::map.
Counter::Map words;
Counter count(words);
std::for_each(
std::istream_iterator<std::string>(myInStream /*std::cin*/),
std::istream_iterator<std::string>(),
count);
std::copy(
words.begin(),
words.end(),
std::ostream_iterator<Counter::Map::value_type>(myOutStream /*std::cout*/, "\n"));
The Counter functor could look like this
struct Counter
{
typedef std::map<std::string, size_t> Map;
Counter(Map& m) : words(&m) {}
void operator()(const std::string& word)
{
Map::iterator it = words->lower_bound(word);
if (it == words->end() || it->first != word)
words->insert(it, std::make_pair(word, 1));
else
++it->second;
}
Map* words;
};
Using a std::vector
struct CounterVector
{
typedef std::vector<std::pair<std::string, size_t> > Vector;
CounterVector(Vector& m) : words(&m) {}
struct WordEqual
{
const std::string* s;
WordEqual(const std::string& w) : s(&w) {}
bool operator()(Vector::const_reference p) const {
return *s == p.first;}
};
void operator()(const std::string& word)
{
Vector::iterator it = std::find_if(
words->begin(), words->end(), WordEqual(word));
if (it == words->end())
words->push_back(std::make_pair(word,1));
else
++it->second;
}
Vector* words;
};