String with the same content but comparison always returns false - string

I have the following code, in which using the WiFi library I perform a scan of the available WiFi networks and want to detect if a specific network is available. I am using ESP32 and Arduino IDE. EEPROM memory handling seems to work, but I don't understand why the comparison always returns zero.
SSID = EEPROM.readString(500); // I read from eeprom the string stored in pos 500
WiFi.mode(WIFI_STA);
delay(100);
Serial.println(logg + "SCAN!");
int n = WiFi.scanNetworks();
Serial.println(logg + "SE DETECTARON: " + String(n) + " REDES WIFI!");
for (int i = 0; i < n; ++i) {
// Print SSID and RSSI for each network found
Serial.println("'" + WiFi.SSID(i) + "' vs '" + SSID + "' SizeOf: " + String(WiFi.SSID(i).length()) + " - " + String(SSID.length()) + " Comparacion " + String(WiFi.SSID(i) == SSID );
}
delay(10);
What I get on the serial monitor is the following:
'WRT1900AC 2.4GHz' vs 'WRT1900AC 2.4GHz' SizeOf: 16 - 16 Comparacion 0
The two strings look the same, they are the same size. I already tried replacing comparator "==" with strcmp and I get -244.
I also tried using .c_str as follows:
WiFi.SSID(i).c_str() == SSID.c_str()
but with the same results. If someone comes up with something I would be very grateful.

The two strings look the same, they are the same size.
Although the WiFI.SSID() return a String object, however it does not necessary to be ASCII-encoded. The string encoding is depend on the locale setting of the router, and the reason it looks the same is because the Serial.print() cast it into ASCII. This can be proof by the following sketch by using both Serial.print() and Serial.printf() in ESP32 which shown what is actually received (Serial.printf() however does not support Unicode formatting in ESP32 implementation, so it will produce some garbage characters).
int n = WiFi.scanNetworks();
for (int i=0; i<n; i++) {
// Serial.print() will cast the WiFi.SSID() to ASCII
Serial.print(WiFi.SSID(i));
// this shown what WiFi.SSID() truly return
Serial.printf(" --- %s\n", WiFi.SSID(i));
}
This will produce results in show in this picture. As you can see some SSIDs produce the correct results but some shown up as garbage.
So String comparison operator does do the job correctly when you compare WiFi.SSID(i) == SSID and the result indeed is not necessary equal for some SSID, even though it "looks" the same to human.
So how to solve it? If you want to treat them "as the same", ironically, converting String object to char array with .c_str() does do the job because it convert each char to an ASCII. I guess you just didn't use the char array comparison strcmp() correctly.
if(strcmp(WiFi.SSID(i).c_str(), SSID.c_str()) == 0) {
// match
}
else {
// not match
}
If you are saying that this c.str() comparison return -244, then edit your question and do a Serial.printf() on both String or better off to loop through the String character by charter and print out the HEX code to see what's going on.

Related

C++ How to parse math problem in string using TinyExpr?

First I need to detect if message from player contains math problem
like I get 2 messages from him
char* message1 = "Please solve this math problem";
char* message2 = "How much is 1 + 2 ?";
These messages are not static, player can say different messages.
I need to process message1 and message2
I need to detect math problem and cut it like see message2 and I need it to be "1+2"
char* mathProblemFromMessage = "1+2"; // <- I need to get this from message also detect if message contains any math problem before
double answer = te_interp(mathProblemFromMessage, 0);
printf("Answer is %f\n", answer);
my current code
std::string msg = Message;
if (func.contains_math_operators(msg.c_str()) && func.contains_number(msg.c_str()))
{
double answer = te_interp(Message, 0);
}
I think it fails and always results 0 because msg is "Player: 1+1" I need to cut math problem from it, I dont know how to do it...a
Find a position of the first digit in that string and get a substring from that point:
std::size_t found = msg.find_first_of("0123456789");
if(found != std::string::npos)
{
std::string sub = msg.substr(found);
double answer = te_interp(sub, 0);
}
If that also fails, try to trim invalid tail.
You may want to extend that find_first_of above to include open paren (, unary minus - or some supported function names (I am not familiar with TinyExpr)

Groovy script overwriting first byte?

Hi so this string I'm creating and appending in groovy is somehow corrupting the first byte and I have no idea why this happening. Its the second string creation. In this script I'm making a query and the first one works but the second initialization somehow messes up the first byte in the string and I have to do a substring of an extra index (it's two because I'm initializing a comma). Any insight would be very appreciated!!
Note: I'm using mulesoft runtime 3.8.5 in Anypoint studio 6.4.4. Not sure if this is the reason but it is a candidate in my mind...
flowVars.queryIds = "Id IN ("
for (Integer i = 0; i < payload.size(); i++) {
flowVars.queryIds += "\'" + payload[i].Id + "\',"
}
flowVars.queryIds = flowVars.queryIds.substring(0,flowVars.queryIds.size() - 1) + ")"
//Assigning comma because a random byte is getting inserted and this makes that error explicit & deterministic
flowVars.queryFields = ",";
for (String key : payload[0].keySet()) {
flowVars.queryFields += key + ",";
}
//Skipping over custom field isMatch
flowVars.queryFields = flowVars.queryFields.substring(2, flowVars.queryFields.size() - 9);
return payload
I can't reproduce your problem but since you use groovy, you can write your code a bit shorter:
flowVars.queryIds = "Id IN ("
flowVars.queryIds += payload.collect{"'${it.Id}'"}.join(", ")
flowVars.queryIds += ")"
flowVars.queryFields = payload[0].keySet().join(", ");
this should produce the same output in a more understandable way
So I found out the reason this issue was happening is actually because the csv file I am parsing is corrupted (I thought it was mulesoft and was mistaken). This blog does a greater job explaining the issue than I could. Thanks for your review on the groovy code though Rdmueller! Is definitely a lot cleaner with your suggestions. https://medium.freecodecamp.org/a-quick-tale-about-feff-the-invisible-character-cd25cd4630e7

Counter for two binary strings C++

I am trying to count two binary numbers from string. The maximum number of counting digits have to be 253. Short numbers works, but when I add there some longer numbers, the output is wrong. The example of bad result is "10100101010000111111" with "000011010110000101100010010011101010001101011100000000111000000000001000100101101111101000111001000101011010010111000110".
#include <iostream>
#include <stdlib.h>
using namespace std;
bool isBinary(string b1,string b2);
int main()
{
string b1,b2;
long binary1,binary2;
int i = 0, remainder = 0, sum[254];
cout<<"Get two binary numbers:"<<endl;
cin>>b1>>b2;
binary1=atol(b1.c_str());
binary2=atol(b2.c_str());
if(isBinary(b1,b2)==true){
while (binary1 != 0 || binary2 != 0){
sum[i++] =(binary1 % 10 + binary2 % 10 + remainder) % 2;
remainder =(binary1 % 10 + binary2 % 10 + remainder) / 2;
binary1 = binary1 / 10;
binary2 = binary2 / 10;
}
if (remainder != 0){
sum[i++] = remainder;
}
--i;
cout<<"Result: ";
while (i >= 0){
cout<<sum[i--];
}
cout<<endl;
}else cout<<"Wrong input"<<endl;
return 0;
}
bool isBinary(string b1,string b2){
bool rozhodnuti1,rozhodnuti2;
for (int i = 0; i < b1.length();i++) {
if (b1[i]!='0' && b1[i]!='1') {
rozhodnuti1=false;
break;
}else rozhodnuti1=true;
}
for (int k = 0; k < b2.length();k++) {
if (b2[k]!='0' && b2[k]!='1') {
rozhodnuti2=false;
break;
}else rozhodnuti2=true;
}
if(rozhodnuti1==false || rozhodnuti2==false){ return false;}
else{ return true;}
}
One of the problems might be here: sum[i++]
This expression, as it is, first returns the value of i and then increases it by one.
Did you do it on purporse?
Change it to ++i.
It'd help if you could also post the "bad" output, so that we can try to move backward through the code starting from it.
EDIT 2015-11-7_17:10
Just to be sure everything was correct, I've added a cout to check what binary1 and binary2 contain after you assing them the result of the atol function: they contain the integer numbers 547284487 and 18333230, which obviously dont represent the correct binary-to-integer transposition of the two 01 strings you presented in your post.
Probably they somehow exceed the capacity of atol.
Also, the result of your "math" operations bring to an even stranger result, which is 6011111101, which obviously doesnt make any sense.
What do you mean, exactly, when you say you want to count these two numbers? Maybe you want to make a sum? I guess that's it.
But then, again, what you got there is two signed integer numbers and not two binaries, which means those %10 and %2 operations are (probably) misused.
EDIT 2015-11-07_17:20
I've tried to use your program with small binary strings and it actually works; with small binary strings.
It's a fact(?), at this point, that atol cant handle numerical strings that long.
My suggestion: use char arrays instead of strings and replace 0 and 1 characters with numerical values (if (bin1[i]){bin1[i]=1;}else{bin1[i]=0}) with which you'll be able to perform all the math operations you want (you've already written a working sum function, after all).
Once done with the math, you can just convert the char array back to actual characters for 0 and 1 and cout it on the screen.
EDIT 2015-11-07_17:30
Tested atol on my own: it correctly converts only strings that are up to 10 characters long.
Anything beyond the 10th character makes the function go crazy.

Char Array Returning Integers

I've been working through this exercise, and my output is not what I expect.
(Check substrings) You can check whether a string is a substring of another string
by using the indexOf method in the String class. Write your own method for
this function. Write a program that prompts the user to enter two strings, and
checks whether the first string is a substring of the second.
** My code compromises with the problem's specifications in two ways: it can only display matching substrings to 3 letters, and it cannot work on string literals with less than 4 letters. I mistakenly began writing the program without using the suggested method, indexOf. My program's objective (although it shouldn't entirely deviate from the assignment's objective) is to design a program that determines whether two strings share at least three consecutive letters.
The program's primary error is that it generates numbers instead of char characters. I've run through several, unsuccessful ideas to discover what the logical error is. I first tried to idenfity whether the char characters (which, from my understanding, are underwritten in unicode) were converted to integers, considering that the outputted numbers are also three letters long. Without consulting a reference, I know this isn't true. A comparison between java and javac outputted permutation of 312, and a comparison between abab and ababbab ouputted combinations of 219. j should be > b. My next thought was that the ouputs were indexes of the arrays I used. Once again, this isn't true. A comparison between java and javac would ouput 0, if my reasoning were true.
public class Substring {
public static char [] array;
public static char [] array2;
public static void main (String[]args){
java.util.Scanner input = new java.util.Scanner (System.in);
System.out.println("Enter your two strings here, the longer one preceding the shorter one");
String container1 = input.next();
String container2 = input.next();
char [] placeholder = container1.toCharArray();
char [] placeholder2 = container2.toCharArray();
array = placeholder;
array2 = placeholder2;
for (int i = 0; i < placeholder2.length; i++){
for (int j = 0; j < placeholder.length; j ++){
if (array[j] == array2[i]) matcher(j,i);
}
}
}
public static void matcher(int higher, int lower){
if ((higher < array.length - 2) && (lower < array2.length - 2))
if (( array[higher+1] == array2[lower+1]) && (array[higher+2] == array2[lower+2]))
System.out.println(array[higher] + array[higher+1] + array[higher+2] );
}
}
The + operator promotes shorts, chars, and bytes operands to ints, so
array[higher] + array[higher+1] + array[higher+2]
has type int, not type char which means that
System.out.println(...)
binds to
System.out.println(int)
which displays its argument as a decimal number, instead of binding to
System.out.println(char)
which outputs the given character using the PrintStream's encoding.

Generating a fake ISBN from book title? (Or: How to hash a string into a 6-digit numeric ID)

Short version: How can I turn an arbitrary string into a 6-digit number with minimal collisions?
Long version:
I'm working with a small library that has a bunch of books with no ISBNs. These are usually older, out-of-print titles from tiny publishers that never got an ISBN to begin with, and I'd like to generate fake ISBNs for them to help with barcode scanning and loans.
Technically, real ISBNs are controlled by commercial entities, but it is possible to use the format to assign numbers that belong to no real publisher (and so shouldn't cause any collisions).
The format is such that:
978-0-01-######-?
Gives you 6 digits to work with, from 000000 to 999999, with the ? at the end being a checksum.
Would it be possible to turn an arbitrary book title into a 6-digit number in this scheme with minimal chance of collisions?
After using code snippets for making a fixed-length hash and calculating the ISBN-13 checksum, I managed to create really ugly C# code that seems to work. It'll take an arbitrary string and convert it into a valid (but fake) ISBN-13:
public int GetStableHash(string s)
{
uint hash = 0;
// if you care this can be done much faster with unsafe
// using fixed char* reinterpreted as a byte*
foreach (byte b in System.Text.Encoding.Unicode.GetBytes(s))
{
hash += b;
hash += (hash << 10);
hash ^= (hash >> 6);
}
// final avalanche
hash += (hash << 3);
hash ^= (hash >> 11);
hash += (hash << 15);
// helpfully we only want positive integer < MUST_BE_LESS_THAN
// so simple truncate cast is ok if not perfect
return (int)(hash % MUST_BE_LESS_THAN);
}
public int CalculateChecksumDigit(ulong n)
{
string sTemp = n.ToString();
int iSum = 0;
int iDigit = 0;
// Calculate the checksum digit here.
for (int i = sTemp.Length; i >= 1; i--)
{
iDigit = Convert.ToInt32(sTemp.Substring(i - 1, 1));
// This appears to be backwards but the
// EAN-13 checksum must be calculated
// this way to be compatible with UPC-A.
if (i % 2 == 0)
{ // odd
iSum += iDigit * 3;
}
else
{ // even
iSum += iDigit * 1;
}
}
return (10 - (iSum % 10)) % 10;
}
private void generateISBN()
{
string titlehash = GetStableHash(BookTitle.Text).ToString("D6");
string fakeisbn = "978001" + titlehash;
string check = CalculateChecksumDigit(Convert.ToUInt64(fakeisbn)).ToString();
SixDigitID.Text = fakeisbn + check;
}
The 6 digits allow for about 10M possible values, which should be enough for most internal uses.
I would have used a sequence instead in this case, because a 6 digit checksum has relatively high chances of collisions.
So you can insert all strings to a hash, and use the index numbers as the ISBN, either after sorting or without it.
This should make collisions almost impossible, but it requires keeping a number of "allocated" ISBNs to avoid collisions in the future, and keeping the list of titles that are already in store, but it's information that you would most probably want to keep anyway.
Another option is to break the ISBN standard and use hexadecimal/uuencoded barcodes, that may increase the possible range to a point where it may work with a cryptographic hash truncated to fit.
I would suggest that since you are handling old book titles, which may have several editions capitalized and punctuated differently, I would strip punctuation, duplicated whitespaces and convert everything to lowercase before the comparison to minimize the chance of a technical duplicate even though the string is different (Unless you want different editions to have different ISBNs, in that case, you can ignore this paragraph).

Resources