我想创建一个简单的" date"使用sscanf
读取,接受输入为:
"dd/mm/yyyy"
两者" dd"和" mm"字段可以是2位数长(例如0,6或11,但不是123)。 "年"字段可以是0或4位字段。在这三个字段中的任何一个中,值为0表示必须代之以系统的日期,月份或年份。
该格式必须严格,因此,如果输入的格式不符合模式,则必须通知用户。
我的尝试是:
int d, m, y;
char const* input = "23/7/1990";
int n = sscanf(input, "%2u/%2u/%4u", &d, &m, &y);
if (n != 3) throw InvalidDate("Invalid format");
// Fill 0 values with system date.
// Check date correctness with `mktime` and `localtime`.
问题是此sscanf
格式接受非允许的输入:
char const* invalid1 = "23/ 12/ 1990";
char const* invalid2 = "23/12/1990/123whatever......."
那么,是否有任何技巧/修饰符在整数之前拒绝前导零,标记字符串的结尾,或者如果解析了更多输入则导致可检测的失败?
对于最后一种情况(invalid2;字符串末尾可检测到的失败),可能的解决方案是:
int d, m, y;
char trick;
char const* input = "23/7/1990";
int n = sscanf(input, "%2u/%2u/%4u%c", &d, &m, &y, &trick);
// If it fills four fields, means the input was too long.
if (fields != 3) throw InvalidDate("Invalid format");
// Fill 0 values with system date.
但我不知道是否有更好的方法来检测end-of-string
。更重要的是,这种格式" (使用前导'%c')导致sscanf
认为输入错误的有效日期(例如," 23/6/1990"引发最后一个字符不是填充;如果使用scanf
代替sscanf
,则会设置ferror
。我甚至尝试过使用"%2u/%2u/%4u\0"
,但是编译器用格式"中的#34; embedded \ 0警告我。
那么,如果不使用正则表达式或stringstream
?
顺便说一句,是否有其他方法来欺骗" sscanf
?
答案 0 :(得分:1)
你可以使用boost regex库,它可以做很多这些东西。检查以下代码:
#include <boost/regex.hpp>
#include <iostream>
#include <string>
int main()
{
// Expression to match
boost::regex e("(^\\d{1,2})/(\\d{1,2})/(\\d{4})$");
// Results are here
boost::match_results<std::string::const_iterator> results;
std::string val_to_match = "1/11/1990";
if (boost::regex_search(val_to_match, results, e) && results.size() == 4) {
std::cout << "Matched " << results[0] << std::endl;
int i = 1;
while (i < 4) {
std::cout << "Value: " << i << " "<< results[i] << std::endl;
i++;
}
} else {
std::cout << "Couldn't match \n";
}
return 0;
}
答案 1 :(得分:1)
修改了你的代码,并使其正常工作:
void parseDate(const char *date) {
char trick;
int d, m, y, n = sscanf(date, "%2u/%2u/%4u%c", &d, &m, &y, &trick);
(n != 3 || y < 999)) ?
puts("Invalid format!") : printf("%u %u %u\n", d, m, y);
}
你提到过&#34;年&#34;可以是零或四位数字,所以我修改了你的代码只接受1000到9999。否则,23/7/1a990
案例的年份为1年。
测试了这个并将输出放到文件中。
<强>结果:强>
Sample date: 23/7/1990
Output: 23 7 1990
Sample date: 23/12/1990/123whatever.......
Output: Invalid format!
Sample date: 23/ 12/ 1990
Output: 23 12 1990
Sample date: 23/12/19a90
Output: Invalid format!
Sample date: 2a/1
Output: Invalid format!
Sample date: a23/12/1990
Output: Invalid format!
Sample date: 23/12/199000
Output: Invalid format!
您可以参考此主题:How to parse and validate a date in std::string in C++?。其中一个答案建议使用strptime
。
答案 2 :(得分:1)
这个怎么样?您可以使用%[^0-9]
转换规范来读取两个数字之间的字符。
#include <stdio.h>
#include <string.h>
void process_date(const char* input){
int d, m, y;
char sep1[3], sep2[3], trick;
int n;
n = sscanf(
input, "%2u%2[^0-9]%2u%2[^0-9]%4u%c",
&d, sep1, &m, sep2, &y, &trick);
if(!(n == 5 && strcmp(sep1, "/") == 0 && strcmp(sep2, "/") == 0)){
fprintf(stderr, "Invalid format (input = %s).\n", input);
return;
}
printf("d = %d, m = %d, y = %d.\n", d, m, y);
}
int main(){
process_date("23/7/1990");
process_date("23/12/1990");
process_date("23/7/0");
process_date("23/0/1990");
process_date("0/7/1990");
process_date("23/ 12/ 1990");
process_date("23/12/1990/123whatever.......");
process_date("123/7/1990");
process_date("23/12/19a90");
process_date("2a/1");
process_date("a23/12/1990");
process_date("23/12/199000");
return 0;
}
输出:
d = 23, m = 7, y = 1990.
d = 23, m = 12, y = 1990.
d = 23, m = 7, y = 0.
d = 23, m = 0, y = 1990.
d = 0, m = 7, y = 1990.
Invalid format (input = 23/ 12/ 1990).
Invalid format (input = 23/12/1990/123whatever.......).
Invalid format (input = 123/7/1990).
Invalid format (input = 23/12/19a90).
Invalid format (input = 2a/1).
Invalid format (input = a23/12/1990).
Invalid format (input = 23/12/199000).
答案 3 :(得分:0)
这样的事情怎么样?它没有使用sscanf
,但正如评论中所说的那样,很难让这个功能按你的意愿工作:
int d, m, y;
int date[3]; //holds day/month/year in its cells
int tokenCount = 0;
char* pc;
int result = 0;
char* pch = strtok(input, "/");
while (pch != NULL)
{
if (strlen(pch) == 0)
{
throw InvalidDate("Invalid format");
}
//atoi is stupid, there's no way to tell whether the string didn't contain a valid integer or if it contained a zero
result = strtol(pch, &pc, 10);
if (*pc != 0)
{
throw InvalidDate("Invalid format");
}
if (tokenCount > 2) //we got too many tokens
{
throw InvalidDate("Invalid format");
}
date[tokenCount] = result;
tokenCount++;
pch = strtok(NULL, "/");
}
if (tokenCount != 3)
{
//not enough tokens were supplied
throw InvalidDate("Invalid format");
}
d = date[0];
m = date[1];
y = date[2];
然后您可以进行更多检查,例如月份是否在1-12之间。
要记住的一点是strtok
修改了它收到的字符串,因此请务必复制。
答案 4 :(得分:0)
因此。既然似乎每个人都认为没有办法让sscanf
更适合这种模式,我认为最好的解决方案是:
char const* input = "23/7/1990";
int d, m, y;
{ // Search blanks due to `sscanf` limitations.
for (unsigned i = 0; i < 10 and input[i] != '\0'; ++i)
if (isspace(input[i]))
throw InvalidDate("Invalid format");
} { // Check format (with extra input detection).
char trick;
int n = sscanf(input, "%2u/%2u/%4u%c", &d, &m, &y, &trick);
if (n != 3 or (y != 0 and y < 1000))
throw InvalidDate("Invalid format");
}
// Fill 0 values with system date.
// Check date correctness with `mktime` and `localtime`.
编辑:之前,我使用strpbrk
来检测空白(sscanf
在数字前忽略它)。该解决方案的问题是strpbrk
解析完整的输入,直到找到某些内容。如果输入太长,但没有空白,则执行速度会非常慢。由于我知道输入的最大允许大小,因此我使用for
的10循环isspace
进行更改。
当然,如果&#39; \ 0&#39;发现太快,但确定太快了#34; for
内的内容过于冗长。所以,我将此工作留给sscanf
,使第一个for
更好地定义。
任何其他&#34;投诉&#34;这个解决方案非常受欢迎。