You are here:   Home >> Win32/MFC >> sscanf with CString

sscanf with CString

Problem:

In the C time, we used to use sscanf to read data from string as following:


char strUserName[20], strPassword[20];
// if the content of m_recvBuff is “Microsoft Bill”.
sscanf((const char *)m_recvBuff, “%s %s”, strUserName, strPassword);

And when you first get CString in sight, you may write the similar code:


CString strUserName, strPassword;
// if the content of m_recvBuff is “Microsoft Bill”.
sscanf((const char *)m_recvBuff, “%s %s” , strUserName, strPassword);

Unfortunately, it can not work as you expected and, in most cases, the process will crash.

Solution:

First, you should not be using sccanf, since this is not Unicode-aware. You should use _stscanf which allows for Unicode.  In addition, it is not a “safe” methodology because it can generate buffer overruns.

Second, the problem here is that you are doing something profoundly illegal: you are passing a CString pointer to a call that expects an LPTSTR.  You can’t do this.  It won’t work, and it surprised me that it merely produces an erroneous result instead of crashing with an access fault, heap corruption assert, or some similar fatal situation.

After all, you can use scanf safely, but you should try to use the new _s versions to make them more “trustworthy”.  Here’s some info on it:

http://msdn2.microsoft.com/en-us/library/w40768et(VS.80).aspx

The problem you’re having is that you are not actually passing the buffers
of the CStrings to the scanf function. Take a look at the GetBuffer()
function in CString.  Also, this function might be useful to you:

http://msdn2.microsoft.com/en-us/library/k4ftfkd2(VS.80).aspx

In additional, You can cast a CString to type LPCTSTR to get read-only access to the contents of the string.  However,
(1)  You can’t cast a CString to type LPTSTR to get writeable access.  You have to do something like call GetBufferSetLength and make sure you set the buffer large enough.
(2)  In variable parameters to sscanf, you can’t expect any casting to be done automatically, you have to code the casts.

But if you take enouth care of your code, sscanf may be not necessarily dangerous. You should check the length of the string and make sure you’ve allocated large enough sizes for your other variables then you won’t have buffer overruns.

At last, in MFC, the following code may be the general solution:


CString strUserName;
CString strPassword;
m_recBuff.Trim(); // remove leading and trailing space
int n = m_recvBuff.Find(_T(” “));
if(n>0) /* has space */
{
strUserName = m_recvBuff.Left(n);
strPassword = m_recvBuff.Mid(n + 1);
strPassword.Trim();
}

Unlike sscanf, which should no longer be considered a viable mechanism for any purpose dealing with string outputs, this actually works and is safe.  It will not cause a buffer overrun.  You should make it a practice to avoid mechanisms that can cause buffer overrun.

The next paragraph is writen by Joseph M. Newcomer, as reference.

There is no economy in using obsolete and unsafe mechanisms.  sscanf is overused and I consider it one of the design errors of the C language.  I have never yet found a situation in which I would trust it to do the right thing in the presence of bad input, and the best I can say for it is that it is only suitable for use in the most naively trusting situations possible.  I never use it, and I have never used it since I first learned of it.  I had, as a beginner, used and trusted such mechanisms in languages I learned in the 1960s, and I quickly learned that such mechanisms are simply not to be trusted.  I doubt if I have used such a mechanism since about 1967 or so.  It was a bad idea then and it remains a bad idea today.  The passage of 40 years does not make a bad idea good.

Permalink: Code Library » https://www.ucosoft.com/sscanf-with-cstring.html

Related Posts

4 responses to “sscanf with CString”

  1. Adam Gross says:

    As far as I can tell, using _tscanf_s with CString is still bad because CString with UNICODE defined is UTF16 but all of these functions expect UTF8. We either need to convert it or to find some Windows-provided ones that expect UTF16.

  2. support says:

    @Adam Gross You are completely right.

  3. Antonio says:

    Well, you are casting an object on the stack as if it is a pointer, what do you expect, no crash ?

  4. This poster does not know what he is talking about. There is a version of sscanf made for Unicode charaters. There is also a secure version of sscanf. If you use that function appropriately, you can use CStrings correctly. Here is the example described above done using _stscanf_s and CStrings:

    const unsigned int kuBufferSize = 20;
    CString strField = _T(“Microsoft Bill”);
    CString strName(_T(“”), kuBufferSize);
    CString strPassword(_T(“”), kuBufferSize);
    int nItemCount = 0;

    nItemCount = _stscanf_s(strField, _T(“%s %s”), strName, kuBufferSize, strPassword, kuBufferSize);

    OutputDebugString(strName);
    OutputDebugString(_T(“\n”));

    OutputDebugString(strPassword);
    OutputDebugString(_T(“\n”));

    This code produces this in the Debug output window in Visual Studio:

    Microsoft
    Bill

    These values are appropriately stored in the variables:
    strName = _T(“Microsoft”);
    strPassword = _T(“Bill”);
    nItemCount = 2;

    I didn’t need this solution for my situation, but I was intrigued on how to solve yours. I simply needed to get an integer out of a CString, so I used the solution above except for integers. For more information, see the documentation here:
    http://msdn.microsoft.com/en-us/library/t6z7bya3%28v=VS.90%29.aspx

Leave a Reply

Your email address will not be published. Required fields are marked *