【问题标题】:How to check if an index contains a symbol?如何检查索引是否包含符号?
【发布时间】:2017-02-13 02:19:33
【问题描述】:

我想检查以确保包含在名为 secretWord 的数组中的给定字符串中没有符号(例如 $ % & #)。如果它确实有一个符号,我会让用户重新输入字符串。它利用递归不断询问,直到他们输入一个不包含符号的字符串。

接受的唯一符号是 NULL 符号(由 ASCII 值为零表示的符号)。这是因为我用 NULL 符号填充了数组中的所有空白空间。

我的功能如下:

void checkForSymbols(char *array, int arraysize){ //Checks for symbols in the array and if there are any it recursively calls this function until it gets input without them.
 for (int i = 0; i < arraysize; i++){
    if (!isdigit(array[i]) && !isalpha(array[i]) && array[i] != (char) 0){
      flushArray(array, arraysize);
      printf("No symbols are allowed in the word. Please try again: ");
      fgets(secretWord, sizeof(secretWord) - 1, stdin);
      checkForSymbols(secretWord, sizeof(secretWord));
    }//end if (!isdigit(array[i]) && !isalpha(array[i]) && array[i] != 0)
    else
      continue;
  }//end for(i = 0; i < sizeof(string[]); i++){
}//end checkForSymbols

问题:当我输入任何输入时(参见下面的示例),if 语句就会运行(它会打印 No symbols are allowed in the word. Please try again: 并要求新的输入)。 我认为问题显然源于声明if (!isdigit(array[i]) &amp;&amp; !isalpha(array[i]) &amp;&amp; array[i] != (char) 0)。但我也尝试将(char) 0 部分更改为'\0'0,但都没有任何效果。

如果索引中的内容是符号,我该如何比较呢?为什么不带符号的字符串会关闭此 if 语句?

如果你们中有人想知道我使用的“flushArray”方法是什么,这里是:

    void flushArray(char *array, int arraysize){ //Fills in the entire passed array with NULL characters
  for (int i = 0; i < arraysize; i++){
    array[i] = 0;
  }
}//end flushArray

这个函数在我的 main() 方法的第三行调用,紧接着第一行的 print 语句要求用户输入一个单词,第二行的 fgets() 语句获取输入的输入这个 checkForSymbols 函数用在上面。


根据要求,例如,如果我输入“Hello”作为secretWord 字符串。然后程序在上面运行函数,if语句由于某种原因被触发,导致它

  1. 将存储在secretWord 数组中的所有值替换为 ASCII 值 0。(AKA NULL)
  2. No symbols are allowed in the word. Please try again: 打印到控制台。
  3. 等待将存储在 secretWord 数组中的新输入。
  4. 对存储在secretWord 中的这些新值调用checkForSymbols() 方法。

无论您输入什么作为新的 secretWord,checkForSymbols() 方法的 if 语句都会触发,它会再次重复步骤 1 到 4。


感谢您的耐心和理解!

【问题讨论】:

  • 请提供minimal reproducible example,包括如何读取输入、测试的确切输入是什么以及如何调用此函数。
  • 在用户输入放入数组之前是否调用了flushArray?另外,为什么不在找到第一个 null 时停止(即将其视为以 null 结尾的字符串)?
  • 另外,你应该学会使用调试器,然后你可以看到 array[i] 中没有通过测试的值。或者你可以把它打印出来(或者是 ASCII 值)
  • fgets(secretWord, ...) 没有secretWord。尽管您说这是递归的,但也不是。您的 checkForSymbols 调用 checkforNums (我们很方便地看不到)。慢慢来,想想你的目标、问题和问题,并包含所有所需的代码,以一个最小的完整示例来演示问题。
  • 解决这个问题的方法是确保array 是一个字符串(例如nul-terminated),然后确定你想要的符号排除(例如char *exclude = "$%&amp;#";)。然后你可以简单地问if (strpbrk (array, exclude)) { ...handle array contains symbol... }。您还可以反转逻辑并检查array 是否仅由a-zA-Z 组成,同样容易。

标签: c arrays


【解决方案1】:

您可以这样做来查找代码中的符号,将代码放在适当的位置

 #include <stdio.h> 
 #include <string.h> 
 int main () { 
      char invalids[] = "@.<#>"; 
      char * temp; 
      temp=strchr(invalids,'s');//is s an invalid character?
      if (temp!=NULL) {
             printf ("Invalid character"); 
      } else {
             printf("Valid character");
      }
  return 0; 
 }

这将检查s 是否是有效条目,因为您可以创建一个数组并在数组不是空终止的情况下执行类似的操作。

 #include <string.h> 
 char false[] = { '@', '#', '&', '$', '<' }; // note last element isn't '\0' 
 if (memchr(false, 'a', sizeof(false)){ 
      // do stuff 
 }

memchr 用于如果您的数组不是以 null 结尾的。

正如@David C. Rankin 所建议的,您也可以使用strpbrk like

 #include <stdio.h> 
 #include <string.h>
 int main () { 
      const char str1[] = ",*#@_$&+.!"; 
      const char str2[] = "@#"; //input string
      char *ret;
      ret = strpbrk(str1, str2);
      if(ret) {
            printf("First matching character: %c\n", *ret); 
      } else {
           printf("Continue"); 
     } 
 return(0); 
 }

【讨论】:

  • 是的,但这意味着我需要一个有限的常用符号列表,我必须手动输入这些符号。我只想检测不是字母或数字的 ANYTHING
  • @Omninano 是的,然后反转逻辑查找 atoz 和 0to9 作为您的错误条件。
  • @Omninano 尝试将 if (!isdigit(array[i]) &amp;&amp; !isalpha(array[i]) &amp;&amp; array[i] != (char) 0) 替换为 if (isdigit(array[i]) || isalpha(array[i]) || array[i] == (char) 0) 并将 checkForSymbols(secretWord, sizeof(secretWord)); 放入 else 而不是 if
【解决方案2】:

我接受的唯一符号是 NULL 符号(由 ASCII 值零表示的符号)。这是因为我用 NULL 符号填充了数组中的所有空白空间。

NULL 是一个指针;如果你想要一个字符值0,你应该使用0'\0'。我假设您使用memsetstrncpy 来确保尾随字节为零?不...真可惜,您的MCVE 可能要短得多(并且完整)。 :(


void checkForSymbols(char *array, int arraysize){
    /* ... */
        if (!isdigit(array[i]) && !isalpha(array[i]) /* ... */

根据 C 标准的 7.4p1 部分,...

在所有情况下,参数都是一个 int,其值应表示为无符号字符或应等于宏 EOF 的值。如果参数有任何其他值,则行为未定义。

并非所有char 值都可以表示为unsigned char 或等于EOF,因此上面的代码调用是可能的(并且很有可能考虑到这个问题的性质)未定义的行为。

由于您尚未完成您的问题(通过提供 MCVE,并描述正在发生的错误)我假设您正在尝试回答的问题ask 可能是this questionthis questionthis questionthis question 的重复项,可能还有很多其他的...如果是这样,您是否尝试谷歌搜索错误消息?这可能是你应该做的第一件事。如果将来失败,请询问关于错误消息的问题!


根据要求,例如,如果我输入“Hello”作为 secretWord 字符串。

我假设secretWord 在您的示例中被声明为char secretWord[] = "Hello";,而不是 char *secretWord = "Hello";。这两种类型是不同的,你的书应该澄清这一点。如果没有,你在看哪本书?如果你愿意,我可能会推荐一本更好的书。

任何修改字符串文字(即char *array = "Hello"; flushArray(array, ...))的尝试都是未定义的行为,正如this question 的答案所解释的那样(我敢肯定)。


似乎可以通过使用something like this来解决这个问题...

【讨论】:

  • 对于我对 memsetstrncpy 函数缺乏了解,我深表歉意,因为我是一名学习者。我仍在弄清楚这种语言是如何工作的,并对教学持开放态度。我没有意识到我的 MCVE 并没有太长和不完整......我有所有的代码来重现问题,没有 #include 语句和我在倒数第三个中描述的 main 函数的三行段落。
  • 是的,我在 Google 上使用了许多不同的措辞寻找解决方案,甚至直接使用了具有多种不同措辞的 Stack Overflow 搜索。我找不到任何东西专门解决像我这样的问题,因此我最终发表了这篇文章。您链接的四个可能重复的问题并未涵盖我遇到的相同问题,但感谢您尝试帮助我找到可以帮助我的资源。我的程序中没有出现错误消息。它只是不断提示“不允许使用任何符号......”并一遍又一遍地请求新的输入,无论你给它什么输入。
【解决方案3】:

在回应您的评论时,您可能会让自己变得比需要的更严厉。你有两个问题要处理(一个你没有看到)。首先是检查输入以验证仅输入a-zA-Z0-9。 (你知道的)。第二个是您需要识别并删除'\n' 读取并包含在您的输入中的尾随 fgets。 (那个可能会绊倒你)

您没有显示最初的array 是如何填充的,但鉴于您在secretWord 上使用fgets[1],我怀疑您也在使用fgets array。这正是您应该使用的。但是,在调用checkforsymbols 之前,您需要删除包含在由fgets 填充的缓冲区末尾的'\n'。否则你最后有字符0xa'\n'),当然不是a-zA-Z0-9,这会导致你的检查失败。

要删除尾随的'\n',您需要做的就是检查缓冲区中的最后一个字符。如果它是 '\n',那么只需用 nul-terminating 字符(0 或等效的字符表示 '\0' - 您的选择)覆盖它。您只需要字符串的length(从string.h 获得strlen),然后检查if (string[len - 1] == '\n')。例如:

    size_t len = strlen (str);          /* get length of str */
    if (str[len - 1] == '\n')           /* check for trailing '\n' */
        str[--len] = 0;                 /* overwrite with nul-byte */

第三个重要但与比较不直接相关的问题是始终为您的函数选择type,该函数将根据需要返回成功/失败指示。在您的情况下,void 的选择不会让您检查以确定是否找到任何符号。您可以选择任何您喜欢的类型intcharchar * 等。所有这些都将允许返回一个值来衡量成功或失败。对于测试字符串,通常的选择是char *,成功时返回有效指针,失败时返回NULL

接受输入时的第四个问题是,您总是需要处理用户选择通过在 Linux 上使用 ctrl+dctrl 生成手册 EOF 来取消输入的情况+z 在windoze上。 fgetsNULL 的回归为您提供了这种能力。但是使用它(以及所有其他输入函数),您必须检查返回并利用返回信息来验证用户输入。只需检查fgets 是否根据您的输入请求返回NULL,例如

    if (!fgets (str, MAXS, stdin)) {    /* read/validate input */
        fprintf (stderr, "EOF received -> user canceled input.\n");
        return 1;   /* change as needed */
    }

对于您只需要a-zA-Z0-9 的特定情况,您需要做的就是遍历用户输入的字符串,检查每个字符以确保它是a-zA-Z0-9,如果遇到任何其他问题则返回失败。考虑到 C 中的每个 string 都是 nul-terminated,这很容易。因此,您只需分配一个指向字符串开头的指针(例如char *p = str;),然后使用forwhile 循环来检查每个字符,例如

for (; *p != 0; p++) { do stuff }

可以简写:

for (; *p; p++) { do stuff }

或使用while:

while (*p) { do stuff; p++; }

将所有这些部分放在一起,您可以编写函数以将字符串作为其唯一参数,如果遇到符号则返回 NULL,或者在成功时返回指向原始字符串的指针,例如

char *checkforsymbols (char *s)
{
    if (!s || !*s) return NULL;     /* validate string and not empty  */
    char *p = s;                    /* pointer to iterate over string */

    for (; *p; p++)     /* for each char in s */
        if ((*p < 'a' || *p > 'z') &&   /* char is not a-z */
            (*p < 'A' || *p > 'Z') &&   /* char is not A-Z */
            (*p < '0' || *p > '9')) {   /* char is not 0-9 */
            fprintf (stderr, "error: '%c' not allowed in input.\n", *p);
            return NULL;    /* indicate failure */
        }

    return s;   /* indicate success */
}

一个简短的完整测试例程可以是:

#include <stdio.h>
#include <string.h>

#define MAXS 256

char *checkforsymbols (char *s);

int main (void) {

    char str[MAXS] = "";
    size_t len = 0;

    for (;;) {                              /* loop until str w/o symbols */
        printf (" enter string: ");         /* prompt for user input */
        if (!fgets (str, MAXS, stdin)) {    /* read/validate input */
            fprintf (stderr, "EOF received -> user canceled input.\n");
            return 1;
        }
        len = strlen (str);                 /* get length of str */
        if (str[len - 1] == '\n')           /* check for trailing '\n' */
            str[--len] = 0;                 /* overwrite with nul-byte */
        if (checkforsymbols (str))          /* check for symbols */
            break;
    }

    printf (" valid str: '%s'\n", str);

    return 0;
}

char *checkforsymbols (char *s)
{
    if (!s || !*s) return NULL;     /* validate string and not empty  */
    char *p = s;                    /* pointer to iterate over string */

    for (; *p; p++)     /* for each char in s */
        if ((*p < 'a' || *p > 'z') &&   /* char is not a-z */
            (*p < 'A' || *p > 'Z') &&   /* char is not A-Z */
            (*p < '0' || *p > '9')) {   /* char is not 0-9 */
            fprintf (stderr, "error: '%c' not allowed in input.\n", *p);
            return NULL;    /* indicate failure */
        }

    return s;   /* indicate success */
}

使用/输出示例

$ ./bin/str_chksym
 enter string: mydoghas$20worthoffleas
error: '$' not allowed in input.
 enter string: Baddog!
error: '!' not allowed in input.
 enter string: Okheisagood10yearolddog
 valid str: 'Okheisagood10yearolddog'

或者如果用户取消用户输入:

$ ./bin/str_chksym
 enter string: EOF received -> user canceled input.

脚注 1。

C 通常更喜欢使用所有小写 变量名,同时保留所有大写 用于宏和定义。为 C++ 和 java 保留 MixedCasecamelCase 变量名。但是,由于这是一个风格问题,这完全取决于你。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-02-03
    • 1970-01-01
    • 2010-11-14
    • 2014-11-17
    • 1970-01-01
    • 2010-10-11
    • 1970-01-01
    相关资源
    最近更新 更多