sprintf issue

<pre>
//C++ Builder XE7 
Edit1->Text = "Cavalieri";
AnsiString URL = "xyz.com?Name=";
AnsiString Name = Edit1->Text;
AnsiString  s1;
s1.sprintf("%s%s", URL, Edit1->Text);
AnsiString s2;
s2.sprintf("%s%s", URL, Name);
/*
At this point:
s1 = "xyz.com?Name=C";
s2 = "xyz.com?Name=Cavalieri";
*/
Why can't I embed a TEdit directly in a sprintf statement? I'm sure it's a Unicode issue. Thanks!
0
Doug
7/1/2015 5:19:10 PM
📁 embarcadero.cppbuilder.using
📃 1848 articles.
⭐ 1 followers.

💬 2 Replies
👁️‍🗨️ 1248 Views


Doug wrote:
> AnsiString  s1;
> s1.sprintf("%s%s", URL, Edit1->Text);
That will not work as shown.  In CB2009+, Edit1->Text is a UnicodeString, 
not an AnsiString.  The AnsiString::sprintf() method expects a char* for 
its "%s" format specifier, but UnicodeString is based on wchar_t* instead, 
so you need to use "%ls" instead of "%s" for wchar_t* input to AnsiString::sprintf(). 
 Also, you need to use the (Ansi|Unicode)String::c_str() method to get the 
actual pointer to the character data, do not pass (Ansi|Unicode)String objects 
themselves:
{code}
AnsiString  s1;
s1.sprintf("%s%ls", URL.c_str(), Edit1->Text.c_str());
{code}
> AnsiString s2;
> s2.sprintf("%s%s", URL, Name);
This work, only because the Ansi "%s" specifer expects char* and AnsiString 
is based on char*.  But again, use the c_str() method, at least:
{code}
AnsiString s2;
s2.sprintf("%s%s", URL.c_str(), Name.c_str());
{code}
> At this point:
> s1 = "xyz.com?Name=C";
Yes, because you passed a wchar_t* where a char* was expected, so the data 
was interpretted as a null-terminated Ansi string and the second byte in 
the wchar_t* data was a null.
> Why can't I embed a TEdit directly in a sprintf statement?
You can, you are just doing it the wrong way.  Not only because of the Ansi/Unicode 
issue of sprintf(), but also because you are not even encoding the URL's 
parameter data correctly anyway.  Reserved characters and Unicode characters 
need to be charset-encoded (typically to UTF-8) and then url-encoded in "%XX" 
format.
You need to do something more like this:
{code}
AnsiString UrlParamEncode(const UTF8String &Value)
{
    AnsiString ret;
    for (int i = 1; i <= Value.Length();)
    {
        char c = Value[i];
        if (c <= 0x7F)
        {
            if (((c >= 'A') && (c <= 'Z')) ||
                ((c >= 'a') && (c <= 'z')) ||
                ((c >= '0') && (c <= '9')))
            {
                ret += c;
            }
            else
            {
                ret.cat_sprintf("%%%02X", c);
            }
            ++i;
        }
        else
        {
            int len;
            if ((c & 0xE0) == 0xC0)
                len = 2;
            else if ((c & 0xF0) == 0xE0)
                len = 3;
            else
                len = 4;
            for (int j = 0; j < len; ++j)
                ret.cat_sprintf("%%%02X", Value[i+j]);
            i += len;
        }
    }
}
AnsiString s1 = URL + UrlParamEncode(Edit1->Text);
{code}
Or, using Indy, which ships pre-installed in the IDE, you can use its TIdURI 
class to encode URLs, eg:
{code}
#include <IdURI.hpp>
AnsiString s1 = URL + TIdURI::ParamsEncode(Edit1->Text);
{code}
Or:
{code}
#include <IdURI.hpp>
AnsiString s1 = TIdURI::URLEncode(URL + Edit1->Text);
{code}
-- 
Remy Lebeau (TeamB)
0
Remy
7/1/2015 6:13:47 PM
Thank you for the nice answer!
Doug
0
Doug
7/15/2015 4:55:26 AM
Reply: