Search code examples
c++stringmacrosnull-character

Seamlessly know the size of a string containing a null char


Long question for a very simple & rookie problem BUT yet I need some advise.

Background

So I have a binary file that I need to parse. This file starts with some magic string that contains the null char (\0). let's define is as ab\0cd.

I'm writing a method that returns true if some file starts with the magic string.

Attempt 1

#define MAGIC_STRING "ab\0cd"

bool IsMagicFile(const wpath& pathFile)
{
    string strData;
    if (!ReadFile(pathFile, strData))
        return false;

    if (strData.size() < 5)
        return false;

    string strPrefix = strData.substr(0, 5);

    if (strcmp(strPrefix.c_str(), MAGIC_STRING) != 0)
        return false;

    return true;
}

Problem 1

What bothers me with the above code is that I "hardcodedly" assume the size of the magic string is 5.

what if tomorrow the magic string changes? say:

#define MAGIC_STRING "abe\0fcd"

the string macro changed and the code no longers work properly.

Attempt 2

#define MAGIC_STRING "ab\0cd"

bool IsMagicFile(const wpath& pathFile)
{
    string strMagic = MAGIC_STRING;

    string strData;
    if (!ReadFile(pathFile, strData))
        return false;

    if (strData.size() < strMagic.size())
        return false;

    string strPrefix = strData.substr(0, strMagic.size());

    if (strcmp(strPrefix.c_str(), MAGIC_STRING) != 0)
        return false;

    return true;
}

Problem 2

I supposedly got rid of the hard-coded size problem BUT the size of strMagic is not really 5 but 2. string ends with \0

Attempt 3

#define MAGIC_STRING        "ab\0cd"    // CAUTION - MAGIC_STRING & MAGIC_STRING_SIZE must be changes together 
#define MAGIC_STRING_SIZE   5           // CAUTION - MAGIC_STRING & MAGIC_STRING_SIZE must be changes together

bool IsMagicFile(const wpath& pathFile)
{
    string strData;
    if (!ReadFile(pathFile, strData))
        return false;

    if (strData.size() < MAGIC_STRING_SIZE)
        return false;

    string strPrefix = strData.substr(0, MAGIC_STRING_SIZE);

    if (strcmp(strPrefix.c_str(), MAGIC_STRING) != 0)
        return false;

    return true;
}

Problem 3

This solved the first problem but I still don't get the seamless magic string change I wanted.

Question

Is attempt 3 good enough? do you have a better approach?


Solution

  • Instead of using the macro definition you could define a constant character array. For example

    const char MAGIC_STRING[] = "abe\0fcd";
    

    In this case the number of characters excluding the terminating zero is equal to

    sizeof( MAGIC_STRING ) - 1
    

    To compare raw bytes you can use standard C function memcmp supplying the number of compared bytes that is equal to the expression above.

    Here is a demonstrative program

    #include <iostream>
    #include <string>
    #include <cstring>
    #include <iterator>
    
    const char MAGIC_STRING[] = "abe\0fcd";
    
    int main() 
    {
        std::string s( std::begin( MAGIC_STRING ), std::prev( std::end( MAGIC_STRING ) )  );
    
        if ( memcmp( s.c_str(), MAGIC_STRING, sizeof( MAGIC_STRING ) - 1 ) == 0 )
        {
            std::cout << "The string starts with the MAGIC_STRING" << std::endl;
        }
    
        return 0;
    }
    

    Its output is

    The string starts with the MAGIC_STRING