How to compare two BSTRs or CComBSTRs? - c++

What is the right way to compare two CComBSTRs? I tried to use
bool operator ==(
const CComBSTR& bstrSrc
) const throw( );
However it always return false even two ComBSTRs are the same. It did not work correctly.
Do I have to convert CComBSTRs to ANSI string first and then use strcmp?
Thanks!
-bc

You should probably use VarBstrCmp.
EDIT: this is actually what CComBSTR::operator== does, so without further context, your code may be incorrect.

BSTRs (and therefore CComBSTRs) are usually Unicode strings. You can use wcscmp() (or wcsicmp() for case-insensitive comparison).
Beware that encapsulated BSTR can be null which is a legal representation for an empty string and this should be treated as a special case, otherwise your program might run into undefined behaviour (most likely just crash).

To properly compare BSTR values which may contain embedded null characters you need to use something like this:
bool EqualBSTR(const BSTR String1, const BSTR String2, bool IgnoreCase = false)
{
if (String1 == nullptr || String2 == nullptr) {
return false;
}
const size_t MaxCount = std::min(static_cast<size_t>(SysStringLen(String1)), static_cast<size_t>(SysStringLen(String2)));
if (IgnoreCase) {
return _wcsnicmp(String1, String2, MaxCount) == 0;
} else {
return wcsncmp(String1, String2, MaxCount) == 0;
}
}

BSTRsAreEqual(BSTR bstr1, BSTR bstr2, VARIANT_BOOL* boolptrEqual)
{
CString s1, s2;
s1 = bstr1;
s2 = bstr2;
if (s1 == s2) {
*boolptrEqual = true;
} else {
*boolptrEqual = false;
}
}

Related

Is there a function/WinAPI to tell if one string starts with another string in a case-insensitive linguistic way?

The best way to illustrate my question is with this example (that doesn't work if I use the strstr CRT function):
const wchar_t* s1 = L"Hauptstraße ist die längste";
const wchar_t* s2 = L"Hauptstrasse";
bool b_s1_starts_with_s2 = !!wcsstr(s1, s2);
_ASSERT(b_s1_starts_with_s2); //Should be true
So far the only WinAPI that seems to recognize linguistic string equivalency is CompareStringEx when used with the LINGUISTIC_IGNORECASE flag, but it is somewhat tricky & inefficient to use for this purpose as I will have to call it on s2 repeatedly until I reach its end.
So I was wondering if there's a better approach to doing this (under Windows)?
EDIT: Here's what I mean:
bool b_s1_starts_with_s2 = false;
int ln1 = (int)wcslen(s1);
int ln2 = (int)wcslen(s2);
for(int p = 1; p <= ln1; p++)
{
if(::CompareString(LOCALE_USER_DEFAULT, LINGUISTIC_IGNORECASE,
s1, p,
s2, ln2) == CSTR_EQUAL)
{
//Match
b_s1_starts_with_s2 = true;
break;
}
}
You can use FindNLSString, check if the return value is zero.
Evidently it matches ß with ss
const wchar_t *s1 = L"Hauptstraße ist die längste";
const wchar_t *s2 = L"Hauptstrasse";
INT found = 0;
int start = FindNLSString(0, LINGUISTIC_IGNORECASE, s1, -1, s2, -1, &found);
wprintf(L"start = %d\n", start);
s1 = L"δεθ Testing Greek";
s2 = L"ΔΕΘ";
start = FindNLSString(0, LINGUISTIC_IGNORECASE, s1, -1, s2, -1, &found);
wprintf(L"start = %d\n", start);
I have not tried it, but I think you probably could use LCMapStringEx to transform all strings to lowercase appropriately for the locale, and then do a normal string prefix match with wcsncmp.
(As noted in comments, it makes no sense that you used wcsstr in your example since wcsstr determines if one string contains another string. To determine if one string starts with another string, it's more efficient to use wcsncmp with the length of the prefix string.)

efficiently compare QString and std::string for equality

I want to efficiently compare a QString and a std::string for (in)equality. Which is the best way to do it, possibly without creating intermediate objects?
QString::fromStdString() and QString::toStdString() comes to mind, but they create temporary copy of the string, so afaik, if you don't want to have temporary objects, you will have to write this function yourself (though what is more efficient is a question).
Example:
QString string="string";
std::string stdstring="string";
qDebug()<< (string.toStdString()==stdstring); // true
QString string="string";
std::string stdstring="std string";
qDebug()<< (str==QString::fromStdString(stdstring)); // false
By the way in qt5, QString::toStdString() now uses QString::toUtf8() to perform the conversion, so the Unicode properties of the string will not be lost (qt-project.org/doc/qt-5.0/qtcore/qstring.html#toStdString
It can be done without intermediate objects, if you are absolutely sure that the two strings contain only Latin characters:
bool latinCompare(const QString& qstr, const std::string& str)
{
if( qstr.length() != (int)str.size() )
return false;
const QChar* qstrData = qstr.data();
for( int i = 0; i < qstr.length(); ++i ) {
if( qstrData[i].toLatin1() != str[i] )
return false;
}
return true;
}
Otherwise you should decode the std::string into a QString and compare the two QStrings.

CString find the last entry

I have two CString s1 and CString s2. I need find the last entry s2 in s1.
I can find any metod in CString like in C# LastIndexOf.
I am nooby in c++. Thanks in advance.
CString has no such function. You have to write it yourself, e.g.
int LastIndexOf(const CString& s1, const CString& s2)
{
int found = -1;
int next_pos = 0;
for (;;)
{
next_pos = s1.Find(s2, next_pos);
if (next_pos == -1)
return found;
found = next_pos;
};
}
A more optimal algorithm would reverse the strings first, I'm leaving that as an exercise.
i adopted the answer from Andrey (to increment the next_pos, seems to cause an endless loop without it). aaand, since i don't have enough repu points yet (can't comment) i'll post it as a separate answer:
int LastIndexOf(const CString& s1, const CString& s2)
{
int start = s1.Find(s2, 0);
if (start >= 0)
{
while (start < s1.GetLength())
{
int idx = s1.Find(s2, start+1);
if (idx >= 0)
start = idx;
else
break;
}
}
return start;
}
There is no CString method to directly address your question. However, you can use a combination of CString::ReverseFind + _tcsncmp to first locate next occurrence of last character of substring and, if found, compare the whole substring from there.
CString is I think part of the "Microsoft Foundation Class Library" and is not Standard C++.
There is a reference including methods here:
http://msdn.microsoft.com/en-us/library/aa315043%28v=vs.60%29.aspx
I don't see anything to turn that directly into a std::string (which has a lot more methods), but it probably is not so hard, either (search for "CString to std::string" and you'll find some stuff).
Although they are presumably related, don't confuse this with a c-string, which is an array of chars from Standard C included in Standard C++.

Comparing const char to a string

I have an issue comparing a const char to a string... If I use Com_Printf ("%s", value);
It returns what I want (0.3c), but how can I convert value to a string and compare that to 0.3c? This is what I have:
value = SearchInfostring(msg, "shortversion");
if (value != "0.3c")
{
Com_Printf (MSG_WARNING,
Com_Printf (MSG_WARNING,
"> WARNING: Value: Should be 0.3c, is: %s \n",
value);
//Run stuff
}
That returns:
WARNING: Value: Should be 0.3c, is: 0.3c
If value is of type const char*, expression
value != "0.3c"
is comparing two pointers (addresses), not strings. You want to compare string and a string literal so can use strcmp:
if(strcmp(value, "0.3c"))
{
// strings are not equal
}
else
{
// strings are equal
}
Bear in mind that preferred string type in C++ is std::string.
Use an std::string for value.
std::string value = SearchInfoString(msg, "shortversion");
Then, you can compare it normally. If you cannot use a string at all for whatever reason (the return value can be converted), use strcmp.
if (strcmp (value, "0.3c") != 0)
{
...
}
It seems that SearchInfoString returns a char *, based on the way you use it with Com_Printf. Therefore you can just use strcmp() to compare value to "0.3c". For example:
if (strcmp(value, "0.3c") != 0)
{
// Print warning
}

strcmp error comparing converted wide string

I added this because I am trying to convert handle WStrings in Android NDK NDK does not support wide characters. I could use advice on how to do this. I think the asciiConvert method does not work anymore
typedef std::basic_string<wchar_t> WString;
WString val;
val=L"";
set_val(L"");
char* value=asciiConvert(get_val()); // value is 0x00000000
std::string token; // value is ""
if (strcmp(token.c_str(),value)==0) //ERROR HERE: INFINITE LOOP HERE I THINK since it will never be true.
HERE IS THE CONVERSION FUNCTION:
char* asciiConvert(const wchar_t* wideStr, char replSpace) // replSpace == -1
{
if (wideStr == NULL)
return NULL;
char* asciiStr = new char[wcslen(wideStr) + 10];
sprintf(asciiStr, "%S", wideStr);
if (replSpace >= 0)
{
int len = strlen(asciiStr);
while (len)
{
if (asciiStr[len] == ' ')
asciiStr[len] = replSpace;
len--;
}
}
return asciiStr;
}
UPDATE: the typedef is advised for some implementations which do not support wstring so I think I need, but now something to not work like above. Have not used C++ in a while so I could use very specific instructions on this.
Basically I have dozens of const wchar_t* foo(const wchar_t* a, const wchar_t& b)
and a quite a few wchar* [] as well as const wchar_t* memVariable; even virutal functions with these.
How about CrystalX for this? Is that the way to go?