首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Weekday+day验证

Weekday+day验证
EN

Code Review用户
提问于 2016-12-22 12:15:54
回答 3查看 202关注 0票数 6

我想检查的字符串类似于"abcSun24def“。如果找到任何有效的"xxxyy“(xxx =工作日和yy = day),则返回字符串中的位置。如果找不到"xxxyy“,则返回-1。

代码按需要工作,但我认为可以对其进行优化。

代码语言:javascript
复制
/* -------------------------------------------------------------
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;
}
EN

回答 3

Code Review用户

回答已采纳

发布于 2016-12-22 14:43:34

我看到了一些可以帮助您改进代码的东西。

使用所需的#includes

代码使用strlenmemcpy,这意味着它应该使用#include <string.h>。不难推断,但如果代码已经完成,则可以帮助审阅者。它也是接口的一个重要部分。我相信这些措施包括:

代码语言:javascript
复制
#include <string.h>
#include <stdlib.h>
#include <ctype.h>

在实际的

中使用const

在您的findxy例程中,传递的字符串从未更改过,这与它应该做的一样。你应该用这样的声明来表明这一事实:

代码语言:javascript
复制
int findxy(const char *c)

检查空指针

如果例程被传递给一个NULL指针,事情就不顺利了。在我的机器上,我得到一个分割错误和一个崩溃。您可以通过在例程顶部附近添加这些行来消除这个漏洞:

代码语言:javascript
复制
if (c == NULL) {
    return -1;
}

使用更好的命名

days数组的名称很好,因为很容易从它的名称中猜出它包含了什么。同样,ij通常用作索引变量,就像您在这段代码中所做的那样。但是,对于这样做,findxy是一个相当神秘的名称,而对于传递的字符串,c是一个糟糕的名称。我建议你做这样的事情:

代码语言:javascript
复制
int findWeekdayDate(const char *str)

避免复制如果实际的

严格来说,没有必要复制传递的字符串的部分。只要有一点周密的计划,就可以做到这一点。这里有一种方法,尽管效率不高:

代码语言:javascript
复制
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'}之一。我们可以这样做,一次一个字符来创建一个有限状态机。下面是这样一个状态机的可视化:

这就是像flexbison以及lexyacc这样的编译器工具的工作方式。代码非常高效,但可能不那么容易理解,因此您应该注意到这是一种权衡。

--非常迂腐的

音符

严格地说,这一行:

代码语言:javascript
复制
int val = (pos[3]-'0') * 10 + (pos[4]-'0');

保证是便携的。C标准要求数字编码是连续的,因此这将适用于任何字符编码,包括EBCDIC、Unicode和ASCII。

票数 6
EN

Code Review用户

发布于 2016-12-22 17:34:20

除了@Edward的精细答案之外,还需要添加一些其他要点。

  1. (int)强制转换在for (i = 0; i <= (int)strlen(c)-5; i++)中不需要,因为代码可以使用i + 5 <= strlen(c)
  2. strlen()返回size_t类型。通常,对于数组索引而言,int可能太窄,而size_t是数组索引的合适大小。只要使用size_t就够了。不过,这确实会影响返回类型。如果在过去的INT_MAX数组中找到日期,那么代码应该返回什么?(是的,这意义深远,但将其应用于字符串处理通常仍然有效。)size_t i;
  3. 与其使用strlen()重复计算字符串长度(每次迭代需要长度),不如只调用strlen()一次,如果有调用,则只调用一次。
  4. 使用is...()函数快速筛选无效候选人。

与其他一些想法,一个样本未经测试(待办)的替代方案。

代码语言:javascript
复制
#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;
}
票数 2
EN

Code Review用户

发布于 2016-12-23 13:35:12

检查输入字符

应检查输入字符是否在ASCII值32至126之外。当前的实现在输入“mon31”时失败,因为strlen返回字符‘’的2。

票数 0
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/150587

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档