首页 > 解决方案 > 识别字符串格式调试断言

问题描述


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

目的是“识别”输入字符串中的格式(%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 来识别它)。

谢谢。

标签: c++visual-studiostring-formattingstdstring

解决方案


我们来看看这个else条款:

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

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

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

现在bufFormat包含字符串“%d 个参数”。在里面isFindFormat搜索第一个isalpha字符。可能你是说isalnum这里?因为只有通过检查才能到达isdigit线路,如果是,则不是。isalphaisalphaisdigit

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

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


推荐阅读