String Comparison without compare() - c++

I'm currently doing an assignment that requires us to create our own library for string comparison without using compare(), etc.
I got it to work, but during my research I created a bool function for character compare and return values.
It needs to work as if it returns like compare(), where 0 = strings are equal and 0 > or 0 < for not equal instead of true or false like I currently set it up to be.
I tried to change the bool functions to int but now when I run the program that was correctly returning strings are equal, it's showing not equal.
Header code:
bool compare_char(char &c1, char &c2)
{
if (c1 == c2)
return true;
else if (toupper(c1) == toupper(c2))
return true;
else
return false;
}
bool insensitive_string_comparision(string &string_one, string &string_two)
{
return ((string_one.size() == string_two.size()) &&
equal(string_one.begin(), string_one.end(), string_two.begin(), &compare_char));
}
string remove_spaces(string string)
{
string.erase(remove(string.begin(), string.end(), ' '), string.end());
return string;
}
string remove_punctuation(string string)
{
for (size_t i = 0, len = string.size(); i < len; ++i)
{
if (ispunct(string[i]))
{
string.erase(i--, 1);
len = string.size();
}
}
return string;
Int header changes
int compare_char(char &c1, char &c2)
{
if (c1 == c2)
return 0;
else if (toupper(c1) == toupper(c2))
return 0;
else if (toupper(c1) > toupper(c2))
return -1;
else if (toupper(c1) < toupper(c2))
return 1;
}
int insensitive_string_comparision(string &string_one, string &string_two)
{
return ((string_one.size() == string_two.size()) &&
equal(string_one.begin(), string_one.end(), string_two.begin(), &compare_char));
}
Int main changes
int result = insensitive_string_comparision(string_one, string_two);
if (result == 0)
cout << "Both Strings are equal." << endl;
else (result == 1 || result == -1)
cout << "Both Strings are not equal." << endl;
return 0;
I feel like I'm going to have to redesign the entire function to return the value that is similar to compare().
I'm assuming bool was the wrong decision to begin with? Where should I go moving forward to return a correct value?

In your question you are not entirely clear about how you want to compare the strings, but I made some assumptions based on your example code. You can fix your problem by writing insensitive_string_comparision like:
int insensitive_string_comparision(string &string_one, string &string_two) {
int len_one = string_one.length();
int len_two = string_two.length();
int len_comparison = 0;
if (len_one > len_two) {
len_comparison = -1;
} else if (len_one < len_two) {
len_comparison = 1;
}
int minlen = (len_comparison == -1) ? len_one : len_two;
for (int i = 0; i < minlen; i++) {
int order = compare_char(string_one[i], string_two[i]);
if (order != 0) {
return order;
}
}
return len_comparison;
}
I'd also recommend turning on warnings on your compiler. You don't need to put some of your return statements in else blocks.

Related

Check if a string is a number without regex or try catch

I want to implement a simple is_number function that checks if it's an integer, float or an unsigned long int using this method:
bool isNumber(const std::string& str)
{
size_t idx = 0;
//Check if it's an integer
std::stoi(str,&idx);
if (idx == str.size())
return true;
//Check if it's a float
std::stof(str,&idx);
if (idx == str.size() || str[str.size()-1] == 'f' && idx == str.size()) //Cause I do have some float numbers ending with 'f' in the database
return true;
//Check if it's an unsigned long int
std::stoul(str,&idx);
if (idx == str.size())
return true;
return false;
}
But if I test it with a pure string like "test" or "nan", it will throw an error because I'm trying to change a pure string to an integer.
terminate called after throwing an instance of 'std::invalid_argument'
what(): stoi
However if I test it with "0nan" for example, stoi or the others will retrieve the first number and assign the index position of the first found number to the idx variable.
Is it possible to find a workaround for pure strings like "nan" or any other?
Or is there a better method to implement this without regex or try-catch?
std::stoi throws when it fails. Instead of using C i/o you can use C++ streams, try to read from the stream and check if there is something left in the stream:
#include <string>
#include <sstream>
#include <iostream>
enum Number {Float,Signed,Unsigned,NotANumber};
template <typename T>
bool is_only_a(const std::string& str){
std::stringstream ss(str);
T x;
return (ss >> x && ss.rdbuf()->in_avail() ==0);
}
Number isNumber(const std::string& str)
{
size_t idx = 0;
if (is_only_a<unsigned long>(str)) return Unsigned;
else if (is_only_a<int>(str)) return Signed;
else if (is_only_a<float>(str)) return Float;
return NotANumber;
}
int main() {
std::cout << isNumber("1.2") << "\n";
std::cout << isNumber("12") << "\n";
std::cout << isNumber("-12") << "\n";
std::cout << isNumber("asd") << "\n";
std::cout << isNumber("nan") << "\n";
}
Order is important, because 12 could be a float as well.
The link I posted in the comments is most probably what you need.
The only slight modification needed from the answers there is adding a +/- sign, and an optional (at most one) decimal point:
bool isNumber(const std::string &s) {
bool first_char = true;
bool saw_decpt = false;
for (const auto &it: s) {
if (std::isdigit(it)) { first_char = false; }
else if (it == '+' && first_char) { first_char = false; }
else if (it == '-' && first_char) { first_char = false; }
else if (it == '.' && !saw_decpt) { first_char = false; saw_decpt = true; }
else return false;
}
return true;
}

Char pointer objects and respective char array element comparisons

I am programming my custom string class with multiple methods. The issue is that the comparison method does not work as I intend. Instead of doing nothing when the two char arrays differ, an if conditional still proceeds in my main function.
There are no errors given when I compile with g++. The code is syntactically correct, however logically faulty. I know this because I can give the compare method two char arrays which differ in content, and it will not matter whether they differ this way, as the main function will run the if conditional for "s8.compare(s7) == 1" regardless if the result in the compare method is not true.
I will post the entire code below. Any help is greatly appreciated.
string.h
class Str {
private:
char *value;
int length;
int capacity;
//Doubles the size of the string when called.
void growArray();
//If the two strings are uneven, get absolute value of difference in length.
int difference(int a, int b);
//Calculates the size of a character array, passed in as an argument
int getCharArrSize(const char *v);
public:
Str();
explicit Str(const char *STR);
void copy(Str s);
void concatenate(Str s);
bool compare(Str s);
void print();
};
//Str constructor
Str::Str() {
//Assign value, capacity, and length to any new Str object
value = new char[100];
capacity = 100;
length = 0;
}
//Pass STR object as a pointer to string object constructor
Str::Str(const char *STR) {
length = getCharArrSize(STR);
capacity = 100;
value = new char[capacity];
//Copy contents from STR to string object
for (int i = 0; i < length; i++)
value[i] = STR[i];
}
//Doubles the size of the string when called.
void Str::growArray() {
const char *tmp = value;
capacity *= 2;
value = new char[capacity];
for (int i = 0; i < length; i++)
value[i] = tmp[i];
}
//If the two strings are uneven, get absolute value of difference in length.
int Str::difference(int a, int b) {
int d = 0;
if (a > b) d = a - b;
else if (b > a) d = b - a;
return d;
}
//Calculates the size of a character array, passed in as an argument
int Str::getCharArrSize(const char *v) {
int c = 0;
while (v[c] != '\0') {
c++;
}
return c;
}
//Overwrites the data of the string array with the data contained in s
void Str::copy(Str s) {
//Check ability for empty string object to hold Str s contents
if (capacity > s.length) {
//Copy over each element until s length is reached
for (int i = 0; i < s.length ; i++)
value[i] = s.value[i];
//Set string object length to copy's size
length = getCharArrSize(value);
} else { growArray(); }
}
//Concatenate Str s onto string object
void Str::concatenate(Str s) {
//Check ability for string object to hold itself and concatenated chars
if (capacity > length + s.length) {
//Fill string object with s object until end of combined lengths if necessary
for (int i = 0; i < length + s.length; i++)
value[length + i] = s.value[i];
//Set length based on chars in concatenated string object
length = getCharArrSize(value);
} else { growArray(); }
}
//Compare each element in Str s against string for similarities
bool Str::compare(Str s) {
if (length == s.length) {
if (*value == *s.value) {
while ((*value != value[length]) && (*s.value != s.value[s.length])) {
value++;
s.value++;
}
return true;
} else return false;
} else {
difference(length, s.length);
}
}
//Print function
void Str::print() {
std::cout << value << std::endl;
}
main.cpp
#include"string.h"
int main() {
Str s1("Hello ");
Str s2("World");
Str s3(", my ");
Str s4("Name ");
Str s5("is ");
Str s6("Chad!");
Str s7;
s7.copy(s1);
s7.concatenate(s2);
s7.concatenate(s3);
s7.concatenate(s4);
s7.concatenate(s5);
s7.concatenate(s6);
s7.print();
std::cout << "\n\n";
Str s8("Hello World, My Name is Chad!");
if (s8.compare(s7) == 1) {
std::cout << "They Match!" << std::endl;
}
Str s9("I dont match....");
if (s9.compare(s8) == 0) {
std::cout << "I differ by " << s8.compare(s6) << " characters" << std::endl;
}
}
The above code returns a result that appears correct, however changing (s8.compare(s7) == 1) to something like (s8.compare(s5) == 1) returns 'They match!' when I am trying to check each individual element in the char arrays against one another, and only return true if they are the same length and each character matches in the arrays.
Your program has undefined behavior since Str::compare does not have a return statement in one of the branches.
bool Str::compare(Str s) {
if (length == s.length) {
...
} else {
// Missing return statement.
difference(length, s.length);
}
}
Perhaps you want to change that line to:
return (difference(length, s.length) == 0);
Your loop is running without a comparison. You compare the initial values in the char array and then loop through the rest without comparison. So you will return true every time the initial values are equal.
Below the loop runs after the same length is determined then every char is compared. If they are not equal then the function will return false. Otherwise the function will return true.
bool Str::compare(Str s) {
if (length == s.length) {
while ((*value != value[length]) && (*s.value != s.value[s.length])) {
if (*value == *s.value) {
value++;
s.value++;
} else {
return false;//will return false as soon as a comparison is false
}
}
return true;
} else {
difference(length, s.length);
}
}
You also need to return a boolean from the difference function. If you want to return ints from that function switch to a int return on the compare function and use 0 and 1s as their boolean counterparts.

Checking for puncuation

So, the goal is to check to see if the C style string ends with a period or exclamation mark. However, for some reason, i keep getting false.
bool isItSentence(const char* s)
{
int x = strlen(s);
for (int c = 0; s[c] != '\0'; c++)
{
if (!isupper(s[0])) return false;
if (isupper(s[c]) && c > 0) return false;
if (s[x-1] != '.') return false;
if (s[x-1] != '!') return false;
}
return true;
}
int main()
{
std::string str = "Smelly.";
reverse(str.c_str());
std::cout << isItSentence(str.c_str()) << std::endl;
std::cout << strlen(str.c_str()) << std::endl;
system("pause");
Heres what I have so far. But when I add the last if statement to handle exclamation marks, it returns zero. Any suggestions?
First, note s[x-1] is a loop invariant, so you'd rather move it out of the for loop
if (s[x-1] != '.') return false;
if (s[x-1] != '!') return false;
this is always false (a char cannot be both a dot and an explanation mark).
the test should rather be
if (s[x-1] != '.' && s[x-1] != '!') return false;

Would Rewriting It Using Regex Shorten/Beautify The Code?

The problem is a little challenging because I want to code it using std::regex believing it would be easier to read and faster to write.
But it seems that I can only code it one way (shown below).
Somehow my mind could not see the solution using std::regex.
How would you code it?
Would using std::regex_search do the job?
/*
input: data coming in:
/product/country/123456/city/7890/g.json
input: url parameter format:
/product/country/<id1:[0-9]+>/city/<id2:[0-9]+>/g.json
output:
std::vector<std::string> urlParams
sample output:
urlParams[0] = "123456"
urlParams[1] = "7890"
*/
bool ParseIt(const char *path, const char* urlRoute, std::vector<std::string> *urlParams)
{
const DWORD BUFSZ = 2000;
char buf[BUFSZ];
DWORD dwSize = strlen(urlRoute);
urlParams.clear();
int j = 0;
int i = 0;
bool good = false;
for (i = 0; i < dwSize; i++)
{
char c1 = path[j++];
char c2 = urlRoute[i];
if (c2 == '<')
{
good = true;
while (c2 != '/')
{
i++;
c2 = urlRoute[i];
}
int k = 0;
memset(buf, 0, BUFSZ);
while (c1 != '/')
{
buf[k++] = c1;
c1 = path[j++];
}
urlParams->push_back(_strdup(buf));
int b = 1;
}
if (c1 != c2)
{
return false;
}
if (c2 != '<')
{
if (c1 == c1)
{
}
else
{
return false;
}
}
}
if (dwSize == i && good)
{
return true;
}
return false;
}
The easiest one I've found (might not be the best but should work with your input data) is
std::string subject("/product/country/123456/city/7890/g.json");
std::regex re("/(\d+)/city/(\d+)/");
std::smatch match;
std::regex_search(subject, match, re);
It matches two values per line. The / matches for the slash at the beginning/end and the () does the capture. You will have to convert it from the string type though.

Checking if argv[i] is a valid integer, passing arguments in main

I'm trying to make sure all arguments passed to main are valid integers, and if not, I'll print an error. For example, if I have an executable named total, I would enter total 1 2 3 4.
I want to print an error if there's an invalid integer, so if I enter total 1 2 3zy it will print an error message. My code is as follows.
#include <iostream>
#include<stdlib.h>
using namespace std;
bool legal_int(char *str);
int main(int argc, char *argv[])
{
//int total = 0;
for(int i = 1; i < argc; i++)
{
if( (legal_int(argv[i]) == true) )
{
cout << "Good to go" << endl;
}
else
{
cerr << "Error: illegal integer." << endl;
return 1;
}
}
// int value = atoi(argv[i]);
//cout << value << endl;
}
bool legal_int(char *str)
{
while(str != 0) // need to
if( (isdigit(str)) )// do something here
{
return true;
}
else
{
return false;
}
}
What I need to know is how can I index through all the characters in the string and make sure they are digits with the legal_int function?
When comparing every character, the logic should be if it's not legal, return false, otherwise continue:
bool legal_int(char *str)
{
while (str != 0)
{
if (!isdigit(*str))
{
return false;
}
str++;
}
return true;
}
What about:
bool legal_int(char *str) {
while (*str)
if (!isdigit(*str++))
return false;
return true;
}
It is not the best function but it should serve the purpose. The isdigit function needs a character to look at so pass in *str. The other key point is that you need to advance the pointer inside of the loop.
bool legal_int(char *str)
{
while(str != 0) // need to
if( (isdigit(str)) )// do something here
{
return true;
}
else
{
return false;
}
}
You have three mistakes:
while (str != 0) should be while (*str != 0). You want to continue until you encounter a zero in the string, not until the string itself goes away.
if( (isdigit(str)) ) should be if( (isdigit(*str++)) ). You want to look at what str points to and see if that's a digit, and you need to point to the next digit.
return true; That should not be there. You don't want to return just because you found a single digit.