首先,这是一个家庭作业问题。
具有 mm/dd/yyyy 格式的日期列表的文件将从命令提示符重定向为输入。下面是一个 ./main.out
我的教授给出了一个关于什么构成无效日期的标准。
1.) 如果有任何非数字字符,例如 12/4A/199A5。
2.) 如果他们缺少正斜杠或正斜杠超过 2 个。
3.) 如果它们是浮点数,例如 12/4/1995.3
4.) 如果它们是不正确的日期,例如 2/29/1973,因为 29 意味着该年应该是闰年,但 1973 年不能被 4 或 400 整除。
5.) 如果没有日期(空字符串)或缺少部分日期,例如 4/3/。
以下是有效日期的示例。
1.) 带有前导或尾随空格的日期,如 12/23/1694
2.) 带有前导零的日期,如 004/030/2000
3.) 任何月份数在 1 到 12 之间的日期,正确的日长(这取决于月份)和年份可以是正数、负数或零。
以下是我到目前为止所做的,仅使用必要的相关功能。
int writeToFile()
{
FILE *outputFile;
char buffer[BUFFERSIZE];
int month = 0, days = 0, year = 0;
int isValidFormat = 0, isValidDate = 0;
size_t strDateLength;
outputFile = fopen("Output.dat", "w");
if(outputFile == NULL)
{
fprintf(stderr, "Couldn't write to the file.n");
return FALSE;
}
while(fgets(buffer, BUFFERSIZE, stdin))
{
strDateLength = strlen(buffer);
if(buffer[strDateLength - 1] != 'n' && strDateLength < BUFFERSIZE - 1)
{
appendNewLine(buffer, strDateLength);
}
isValidFormat = validateDateFormat(buffer, &month, &days, &year, strDateLength);
if(isValidFormat)
{
isValidDate = validateDate(month, days, year);
if(isValidDate)
{
fprintf(outputFile, "%s", buffer);
}
}
}
fclose(outputFile);
return TRUE;
}
void appendNewLine(char *str, size_t strLength)
{
str[strLength] = 'n';
str[strLength + 1] = ' ';
}
int validateDateFormat(char *str, int *month, int *days, int *year, size_t strDateLength)
{
int index, index2 = 0;
char temp[BUFFERSIZE];
int numOfForwSlashes = 0;
for(index = 0; index < strDateLength; index++)
{
if(str[index] == '/')
{
if(numOfForwSlashes == 0)
{
*month = atoi(temp);
}
else if(numOfForwSlashes == 1)
{
*days = atoi(temp);
}
numOfForwSlashes++;
memset(&temp[0], 0, sizeof(temp));
index2 = 0;
}
else if(str[index] == 'n')
{
*year = atoi(temp);
}
else if(!isdigit(str[index]) && str[index] != ' ')
{
return FALSE;
}
else
{
temp[index2] = str[index];
index2++;
}
}
if(numOfForwSlashes != TOTALFORWARDSLASHES) // This define is 2
{
return FALSE;
}
else
return TRUE;
}
int validateDate(int month, int days, int year)
{
if((month < JANUARY || month > DECEMBER) && (days < 1 || days > THIRTYONE))
{
return FALSE;
}
if(month == FEBURARY)
{
if(isLeapYear(year))
{
if(days > TWENTYNINE)
{
return FALSE;
}
}
else
{
if(days > TWENTYEIGHT)
{
return FALSE;
}
}
}
else if(month == APRIL || month == JUNE || month == SEPTEMBER || month == NOVEMBER)
{
if(days > THIRTY)
{
return FALSE;
}
}
else
{
if(days > THIRTYONE)
{
return FALSE;
}
}
return TRUE;
}
问题:
在大多数情况下,除了缺少年份的情况外,它都可以工作,坦率地说,我想知道是否有办法在遵循 citeria 的同时使其更简洁。使用 sscanf 或 scanf 并不能解决我遇到的所有问题,像 strtok 这样的字符串函数也没有多大帮助。
与其只创建一个函数来分析。除了缺少年份的情况外,它有效,坦率地说,我想知道是否有办法使它更简洁......
日期,不如创建一个帮助程序函数来分析每个整数。
未经测试的代码示例:
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
// Parse 1 int and optional following delimiter
// Return NULL on error
static const char *Get_int(const char *p, int *i, int endchar) {
if (p) {
char *endptr;
errno = 0;
long y = strtol(p, &endptr, 10);
if (errno || p == endptr) return NULL;
*i = y; // Test for INT_MIN... INT_MAX if desired
// skip following white-space
while (isspace((unsigned char ) *endptr))
endptr++;
// Look for delimiter
if (endchar) {
if (*endptr != endchar) return NULL;
endptr++;
}
return endptr;
}
return NULL;
}
int validateDateFormat(char *str, int *month, int *days, int *year,
size_t strDateLength) {
// form string
char temp[strDateLength + 1];
memcpy(temp, str, strDateLength);
temp[strDateLength] = ' ';
const char *p = temp;
p = Get_int(p, month, '/');
p = Get_int(p, days, '/');
p = Get_int(p, year, 0);
return p && *p == ' ';
}
sscanf()
方法:
使用 "%n"
检测是否扫描了整个字符串。
int validateDateFormat(char *str, int *month, int *days, int *year,
size_t strDateLength) {
// form string
char temp[strDateLength + 1];
memcpy(temp, str, strDateLength);
temp[strDateLength] = ' ';
int n = 0;
sscanf(temp, "%d /%d /%d %n", month, days, year, &n);
return n > 0 && temp[n] == ' ';
)
黑客攻击:请注意,以下代码假定 strDateLength > 0
。 fgets()
会很乐意读入空字符作为第一个字符,并继续阅读,直到遇到'n'
。
由于strDateLength
是size_t
,无符号类型,0-1
是一个巨大的价值,buffer[strDateLength - 1]
肯定会引起问题。
while(fgets(buffer, BUFFERSIZE, stdin)) {
strDateLength = strlen(buffer);
if(buffer[strDateLength - 1] != ...
顺便说一句,无论如何都不需要添加'n'
。