Comparing strings in this instances work differently, any reason? - c++

Can someone help me with this, please? The block of code in the green box works fine, but the one in red works but not really. It's checking if the emails have both "#gmail.com" and "#yahoo.com". I want it to check both emails so that if they both contain only one "#gmail.com" or "#yahoo.com", it will exit the loop. Thank you!
#include <iostream>
using namespace std;
int main()
{
string emailOne_, emailTwo_;
bool valid = true;
do
{
cout << "Email: ";
cin >> emailOne_;
cout << "Re-enter email: ";
cin >> emailTwo_;
if (emailOne_.compare(emailTwo_) != 0)
{
valid = false;
cerr << "\t[*] ERROR: EMAILS DO NOT MATCH\n\n";
}
else
{
valid = true;
}
string myArray[] = { "#gmail.com", "#yahoo.com" };
int arrSize = sizeof(myArray) / sizeof(myArray[0]);
for (int i = 0; i < arrSize; i++)
{
auto found = emailOne_.find(myArray[i]);
if (found == string::npos)
{
valid = false;
cerr << "\t[*] ERROR: EMAILS MUST HAVE #gmail.com or #yahoo.com\n\n";
break;
}
else
{
valid = true;
}
}
} while (valid == false);
return 0;
}

You have some issues in the code
auto found = emailOne_.find(myArray[i]); will find the #gmail.com even if the entered email address is foo#gmail.com.uk, which is probably not what you want.
If the first entry in myArray doesn't match, you break out and don't test the next.
If the first entry is a match, you don't break out, so you go on and try the next string in myArray which will of course not match - and then you break out.
It's therefore currently impossible to get a valid match. By breaking out only when a valid match is found, you should be able to get the correct result.
Example with some suggested simplifications:
#include <iostream>
#include <string>
int main() {
const std::string myArray[] = {"#gmail.com", "#yahoo.com"};
std::string emailOne_;
for(;;) {
std::string emailTwo_;
std::cout << "Email: ";
std::cin >> emailOne_;
std::cout << "Re-enter email: ";
std::cin >> emailTwo_;
// simplify comparisons:
if(emailOne_ != emailTwo_) {
std::cerr << "\t[*] ERROR: EMAILS DO NOT MATCH\n\n";
continue; // back to entering email
}
bool valid = false;
// Use range-based for loops to simplify looping over arrays:
for(const std::string& end : myArray) {
// check if the end of emailOne_ is equal to `end` by comparing
// a substring of emailOne_ with the same length as end, with end:
if(emailOne_.size() > end.size() && // must be long enough
emailOne_.substr(emailOne_.size() - end.size()) == end)
{
valid = true;
break;
}
}
// check the valid state after the loop:
if(!valid) {
std::cerr << "\t[*] ERROR: EMAILS MUST HAVE #gmail.com or #yahoo.com\n";
} else {
break; // a valid email address was found.
}
}
std::cout << emailOne_ << " is a valid address\n";
}

Related

Check all occurrences of a letter appear before another letter C++

I have a string S consisting of N letters 'a' or 'b'. This should return true when all occurrences of 'a' are before all occurrences of 'b' and return false otherwise.
b does not need to occur in S and a does not need to occur in S
For example
S='aabbb' returns true
S = 'ba' returns false
S = 'aaa' returns true
S= 'b' returns true
S='abba' returns false
this is my solution but it displays 1 extra true. I don't know why.
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
vector <string> vec;
int n;
string item;
int contora=0,contorb=0;
cout<<"n: ";
cin>>n;
cout << "Enter a string: ";
for(int i=0;i<= n;i++){
getline(cin, item);
//cin>>item;
vec.push_back(item);
}
for (int j=0; j <= n; j++) {
cout << vec[j];
}
cout<<endl;
for (auto of:vec) {
if (of.find("ba") != std::string::npos)
cout << "false";
else
cout<<"true";
}
return 0;
}
Wording it differently, you're trying to check if there are any characters in the string that come after a. If the answer is no, your function will return true, and false otherwise.
bool HasCorrectOrder(char prefix_char, char suffix_char, std::string_view str) {
if (str.empty()) {
return true;
}
// Run through all the preceeding characters.
int index = 0;
while (str[index] == prefix_char && index < str.size()) {
++index;
}
// At this point, if we've reached the end of the string, that means there are
// no characters after the preceeding character. So we can return true.
if (index == str.size()) {
return true;
}
// As there are more characters left, they should all be equal to the
// suffix_char in order for the ordering to be correct.
while (str[index] == suffix_char && index < str.size()) {
++index;
}
return (index == str.size());
}
Running a quick check:
void RunTest() {
std::vector<std::string> test_strings = {"aabbb", "ba", "aaa", "b", "abba"};
for (std::string_view str : test_strings) {
std::cout << "S = " << str << " returns "
<< (HasCorrectOrder(/*prefix_char=*/'a',
/*suffix_char=*/'b', str) ? "true" : "false")
<< std::endl;
}
}
returns:
S = aabbb returns true
S = ba returns false
S = aaa returns true
S = b returns true
S = abba returns false
Adding some debug output to the code shows a couple of issues:
The first for loop reads in n+1 strings, while we are expecting the user to enter only n strings. The second for loop also prints n+1 strings, although that's not a problem, because the vector contains n+1 elements.
Due to mixing both std::cin >> and std::getline for reading user input, the first string read with std::getline is empty. You would need whether to:
use std::getline for all the user input readings (and, for n, convert the string to a number), or
flush the input buffer with cin.ignore after the std::cin >>, as explained in this answer.
The code below fixes those two issues and adds some debug output (it also follows a few best practices such as, not using namespace std;, defining variables next to their first use, value-initializing local variables, and using block braces even for one-line blocks).
[Demo]
#include <iostream> // cin, cout
#include <limits>
#include <string> // getline
#include <vector>
int main() {
int n{};
std::cout << "n: ";
std::cin >> n;
std::cout << n << "\n\n";
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::vector<std::string> vec{};
for (int i = 0; i < n; i++) {
std::cout << "Enter a string: ";
std::string item{};
std::getline(std::cin, item);
std::cout << "item: " << i << ": '" << item << "'\n";
vec.push_back(item);
}
std::cout << "\n";
for (auto& of : vec) {
std::cout << "'" << of << "': ";
if (of.find("ba") != std::string::npos) {
std::cout << "false\n";
} else {
std::cout << "true\n";
}
}
}
// Outputs:
//
// n: 5
//
// Enter a string: item: 0: 'aabbb'
// Enter a string: item: 1: 'ba'
// Enter a string: item: 2: 'aaa'
// Enter a string: item: 3: 'b'
// Enter a string: item: 4: 'abba'
//
// 'aabbb': true
// 'ba': false
// 'aaa': true
// 'b': true
// 'abba': false
For this particular case, you could also use std::is_partitioned. This algorithm tells you if all the elements of a range that satisfy a given predicate appear before all the elements that don't (see here). The predicate would check if a character is equal to 'a'.
[Demo]
#include <algorithm> // is_partitioned
#include <cstring> // strlen
#include <fmt/core.h>
int main() {
for (auto&& s : { "aabbb", "ba", "aaa", "b", "abba" }) {
fmt::print("'{}': {}\n", s, std::is_partitioned(s, s + strlen(s),
[](unsigned char c) { return c == 'a'; }));
}
}
After fixing the loop logic, here is another simple solution you could use, with the is_sorted algorithm:
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
int main() {
// ...
for (auto of : vec)
std::cout << std::boolalpha << std::ranges::is_sorted(of);
}
This compiles with C++20 support, and it works because strings are ranges of characters, for which an operator< is defined. It happens to be the case that 'a' < 'b', so this simple expression is all you need.
You just need to find "ba" occurrences.
C++ string type already provides such function. Complexity is linear on string length O(n)
#include <iostream>
#include <string>
void checker(const std::string S)
{
if (S.find("ba") != std::string::npos) {
std::cout << "false" << '\n';
} else {
std::cout << "true" << '\n';
}
}

First day at C++

I need to ask for the user's Initials and have it return their input. If they input any int I need it to loop back and ask them again.
int main()
{
// Program 1-1
std::cout<<"Hello, can you please enter your initials?\n";
char initials[50];
std::cin.getline(initials, 50);
while (std::cin != int)
{
std::cout << "Thank you. " << initials << std::endl;
}
else (std::cin.fail())
{
std::cin.clear();
}
}
#include <cctype>
#include <iostream>
#include <string>
int main() {
std::string tmp;
bool bogusInput;
do {
std::cout << "Initials: ";
std::getline(std::cin, tmp);
bogusInput = false;
for (auto i : tmp) {
if (std::isdigit(i)) {
bogusInput = true;
break;
}
}
if (bogusInput) {
std::cout << "Are you Elon's son? Please enter letters only.\n";
}
} while (bogusInput);
std::cout << "Thank you, " << tmp << ".\n";
return 0;
}
This code does the job you describe. I chose a do/while loop since I always need to get initials at least one time.
The variable bogusInput is a flag that I use to know if something hasn't worked out properly. It is declared outside of the loop for the sake of the while condition. It is always reset before performing the check, this allows the case of multiple attempts to work.
Finally, "valid" input gets us out of the loop and we say thank you and end.

Modifying the Create Account Program

I am a student currently learning C++ and I need some help. I am currently working on a textbook exercise from the book Murach's C++ Programming. I am working on Exercise 7-2 (second exercise in chapter 7). The instructions for the program are here: Exercise 7-2 instructions I've managed to make sense of most of it, but I am currently stuck on step 9. I know how to call functions, but when I run the program, it only allows me to enter my full name. After I do that, the program ends without letting me enter my password or email. And yes I have added a return value to my variables as needed. How can I make the program let me enter my full name, password and email like it's supposed to? Nothing that I've tried seems to work. I've tried returning a value of 0, I've tried making a local variable and then adding the return value to said variable, but none of that worked. Please help me understand what I'm supposed to do as I'm still new to C++ and have a lot to learn. By the way, I'm using Microsoft Visual Studios as my IDE.
Here's the code that I have so far for my main.cpp file:
#include <iostream>
#include <string>
#include "validation.h"
using namespace std;
int main()
{
cout << "Create Account\n\n";
// get full name and parse first name
string full_name;
string first_name;
bool valid_name = false;
while (!valid_name) {
cout << "Enter full name: ";
getline(cin, full_name);
// strip whitespace from front
int i = full_name.find_first_not_of(" \n\t");
if (i > -1) {
full_name = full_name.substr(i);
}
// get first name
int space_index = full_name.find(' ');
if (space_index == -1) {
cout << "You must enter your full name. Please try again.\n";
}
else {
first_name = full_name.substr(0, space_index);
valid_name = true;
}
}
cout << endl;
bool validation::is_valid_password(string password);
bool validation::is_valid_email(string email);
// make sure first name uses initial cap
char letter = first_name[0];
first_name[0] = toupper(letter);
for (int i = 1; i < first_name.length(); ++i) {
letter = first_name[i];
first_name[i] = tolower(letter);
}
// display welcome message
cout << "Hi " << first_name << ",\n"
<< "Thanks for creating an account!\n\n";
}
And the code I have so far with the validation.cpp implementation file:
#include "validation.h"
#include <string>
#include <iostream>
using namespace std;
using namespace validation;
bool validation::is_valid_password(string password) {
bool valid_password = false;
while (!valid_password) {
valid_password = true;
cout << "Enter password: ";
getline(cin, password);
if (password.length() < 8) {
cout << "Password must be at least 8 characters.\n";
valid_password = false;
}
int index = password.find_first_of("0123456789");
if (index == -1) {
cout << "Password must include a number.\n";
valid_password = false;
}
bool special_character = false;
for (char c : password) {
if (ispunct(c)) {
special_character = true;
break;
}
}
if (!special_character) {
cout << "Password must include a special character.\n";
valid_password = false;
}
if (!valid_password) {
cout << "Please try again.\n";
}
else {
password = password.substr(0, index);
valid_password = true;
}
}
cout << endl;
return false;
}
bool validation::is_valid_email(string email) {
bool valid_email = false;
while (!valid_email) {
valid_email = true;
cout << "Enter email: ";
getline(cin, email);
int at_index = email.find('#');
if (at_index == -1) {
cout << "The email must include an at character (#).\n";
valid_email = false;
}
int dot_index = email.rfind('.');
if (dot_index == -1) {
cout << "The email must include a dot operator (.).\n";
valid_email = false;
}
bool valid_chars = true;
for (char c : email) {
if (c != '#' && c != '.' && c != '_' && c != '-') {
if (!isalnum(c)) {
valid_chars = false;
break;
}
}
}
if (at_index == 0) {
cout << "The local part of the email must include at least one character.\n";
valid_email = false;
}
if (dot_index - at_index == 1) {
cout << "The server name of the email must include at least one character.\n";
valid_email = false;
}
if (email.length() - dot_index - 1 != 3 && email.length() - dot_index - 1 != 2) {
cout << "The domain name of the email must have two or three characters.\n";
valid_email = false;
}
if (!valid_email) {
cout << "Please try again.\n";
}
else {
email = email.substr(0, at_index);
email = email.substr(0, dot_index);
valid_email = true;
}
}
cout << endl;
return false;
}
And the code I have for the validation header file:
#ifndef T_FRYE_VALIDATION_H
#define T_FRYE_VALIDATION_H
#endif // !T_FRYE_VALIDATION_H
#include <string>
using namespace std;
namespace validation {
bool is_valid_password(string password);
bool is_valid_email(string email);
}
I know that this is a lot to read, and I'm really sorry about that, but I'm at a loss as to what to do next. If anyone can help me, I'd really appreciate it.
The calls to your validation functions are malformed. You attempt to call the code
validation::isvalid_password(); from main but what you provide instead is:
bool validation::is_valid_password(string password);
bool validation::is_valid_email(string email);
which are declarations. You need to actually call the code you want.
string password;
string email;
validation::isvalid_password(password);
validation::is_valid_email(email);
What do you think these two lines do inside the main() function?
int main()
{
// ...
bool validation::is_valid_password(string password);
bool validation::is_valid_email(string email);
// ...
}
That is not a function call, that is just a declaration of the function. You should have something like:
int main()
{
// ...
std::string password;
while (!validation::is_valid_password(password));
std::string email;
while (!validation::is_valid_email(email));
// ...
}
There are many other problems in your code anyway... For example, if you are reading something inside the function is_valid_xxx (which is a bad design by itself because is_-type of functions shouldn't do anything except checking the values), you should pass the string by reference:
bool validation::is_valid_password(string &password);
Even if you don't plan to get the new value, passing something by value is a bad idea in this type of functions. Pass the value by reference to const:
bool validation::is_valid_password(const string &password);

operator _surrogate_func: no matching overload found and 2 other errors

I've written a code which creates a list of buses that you can modify and manage. The whole managing process is proceed by writing strings in console. After running code I'm receiving 3 errors, none of which I understand therefore can fix. Code is planned this way:
NEW_BUS - Adds new bus in list by taking it's number, amount of stops and stop list.
ALL_BUSES - Display all buses in lexicographical order (by their name)
STOPS_FOR_BUS - Display all stops specific bus follows.
BUSES_FOR_STOP - Diplay all buses that go though the specific stop.
Here are error list:
1. operator _surrogate_func: no matching overload found
2. Failed to specialize function template 'unknown-type std::less::operator ()(_Ty 1 &&,_Ty2 &&)
3. illegal expression
All errors come from line 617 from xutility file.
#include <iostream>
#include <string>
#include <vector>
#include <list>
#include <tuple>
#include <iterator>
using namespace std;
class Bus {
public:
int StopsAmount;
string BusNumber;
vector<string> Stops;
Bus(tuple<string, int, vector<string>> BusParams) {
BusNumber = get<0>(BusParams);
StopsAmount = get<1>(BusParams);
Stops = get<2>(BusParams);
}
void ShowStops() {
cout << BusNumber << ": ";
for (int i = 0; i < StopsAmount; i++) cout << Stops[i] << " ";
cout << "\n";
}
bool FindStop(string Stop) {
for (int i = 0; i < StopsAmount; i++) {
if (Stops[i] == Stop) {
return true;
}
}
return false;
}
};
class BusTraffic {
public:
BusTraffic() {
while (true) {
string Request;
cin >> Request;
switch (Request[0]) {
case 'N': NEW_BUS(Request.substr(8, Request.length() - 8)) ;
break;
case 'B': BUSES_FOR_STOP(Request.substr(15, Request.length() - 15));
break;
case 'S': STOPS_FOR_BUS(Request.substr(14, Request.length() - 14));
break;
case 'A': ALL_BUSES();
break;
}
}
}
private:
list<Bus> BusList;
void NEW_BUS(string Request) {
BusList.push_back(Bus::Bus(SplitString(Request)));
}
void BUSES_FOR_STOP(string Stop) {
cout << Stop << ": ";
for (list<Bus>::iterator It = BusList.begin(); It != BusList.end(); It++) {
if (It->FindStop(Stop)) {
cout << It->BusNumber << " ";
}
}
cout << endl;
}
void STOPS_FOR_BUS(string Name) {
cout << Name << ": ";
for (list<Bus>::iterator It = BusList.begin(); It != BusList.end(); It++) {
if (It->BusNumber == Name) {
It->ShowStops();
}
}
}
void ALL_BUSES() {
if (BusList.size() > 0) {
BusList.sort();
for (list<Bus>::iterator It = BusList.begin(); It != BusList.end(); It++) {
cout << It->BusNumber << ": ";
It->ShowStops();
}
}
else {
cout << "No buses" << endl;
}
}
// Converting string to information about bus
tuple<string, int, vector<string>> &SplitString(string str) {
tuple<string, int, vector<string>> BusParams;
string Word = "";
int WordNum = 0;
for (auto Letter : str) {
if (Letter == ' ') {
if (WordNum == 0) get<0>(BusParams) = Word;
if (WordNum == 1) get<1>(BusParams) = stoi(Word);
if (WordNum == 2) get<2>(BusParams).push_back(Word);
Word = "";
WordNum++;
}
else {
Word = Word + Letter;
}
}
get<2>(BusParams).push_back(Word);
return BusParams;
}
};
int main() {
BusTraffic TestTraffic;
return 0;
}
Unfortunately, you are absolutely correct. the error message here is just terrible. It's just from experience that you learn to interpret that as a missing less-than-operator.
So something in the lines of:
bool operator< (const Bus& lhs, const Bus& rhs) {
// however you want to sort them...
return (lhs.BusNumber < rhs.BusNumber);
}
The other small bug is within NEW_BUS: In C++ you don't need to specify the constructor name. So it's not Bus::Bus it's just Bus
Last but not least your SplitString is returning a reference to a local variable. In general that's a bad idea as that memory might simply not be accessible anymore when you try to. Just remove "&" from the return type. Further explanations.
You're trying to sort a list of buses. By the sounds of it there is no overload for the less than operator. This means C++ doesn't know how to compare objects of type bus. Add this to your Bus class and it should work. This overload of the less than operator returns true if the other bus is less than the current bus.
bool operator < (const Bus& otherBus) const {
if(otherBus.StopsAmount < this.StopsAmount) {
return true;
}
return false;
}
Useful links with more details:
http://fusharblog.com/3-ways-to-define-comparison-functions-in-cpp/
https://www.tutorialspoint.com/cplusplus/relational_operators_overloading.htm

Validating user input is valid number or not [invalid conversion from 'char' to 'char*']

Ok so I am a beginner in c/c++ and I am creating this little program that checks if the input provided by user is valid number or not, if it is then it prints " it is a number" or else it prints "it is a character string"
Some example output
1 - is a number
-1.1 - is a number
1......1 - is a character string
three - is a character string
.12 is a character string
+0.12 is a number
ABC123ABC - is a character string
I'm getting this error in my code. If someone could help me fix this I would really appreciate it. TIA
cpp:52:23: error: invalid conversion from 'char' to 'char*' [-fpermissive]
if (!isNum(c[i]))
{
~~~^
task1.cpp:5:19: note: initializing argument 1 of 'bool isNum(char*)'
bool isNum(char * p){
My code
#include <iostream>
bool isNum(char * p){
if (NULL == p || *p == '\0'){
return false;
}
int dot = 0;
int plus = 0;
int minus = 0;
while(*p){
char a = *p;
switch (a)
{
//Only allows 1 dot
case '.':
if (++dot > 1){
return false;
}
break;
//only allows 1 plus sign
case '+':
if (++plus > 1){
return false;
}
//only allows 1 minus sign
case '-':
if (++minus > 1){
return false;
}
//Only allows 0-9
default:
if (a < '0' || a > '9'){
return false;
}
}
p++;
}
return true;
}
int main(){
//char array of size 1024
char c[1024];
std::cout << "Enter something: ";
std::cin >> c;
for(int i = 0; i < sizeof(c); i++){
if (!isNum(c[i])){
std::cout << c << " is a character string";
}
else {
std::cout << c << " is a number";
}
}
}
If you want to practice complicated algorithms, parsing numbers is a good exercise. But if your goal is to write useful, simple programs, you are on the wrong track. In C++, many common tasks are already solved by the C++ standard library, you just have to use them.
#include <iostream>
#include <sstream>
#include <string>
int main() {
std::string line;
if (!std::getline(std::cin, line)) {
std::cerr << "error reading the line\n";
return 1;
}
std::istringstream in{line};
double num;
if (in >> num && in.peek() == EOF) {
std::cout << "it's a number, " << num << "\n";
} else {
std::cout << "it's not a number\n";
}
}
The above code reads more high-level than your code. Most importantly it can handle arbitrary long lines without crashing the program.
I'm not intimately familiar with the C++ headers, so I may have forgotten to include some others. But the rest of the code should be ok, even though I didn't test it.
The following function isNumber would work for you.
Here I use a dynamic character sequence std::string which enables us to input any size strings less shorter than std::string::max_size.
We can check whether a given character is a digit or not by std::isdigit.
No extra copies and object creation would show good performance.
Whitespace characters are not allowed in the left and right side of the input string.
I also write the explicit type of the iterators and avoid using auto because you are tagging C++98:
#include <string>
#include <cctype>
bool isNumber(const std::string& s)
{
// this also validates the following access to s[0]
if(s.empty()){
return false;
}
const std::size_t offset = (s[0] == '+' || s[0] == '-') ? 1 : 0;
std::string::const_iterator begin = s.begin() + offset;
// this also validates the following dereferencing begin
if(begin == s.end()){
return false; // false if just a sign "+" or "-"
}
if(!std::isdigit(static_cast<unsigned char>(*begin))){
return false; // e.g. "+.123"
}
bool isdecimal = false;
for(std::string::const_iterator it = ++begin; it != s.end(); ++it)
{
if (!std::isdigit(static_cast<unsigned char>(*it)))
{
if(!isdecimal && (*it == '.'))
{
isdecimal = true;
if((it+1) == s.end()){
return false; // e.g. "+1."
}
}
else{
return false;
}
}
}
return true;
}
Now it is easy and straightforward to implement the main function:
DEMO
#include <iostream>
int main()
{
std::string s;
std::cout << "Enter something: ";
std::getline(std::cin, s);
std::cout << std::endl;
std::cout
<< s << " is a "
<< (isNumber(s) ? "number." : "character string.");
return 0;
}
There you go, i've commented the things i had changed
#include <iostream>
bool isNum(char * p) {
if (NULL == p || *p == '\0') {
return false;
}
int dot = 0;
char a = *p;
if (a<'0' || a>'9') {
if (a != '-' && a != '+') { return false; }
else p++;
}
if (*p<'0' || *p>'9') return false;
p++;
while (*p != '\0') {
a = *p;
switch (a)
{
//Only allows 1 dot
case '.':
if (++dot > 1) {
return false;
}
p++;
if (*p == '\0') return false;
break;
default:
if (a < '0' || a > '9') {
return false;
}
p++;
break;
}
}
return true;
}
int main() {
//char array of size 1024
char c[1024];
std::cout << "Enter something: ";
std::cin >> c;
// you don't need to loop through every character just pass your array of characters & your function is looping through it
if (!isNum(c)) {
std::cout << c << " is a character string";
}
else {
std::cout << c << " is a number";
}
}