从重定向为输入的文件中验证日期
Validating dates from a file that is redirected as input
首先,这是一道作业题。
具有 mm/dd/yyyy 格式的日期列表的文件将被重定向为来自命令提示符的输入。这是一个例子./main.out < file.txt。对于我找到的每个有效日期,我想将其写入输出文件。
我的教授给出了构成无效日期的标准。
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] = '[=10=]';
}
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;
}
问题:
除了缺少年份的情况外,它在大多数情况下都有效,坦率地说,我想知道是否有办法在仍然遵循标准的同时使它更简洁。使用 sscanf 或 scanf 并不能解决我遇到的所有问题,strtok 之类的字符串函数也无济于事。
... it works except for cases where the year is missing and to be frank I would like to know if there is way I could make this more concise ...
与其只创建一个函数来解析日期,不如创建一个辅助函数来解析每个整数。
未经测试的代码示例:
#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] = '[=10=]';
const char *p = temp;
p = Get_int(p, month, '/');
p = Get_int(p, days, '/');
p = Get_int(p, year, 0);
return p && *p == '[=10=]';
}
一个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] = '[=11=]';
int n = 0;
sscanf(temp, "%d /%d /%d %n", month, days, year, &n);
return n > 0 && temp[n] == '[=11=]';
)
黑客利用:请注意,下面的代码假定 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'
。
首先,这是一道作业题。
具有 mm/dd/yyyy 格式的日期列表的文件将被重定向为来自命令提示符的输入。这是一个例子./main.out < file.txt。对于我找到的每个有效日期,我想将其写入输出文件。
我的教授给出了构成无效日期的标准。
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] = '[=10=]';
}
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;
}
问题:
除了缺少年份的情况外,它在大多数情况下都有效,坦率地说,我想知道是否有办法在仍然遵循标准的同时使它更简洁。使用 sscanf 或 scanf 并不能解决我遇到的所有问题,strtok 之类的字符串函数也无济于事。
... it works except for cases where the year is missing and to be frank I would like to know if there is way I could make this more concise ...
与其只创建一个函数来解析日期,不如创建一个辅助函数来解析每个整数。
未经测试的代码示例:
#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] = '[=10=]';
const char *p = temp;
p = Get_int(p, month, '/');
p = Get_int(p, days, '/');
p = Get_int(p, year, 0);
return p && *p == '[=10=]';
}
一个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] = '[=11=]';
int n = 0;
sscanf(temp, "%d /%d /%d %n", month, days, year, &n);
return n > 0 && temp[n] == '[=11=]';
)
黑客利用:请注意,下面的代码假定 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'
。