我想检查的字符串类似于"abcSun24def“。如果找到任何有效的"xxxyy“(xxx =工作日和yy = day),则返回字符串中的位置。如果找不到"xxxyy“,则返回-1。
代码按需要工作,但我认为可以对其进行优化。
/* -------------------------------------------------------------
FUNC : findxy (find pattern xxxyy)
xxx = weekday (e.g. "Mon01")
yy = day
roster specific formatting
PARAMS : c (char *), pointer to string
RETURNS : (int), if pattern found, pointer to found pattern in string c
-1 if pattern not found
REMARKS :
---------------------------------------------------------------- */
int findxy(char *c) {
const char *days[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
int i, j;
char bufw[4];
char bufd[3];
/* check if c is at least 5 chars long */
if (strlen(c) < 5)
return -1;
for (i = 0; i <= (int)strlen(c)-5; i++) {
memcpy(bufw, c+i, 3);
bufw[3]='\0';
/* check all 7 weekdays */
for (j = 0; j < 7; j++) {
/* find weekday matches */
if (!strcmp(bufw, days[j])) {
/* check if both chars following weekday are numerical */
if (isdigit(c[i+3]) && isdigit(c[i+4])) {
memcpy(bufd, c+i+3, 2);
bufd[2]='\0';
/* check if number after weekday is a valid day */
if (atoi(bufd) >= 1 && atoi(bufd) <= 31) {
return i;
}
}
}
}
}
return -1;
}发布于 2016-12-22 14:43:34
我看到了一些可以帮助您改进代码的东西。
#includes代码使用strlen和memcpy,这意味着它应该使用#include <string.h>。不难推断,但如果代码已经完成,则可以帮助审阅者。它也是接口的一个重要部分。我相信这些措施包括:
#include <string.h>
#include <stdlib.h>
#include <ctype.h>中使用const
在您的findxy例程中,传递的字符串从未更改过,这与它应该做的一样。你应该用这样的声明来表明这一事实:
int findxy(const char *c)如果例程被传递给一个NULL指针,事情就不顺利了。在我的机器上,我得到一个分割错误和一个崩溃。您可以通过在例程顶部附近添加这些行来消除这个漏洞:
if (c == NULL) {
return -1;
}days数组的名称很好,因为很容易从它的名称中猜出它包含了什么。同样,i和j通常用作索引变量,就像您在这段代码中所做的那样。但是,对于这样做,findxy是一个相当神秘的名称,而对于传递的字符串,c是一个糟糕的名称。我建议你做这样的事情:
int findWeekdayDate(const char *str)严格来说,没有必要复制传递的字符串的部分。只要有一点周密的计划,就可以做到这一点。这里有一种方法,尽管效率不高:
int isValidWeekdayDate(const char *str) {
static const char *days[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
if (str == NULL || strlen(str) < 5) {
return 0;
}
for (const char **dayname = days; *dayname; ++dayname) {
char *pos = strstr(str, *dayname);
if (pos && pos == str) {
if (isdigit(pos[3]) && isdigit(pos[4])) {
int val = (pos[3]-'0') * 10 + (pos[4]-'0');
if (val > 0 && val <= 31) {
return 1;
}
}
}
}
return 0;
}
int findWeekdayDate(const char *str) {
for (const char *curr = str ; *curr; ++curr) {
if (isValidWeekdayDate(curr)) {
return curr-str;
}
}
return -1;
}我们可以通过创建一个有限状态机来创建一个更有效的例程。对于特定的候选字符串,我们注意到第一个字符必须是{'F‘、'M’、'S‘、'T’、'W'}之一。如果不是其中之一,则可以立即拒绝候选字符串。现在我们假设第一个角色是'S‘。在这种情况下,第二个字符必须是{'a','u'}之一。我们可以这样做,一次一个字符来创建一个有限状态机。下面是这样一个状态机的可视化:

这就是像flex和bison以及lex和yacc这样的编译器工具的工作方式。代码非常高效,但可能不那么容易理解,因此您应该注意到这是一种权衡。
音符
严格地说,这一行:
int val = (pos[3]-'0') * 10 + (pos[4]-'0');保证是便携的。C标准要求数字编码是连续的,因此这将适用于任何字符编码,包括EBCDIC、Unicode和ASCII。
发布于 2016-12-22 17:34:20
除了@Edward的精细答案之外,还需要添加一些其他要点。
(int)强制转换在for (i = 0; i <= (int)strlen(c)-5; i++)中不需要,因为代码可以使用i + 5 <= strlen(c)。strlen()返回size_t类型。通常,对于数组索引而言,int可能太窄,而size_t是数组索引的合适大小。只要使用size_t就够了。不过,这确实会影响返回类型。如果在过去的INT_MAX数组中找到日期,那么代码应该返回什么?(是的,这意义深远,但将其应用于字符串处理通常仍然有效。)size_t i;strlen()重复计算字符串长度(每次迭代需要长度),不如只调用strlen()一次,如果有调用,则只调用一次。is...()函数快速筛选无效候选人。与其他一些想法,一个样本未经测试(待办)的替代方案。
#defined UC(ch) ((unsigned char) ch)
#define DOM_MIN 1
#define DOM_MAX 31
// return pointer to location with the date or NULL if not found
const char *find_DOW_Date2(const char *c) {
while (*c) {
if (isupper(UC(c[0])) && islower(UC(c[1])) && islower(UC(c[2])) &&
isdigit(UC(c[3])) && isdigit(UC(c[4]))) {
int dom = (c[3] - '0')*10 + (c[4] - '0');
if (dom >= DOM_MIN && dom <= DOM_MAX) {
static const char days[][3] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
for (size_t i = 0; i < sizeof days/sizeof days[0]; i++) {
if (strncmp(c, days[i], 3) == 0) {
return c;
}
}
}
}
c++;
}
return NULL;
}发布于 2016-12-23 13:35:12
应检查输入字符是否在ASCII值32至126之外。当前的实现在输入“mon31”时失败,因为strlen返回字符‘’的2。
https://codereview.stackexchange.com/questions/150587
复制相似问题