Arduino String comparison not working - c++

I tried to compare a EEPROM stored SSID that returns a string to the WiFi.SSID() function.
Although it is literally the same in the Serial Monitor I don't get a match and it never reaches the if statement.
I tried using == operator without the .str() and used if( strcmp ( a.c_str(),b.c_str)==1)
Nothing seems to work. What am I missing here?
void ConnectToBestWifi()
{
int apnos = WiFi.scanNetworks();
int loc[3];
int no=0;
for(int i=0;i<apnos;i++)
{
for(int j=0;j<3;j++)
{
Serial.println("");
Serial.println("Wifi SSID");
Serial.println(WiFi.SSID(i).c_str());
Serial.println("");
Serial.println("Read SSID");
Serial.println(ReadWifiSSID(j).c_str());
Serial.println("");
if (strcmp(((WiFi.SSID(i)).c_str()),((ReadWifiSSID(j)).c_str()))==1)
{
Serial.println("gotcha");
loc [no]=i;
no++;
}
}
Image of Serial Monitor attached below

Read the man page, once again.
Also, quoting C11, chapter §7.24.4.2 , (emphasis mine)
int strcmp(const char *s1, const char *s2);
The strcmp function returns an integer greater than, equal to, or less than zero,
accordingly as the string pointed to by s1 is greater than, equal to, or less than the string pointed to by s2.
strcmp() returns a 0 when both the strings match. So,
if (strcmp(((WiFi.SSID(i)).c_str()),((ReadWifiSSID(j)).c_str()))==1)
should better be
if (strcmp (((WiFi.SSID(i)).c_str()),((ReadWifiSSID(j)).c_str())) == 0)

As already said, you are using strcmp wrong.
But you don't need to use that.
You can compare String objects directly with ==.
if (WiFi.SSID(i) == ReadWifiSSID(j))

Related

Comparing chars stored in 2d arrays c++/c [duplicate]

I am trying to get a program to let a user enter a word or character, store it, and then print it until the user types it again, exiting the program. My code looks like this:
#include <stdio.h>
int main()
{
char input[40];
char check[40];
int i=0;
printf("Hello!\nPlease enter a word or character:\n");
gets(input); /* obsolete function: do not use!! */
printf("I will now repeat this until you type it back to me.\n");
while (check != input)
{
printf("%s\n", input);
gets(check); /* obsolete function: do not use!! */
}
printf("Good bye!");
return 0;
}
The problem is that I keep getting the printing of the input string, even when the input by the user (check) matches the original (input). Am I comparing the two incorrectly?
You can't (usefully) compare strings using != or ==, you need to use strcmp:
while (strcmp(check,input) != 0)
The reason for this is because != and == will only compare the base addresses of those strings. Not the contents of the strings themselves.
Ok a few things: gets is unsafe and should be replaced with fgets(input, sizeof(input), stdin) so that you don't get a buffer overflow.
Next, to compare strings, you must use strcmp, where a return value of 0 indicates that the two strings match. Using the equality operators (ie. !=) compares the address of the two strings, as opposed to the individual chars inside them.
And also note that, while in this example it won't cause a problem, fgets stores the newline character, '\n' in the buffers also; gets() does not. If you compared the user input from fgets() to a string literal such as "abc" it would never match (unless the buffer was too small so that the '\n' wouldn't fit in it).
Use strcmp.
This is in string.h library, and is very popular. strcmp return 0 if the strings are equal. See this for an better explanation of what strcmp returns.
Basically, you have to do:
while (strcmp(check,input) != 0)
or
while (!strcmp(check,input))
or
while (strcmp(check,input))
You can check this, a tutorial on strcmp.
You can't compare arrays directly like this
array1==array2
You should compare them char-by-char; for this you can use a function and return a boolean (True:1, False:0) value. Then you can use it in the test condition of the while loop.
Try this:
#include <stdio.h>
int checker(char input[],char check[]);
int main()
{
char input[40];
char check[40];
int i=0;
printf("Hello!\nPlease enter a word or character:\n");
scanf("%s",input);
printf("I will now repeat this until you type it back to me.\n");
scanf("%s",check);
while (!checker(input,check))
{
printf("%s\n", input);
scanf("%s",check);
}
printf("Good bye!");
return 0;
}
int checker(char input[],char check[])
{
int i,result=1;
for(i=0; input[i]!='\0' || check[i]!='\0'; i++) {
if(input[i] != check[i]) {
result=0;
break;
}
}
return result;
}
Welcome to the concept of the pointer. Generations of beginning programmers have found the concept elusive, but if you wish to grow into a competent programmer, you must eventually master this concept — and moreover, you are already asking the right question. That's good.
Is it clear to you what an address is? See this diagram:
---------- ----------
| 0x4000 | | 0x4004 |
| 1 | | 7 |
---------- ----------
In the diagram, the integer 1 is stored in memory at address 0x4000. Why at an address? Because memory is large and can store many integers, just as a city is large and can house many families. Each integer is stored at a memory location, as each family resides in a house. Each memory location is identified by an address, as each house is identified by an address.
The two boxes in the diagram represent two distinct memory locations. You can think of them as if they were houses. The integer 1 resides in the memory location at address 0x4000 (think, "4000 Elm St."). The integer 7 resides in the memory location at address 0x4004 (think, "4004 Elm St.").
You thought that your program was comparing the 1 to the 7, but it wasn't. It was comparing the 0x4000 to the 0x4004. So what happens when you have this situation?
---------- ----------
| 0x4000 | | 0x4004 |
| 1 | | 1 |
---------- ----------
The two integers are the same but the addresses differ. Your program compares the addresses.
Whenever you are trying to compare the strings, compare them with respect to each character. For this you can use built in string function called strcmp(input1,input2); and you should use the header file called #include<string.h>
Try this code:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
char s[]="STACKOVERFLOW";
char s1[200];
printf("Enter the string to be checked\n");//enter the input string
scanf("%s",s1);
if(strcmp(s,s1)==0)//compare both the strings
{
printf("Both the Strings match\n");
}
else
{
printf("Entered String does not match\n");
}
system("pause");
}
You need to use strcmp() and you need to #include <string.h>
The != and == operators only compare the base addresses of those strings. Not the contents of the strings
while (strcmp(check, input))
Example code:
#include <stdio.h>
#include <string.h>
int main()
{
char input[40];
char check[40] = "end\n"; //dont forget to check for \n
while ( strcmp(check, input) ) //strcmp returns 0 if equal
{
printf("Please enter a name: \n");
fgets(input, sizeof(input), stdin);
printf("My name is: %s\n", input);
}
printf("Good bye!");
return 0;
}
Note1: gets() is unsafe. Use fgets() instead
Note2: When using fgets() you need to check for '\n' new line charecter too
You can:
Use strcmp() from string.h, which is the easier version
Or if you want to roll your own, you can use something like this:
int strcmp(char *s1, char *s2)
{
int i;
while(s1[i] != '\0' && s2[i] != '\0')
{
if(s1[i] != s2[i])
{
return 1;
}
i++;
}
return 0;
}
I'd use strcmp() in a way like this:
while(strcmp(check, input))
{
// code here
}
How do I properly compare strings?
char input[40];
char check[40];
strcpy(input, "Hello"); // input assigned somehow
strcpy(check, "Hello"); // check assigned somehow
// insufficient
while (check != input)
// good
while (strcmp(check, input) != 0)
// or
while (strcmp(check, input))
Let us dig deeper to see why check != input is not sufficient.
In C, string is a standard library specification.
A string is a contiguous sequence of characters terminated by and including the first null character.
C11 §7.1.1 1
input above is not a string. input is array 40 of char.
The contents of input can become a string.
In most cases, when an array is used in an expression, it is converted to the address of its 1st element.
The below converts check and input to their respective addresses of the first element, then those addresses are compared.
check != input // Compare addresses, not the contents of what addresses reference
To compare strings, we need to use those addresses and then look at the data they point to.
strcmp() does the job. §7.23.4.2
int strcmp(const char *s1, const char *s2);
The strcmp function compares the string pointed to by s1 to the string pointed to by s2.
The strcmp function returns an integer greater than, equal to, or less than zero,
accordingly as the string pointed to by s1 is greater than, equal to, or less than the string pointed to by s2.
Not only can code find if the strings are of the same data, but which one is greater/less when they differ.
The below is true when the string differ.
strcmp(check, input) != 0
For insight, see Creating my own strcmp() function
#include<stdio.h>
#include<string.h>
int main()
{
char s1[50],s2[50];
printf("Enter the character of strings: ");
gets(s1);
printf("\nEnter different character of string to repeat: \n");
while(strcmp(s1,s2))
{
printf("%s\n",s1);
gets(s2);
}
return 0;
}
This is very simple solution in which you will get your output as you want.

What is the best way of clearing a char array in C/C++?

I've been searching for ways of emptying a char array in C/C++. I have come up with this code:
char testName[20];
for(int i = 0; i < sizeof(testName); ++i)
{
testName[i] = (char)0;
}
It has been working for a while now but when I try to strlenthe result is always two more than the typed in word. For instance I input the word dog the output would be five. Why is that so? Is my char array not cleared?
Here is my code:
char testName[20];
void loop()
{
if(Serial.available())
{
Serial.println("Waiting for name...");
index = 0;
for(int i = 0; i < sizeof(testName); ++i)
{
testName[i] = (char)0;
}
while(Serial.available())
{
char character = Serial.read();
testName[index] = character;
index++;
}
Serial.print("Name received: ");
Serial.println(testName);
Serial.print("The sentence entered is ");
Serial.print(strlen(testName));
Serial.println(" long");
delay(1000);
}
delay(1000);
}
Screenshot of the output:
Output as text:
Name received: dog
The sentence entered is 5 characters long
Don't use C style arrays in modern C++. When you require a fixed size array, use std::array instead. From a memory storage point of view, they are identical.
You can then clear the array with: myarray.fill('\0')
If your definition of "emptying char array" is set all elements of an array to zero, you can use std::memset.
This will allow you to write this instead of your clear loop:
const size_t arraySize = 20; // Avoid magic numbers!
char testName[arraySize];
memset(&(testName[0]), 0, arraySize);
As for "strange" results of strlen():
strlen(str) returns "(...) the number of characters in a character array whose first element is pointed to by str up to and not including the first null character". That means, it will count characters until it finds zero.
Check content of strings you pass to strlen() - you may have white characters there (like \r or \n, for example), that were read from the input stream.
Also, an advice - consider using std::string instead of plain char arrays.
Small note: memset() is often optimized for high performance. If this in not your requirement, you can also use std::fill which is a more C++ - like way to fill array of anything:
char testName[arraySize];
std::fill(std::begin(testName), std::end(testName), '\0');
std::begin() (and std::end) works well with arrays of compile-time size.
Also, to answer #SergeyA's comment, I suggest to read this SO post and this answer.
Yet another quick way of filling an array with zeroes, in initialization only:
char testName[20] = {};
same as
char testName[20] = {0};
Since it is a C99 feature, compiler could complain with the following
warning: ISO C forbids empty initializer braces
More here.
Anyway, looking at the OP code, there's no need at all of initializing/filling the array, it could be better like this:
#define MAX_LENGTH 20
char testName[MAX_LENGTH];
void loop()
{
if(Serial.available())
{
Serial.println("Waiting for name...");
index = 0;
while(Serial.available())
{
if(index < MAX_LENGTH)
testName[index++] = Serial.read();
}
if(index < MAX_LENGTH)
{
testName[index] = 0;
Serial.print("Name received: ");
Serial.println(testName);
Serial.print("The sentence entered is ");
Serial.print(strlen(testName));
Serial.println(" long");
}
else
Serial.print("Name too long ...");
delay(1000);
}
delay(1000);
}
For posterity. The OP example is an Arduino sketch. Assuming here that the input is taken through the built-in Serial Monitor of the Arduino IDE. At the bottom of the Serial Monitor window is a pop-up menu where the line ending can be selected as:
No line ending
newline
Carriage return
Both NL & CR
Whatever option is selected in this menu is appended to whatever is entered in the box at the top of the Serial Monitor when the Send button is clicked or the return key is hit. So the extra two characters are most certainly the result of having the "Both NL & CR" menu option selected.
Picking the "No line ending" option will solve the problem.
FWIW, in this application, the array does not need to be "cleared". Just append a '\0' char to the last char received to make it a C-string and all will be good.

Can someone explain this code line by line?

This method
bool isNumber(string input)
{
char* p;
strtod(input.c_str(), &p);
return *p == 0;
}
should input a string and convert it to a double. But I do not understand the process of it. Can someone explain this to me in detail, line by line? And also, shouldn't bool be changed to double since it's not returning a true or false value? Thanks.
strtod tries to convert the string to a double. It also sets the p parameter to point to the position where the conversion ended.
If the conversion used all the characters in the string - if they were all part of a number - the pointer p will point to the '\0' terminator of the string.
So, return *p == 0, or better return *p == '\0', tells us if we reached the end of the string. And, of course, == returns a bool result.

How to convert string (char*) to number with error checking using standard library functions?

I can think of 2 ways to convert a string to int: strtol and std::stringstream. The former doesn't report errors (if string is not a representation of a number), the latter throws an exception BUT it is too relaxed. An example:
std::wstring wstr("-123a45");
int result = 0;
try { ss >> result; }
catch (std::exception&)
{
// error handling
}
I want to detect an error here because the whole string is not convertible to int, but no exception is being thrown and result is set to -123.
How can I solve my task using standard C++ facilities?
You erroneously believe that strtol() does not provide error checking, but that is not true. The second parameter to strtol() can be used to detect if the entire string was consumed.
char *endptr;
int result = strtol("-123a45", &endptr, 10);
if (*endptr != '\0') {
/*...input is not a decimal number */
}
There's std::stoi, or std::strtol.
The first throws an exception (and is in C++11 and later), the other you have to manually check (as it's originally a standard C function).
And you can indeed use std::strtol to check that a string is a valid number:
char some_string[] = "...";
char *endptr;
long value = std::strtol(some_string, &endptr, 10);
if (endptr == some_string)
{
// Not a valid number at all
}
else if (*endptr != '\0')
{
// String begins with a valid number, but also contains something else after the number
}
else
{
// String is a number
}
An alternative approach, you could convert to an int, and then convert that back into a wstring, and check the strings for equality.
Not a good idea for doubles, and you would probably need to trim the input string of whitespace even for ints.

Basics of strtol?

I am really confused. I have to be missing something rather simple but nothing I am reading about strtol() is making sense. Can someone spell it out for me in a really basic way, as well as give an example for how I might get something like the following to work?
string input = getUserInput;
int numberinput = strtol(input,?,?);
The first argument is the string. It has to be passed in as a C string, so if you have a std::string use .c_str() first.
The second argument is optional, and specifies a char * to store a pointer to the character after the end of the number. This is useful when converting a string containing several integers, but if you don't need it, just set this argument to NULL.
The third argument is the radix (base) to convert. strtol can do anything from binary (base 2) to base 36. If you want strtol to pick the base automatically based on prefix, pass in 0.
So, the simplest usage would be
long l = strtol(input.c_str(), NULL, 0);
If you know you are getting decimal numbers:
long l = strtol(input.c_str(), NULL, 10);
strtol returns 0 if there are no convertible characters at the start of the string. If you want to check if strtol succeeded, use the middle argument:
const char *s = input.c_str();
char *t;
long l = strtol(s, &t, 10);
if(s == t) {
/* strtol failed */
}
If you're using C++11, use stol instead:
long l = stol(input);
Alternately, you can just use a stringstream, which has the advantage of being able to read many items with ease just like cin:
stringstream ss(input);
long l;
ss >> l;
Suppose you're given a string char const * str. Now convert it like this:
#include <cstdlib>
#include <cerrno>
char * e;
errno = 0;
long n = std::strtol(str, &e, 0);
The last argument 0 determines the number base you want to apply; 0 means "auto-detect". Other sensible values are 8, 10 or 16.
Next you need to inspect the end pointer e. This points to the character after the consumed input. Thus if all input was consumed, it points to the null-terminator.
if (*e != '\0') { /* error, die */ }
It's also possible to allow for partial input consumption using e, but that's the sort of stuff that you'll understand when you actually need it.
Lastly, you should check for errors, which can essentially only be overflow errors if the input doesn't fit into the destination type:
if (errno != 0) { /* error, die */ }
In C++, it might be preferable to use std::stol, though you don't get to pick the number base in this case:
#include <string>
try { long n = std::stol(str); }
catch (std::invalid_argument const & e) { /* error */ }
catch (std::out_of_range const & e) { /* error */ }
Quote from C++ reference:
long int strtol ( const char * str, char ** endptr, int base );
Convert string to long integer
Parses the C string str interpreting its content as an integral number of the specified base, which is returned as a long int value. If endptr is not a null pointer, the function also sets the value of endptr to point to the first character after the number.
So try something like
long l = strtol(pointerToStartOfString, NULL, 0)
I always use simply strol(str,0,0) - it returns long value. 0 for radix (last parameter) means to auto-detect it from input string, so both 0x10 as hex and 10 as decimal could be used in input string.