【问题标题】:Recognize string formatting Debug Assertion识别字符串格式调试断言
【发布时间】:2021-02-15 16:12:19
【问题描述】:

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

目的是“识别”输入字符串中的格式(%s %d 等)。
为此,它返回一个与数据类型匹配的整数。 然后提取的类型在其他函数中进行操作/处理。

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

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

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] = '\0';

    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] = '\0';

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

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

我还想问你有没有更好/更高效的方法来识别字符串中的类型“%d %s etc”? (甚至不一定返回一个 int 来识别它)。

谢谢。

【问题讨论】:

  • memcpy(bufFormat, buf + pos, stringSize); 复制的字符串多于剩余的字符串(stringSize - pos 似乎更合适)... 不知道为什么你也复制到中间缓冲区,因为你可能直接使用std::string 和偏移量(@ 987654326@).

标签: c++ visual-studio string-formatting stdstring


【解决方案1】:

我们来看看这个else子句:

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

变量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 肯定会返回false,所以我们进入那个if 块。您的代码将在此处找到正确的类型。但是,循环不会终止。相反,它会继续扫描最多stringSize 个字符,即main 中的stringSize,即原始格式字符串的大小。但是您传递给isFindFormat 的字符串仅包含以'%' 开头的部分。因此,您将扫描字符串的末尾并读取缓冲区中的任何内容,这可能会触发您看到的断言错误。

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

【讨论】:

    【解决方案2】:

    你可以要求一个正则表达式引擎来搜索字符串 由于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 正则表达式的任何格式字符串。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-15
      • 2010-11-10
      • 2011-04-09
      • 1970-01-01
      相关资源
      最近更新 更多