识别字符串格式调试断言

Recognize string formatting Debug Assertion


下面的代码存在运行时问题。

目的是“识别”输入字符串中的格式(%s %d 等)。
为此,它 returns 一个与数据类型匹配的整数。 那么提取出来的类型在其他函数中是manipulated/handled

我想澄清一下,我的目的不是在字符串(snprintf 等)中编写格式化类型,而只是 recognize/extract 它们。

问题是我的应用程序崩溃并出现错误:

Debug Assertion Failed!
Program:
...ers\Alex\source\repos\TestProgram\Debug\test.exe
File: minkernel\crts\ucrt\appcrt\convert\isctype.cpp
Line: 36

Expression: c >= -1 && c <= 255

我的代码:

#include <iostream>
#include <cstring>

enum Formats
{
    TYPE_INT,
    TYPE_FLOAT,
    TYPE_STRING,

    TYPE_NUM
};

typedef struct Format
{
    Formats         Type;
    char            Name[5 + 1];
} SFormat;

SFormat FormatsInfo[TYPE_NUM] =
{
    {TYPE_INT,      "d"},
    {TYPE_FLOAT,    "f"},
    {TYPE_STRING,   "s"},
};


int GetFormatType(const char* formatName)
{
    for (const auto& format : FormatsInfo)
    {
        if (strcmp(format.Name, formatName) == 0)
            return format.Type;
    }

    return -1;
}

bool isValidFormat(const char* formatName)
{
    for (const auto& format : FormatsInfo)
    {
        if (strcmp(format.Name, formatName) == 0)
            return true;
    }

    return false;
}

bool isFindFormat(const char* strBufFormat, size_t stringSize, int& typeFormat)
{
    bool foundFormat = false;
    std::string stringFormat = "";

    for (size_t pos = 0; pos < stringSize; pos++)
    {
        if (!isalpha(strBufFormat[pos]))
            continue;

        if (!isdigit(strBufFormat[pos]))
        {
            stringFormat += strBufFormat[pos];

            if (isValidFormat(stringFormat.c_str()))
            {
                typeFormat = GetFormatType(stringFormat.c_str());
                foundFormat = true;
            }
        }
    }

    return foundFormat;
}

int main()
{
    std::string testString = "some test string with %d arguments";          // crash application
    // std::string testString = "%d some test string with arguments";   // not crash application

    size_t stringSize = testString.size();

    char buf[1024 + 1];
    memcpy(buf, testString.c_str(), stringSize);
    buf[stringSize] = '[=11=]';

    for (size_t pos = 0; pos < stringSize; pos++)
    {
        if (buf[pos] == '%')
        {
            if (buf[pos + 1] == '%')
            {
                pos++;
                continue;
            }
            else
            {
                char bufFormat[1024 + 1];
                memcpy(bufFormat, buf + pos, stringSize);
                bufFormat[stringSize] = '[=11=]';

                int typeFormat;
                if (isFindFormat(bufFormat, stringSize, typeFormat))
                {
                    std::cout << "type = " << typeFormat << "\n";
                    // ...
                }
            }
        }
    }
}

正如我在代码中评论的那样,第一个字符串一切正常。而第二个,应用程序崩溃。

我还想问你有没有一种better/more 执行方式来识别字符串中的类型“%d %s etc”? (甚至不一定返回一个 int 来识别它)。

谢谢。

我们来看看这个else子句:

char bufFormat[1024 + 1];
memcpy(bufFormat, buf + pos, stringSize);
bufFormat[stringSize] = '[=10=]';

变量stringSize 已用原始格式字符串的大小进行了初始化。假设在这种情况下是 30。

假设您在偏移量 20 处找到了 %d 代码。您将从偏移量 20 开始复制 30 个字符到 bufFormat。这意味着您要复制超过原始字符串末尾的 20 个字符。您可能会读出原始 buf 的结尾,但此处不会发生这种情况,因为 buf 很大。第三行将 NUL 设置到缓冲区的位置 30,再次超过数据的末尾,但是你的 memcpy 将 NUL 从 buf 复制到 bufFormat,所以这就是字符串bufFormat即将结束。

现在 bufFormat 包含字符串“%d 个参数”。在 isFindFormat 中搜索第一个 isalpha 字符。可能你的意思是 isalnum 在这里?因为只有isalpha检查通过了才能到isdigit行,如果是isalpha,就不是isdigit.

反正isalpha过后,isdigit肯定会returnfalse所以我们进入那个if块。您的代码将在这里找到正确的类型。但是,循环不会终止。相反,它会继续扫描最多 stringSize 个字符,即 main 中的 stringSize 个字符,即原始格式字符串的大小。但是您传递给 isFindFormat 的字符串只包含以“%”开头的部分。所以你要扫描字符串的末尾并读取缓冲区中的任何内容,这可能会触发你看到的断言错误。

这里还有很多事情要做。您正在混合和匹配 std::string 和 C 字符串;看看是否可以使用 std::string::substr 而不是复制。您可以使用 std::string::find 来查找字符串中的字符。如果必须使用 C 字符串,请使用 strcpy 而不是 memcpy,然后添加 NUL。

你可以只要求一个正则表达式引擎来搜索字符串 由于 C++11 有直接支持,你需要做的是

   #include <regex>

然后你可以使用各种方法匹配字符串,例如 regex_match ,这让你有可能,连同 smatch 使用标准库仅需几行代码就可以找到您的目标

   std::smatch sm;
   std::regex_match ( testString.cbegin(), testString.cend(), sm, str_expr);

其中 str_exp 是您的正则表达式,用于查找您特别想要的内容 在 sm 你现在有每个匹配的字符串与你的正则表达式,你可以用这种方式打印

   for (int i = 0; i < sm.size(); ++i)
   {
      std::cout << "Match:" << sm[i] << std::endl;
   }

编辑: 为了更好地表达您将实现的结果,我将在下面包含一个简单示例

    // target string to be searched against
    string target_string = "simple example no.%d is: %s";
    // pattern to look for 
    regex str_exp("(%[sd])");
    // match object
    smatch sm;
    // iteratively search your pattern on the string, excluding parts of the string already matched 
    cout << "My format strings extracted:" << endl;
    while (regex_search(target_string, sm, str_exp))
    {
        std::cout << sm[0] << std::endl;
        target_string = sm.suffix();
    }

您可以轻松添加任何您想要修改 str_exp 正则表达式的格式字符串。