识别字符串格式调试断言
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 正则表达式的格式字符串。
下面的代码存在运行时问题。
目的是“识别”输入字符串中的格式(%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 正则表达式的格式字符串。