C语言中的列表转换

hmtdttj4  于 2022-12-02  发布在  其他
关注(0)|答案(3)|浏览(234)

我试图让用户将命令行参数放入数组中,但我不确定如何处理它。
比如说我这样运行程序。
./程序1、2、3、4、5
我如何存储没有逗号的1 2 3 4 5,并允许它传递给其他函数使用,我确信这与使用argv有关。
PS:没有空格分隔,我想把数字解析成整数,我有一个200的数组,我想把这些数字存储在数组中,arr[0] = 1,arr[1] = 2....
存储不带逗号的1 2 3 4 5,并允许将其传递给其他函数使用。

dl5txlt9

dl5txlt91#

PS:没有空格分隔,我想把数字解析成整数
空格或逗号分隔并不重要。参数总是以字符串形式出现。你必须使用atoi(Ascii-TO-Integer)将它们转换为整数。
在参数之间使用空格是常规约定:./program 1 2 3 4 5。它们在argv中已经分开。循环argv(跳过argv[0],程序名),并通过atoi运行它们。

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. int main(int argc, char *argv[]) {
  4. for(int i = 1; i < argc; i++) {
  5. int num = atoi(argv[i]);
  6. printf("%d: %d\n", i, num);
  7. }
  8. }

使用逗号会使这变得更困难,首先必须使用奇怪的strtok(STRing TOKenizer)来拆分字符串,然后对结果值再次调用atoi

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. int main(int argc, char *argv[]) {
  5. char *token = strtok(argv[1], ",");
  6. while(token) {
  7. int num = atoi(token);
  8. printf("%d\n", num);
  9. token = strtok(NULL, ",");
  10. }
  11. }

这种方法也比把它们作为单独的参数更脆弱。如果用户键入./program 1, 2, 3, 4, 5,则只会读取1

展开查看全部
y4ekin9u

y4ekin9u2#

使用atoi()的一个主要缺点是它不提供对正在处理的字符串的检查,并且会很高兴地接受atoi ("my-cow");,并且静默地失败返回0,而不显示任何问题。虽然有点复杂,但是使用strtol()可以让你确定失败的地方,然后恢复。这可以是你的设计所要求的简单或深入的恢复。
正如在注解中提到的,strtol()被设计为处理字符串,将字符串中找到的数字集转换为数值。每次调用时,它都会更新endptr参数,以指向字符串中最后一个转换的数字之后的下一个字符(在您的例子中是每个','--或者结尾的空终止字符)。man 3 strtol提供了详细信息。
由于strtol()endptr更新为最后一个转换数字之后的字符,因此您需要检查nptr == endptr是否在没有转换数字时捕获错误。您需要检查errno是否存在数字转换错误(如溢出)。最后,因为返回类型是long,所以在赋值给int数组之前,需要检查返回的值是否在int的范围内。
把它与一个非常小的错误处理放在一起,你可以做这样的事情:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <limits.h>
  5. #include <errno.h>
  6. #define NELEM 200 /* if you need a constant, #define one (or more) */
  7. int main (int argc, char **argv) {
  8. int arr[NELEM] = {0}, ndx = 0; /* array and index */
  9. char *nptr = argv[1], *endptr = nptr; /* nptr and endptr */
  10. if (argc < 2) { /* if no argument, handle error */
  11. fputs ("error: no argument provided.\n", stderr);
  12. return 1;
  13. }
  14. else if (argc > 2) { /* warn on more than 2 arguments */
  15. fputs ("warning: more than one argument provided.\n", stdout);
  16. }
  17. while (ndx < NELEM) { /* loop until all ints processed or arr full */
  18. int error = 0; /* flag indicating error occured */
  19. long tmp = 0; /* temp var to hold strtol return */
  20. char *onerr = NULL; /* pointer to next comma after error */
  21. errno = 0; /* reset errno */
  22. tmp = strtol (nptr, &endptr, 0); /* attempt conversion to long */
  23. if (nptr == endptr) { /* no digits converted */
  24. fputs ("error: no digits converted.\n", stderr);
  25. error = 1;
  26. onerr = strchr (endptr, ',');
  27. }
  28. else if (errno) { /* overflow in conversion */
  29. perror ("strtol conversion error");
  30. error = 1;
  31. onerr = strchr (endptr, ',');
  32. }
  33. else if (tmp < INT_MIN || INT_MAX < tmp) { /* check in range of int */
  34. fputs ("error: value outside range of int.\n", stderr);
  35. error = 1;
  36. onerr = strchr (endptr, ',');
  37. }
  38. if (!error) { /* error flag not set */
  39. arr[ndx++] = tmp; /* assign integer to arr, advance index */
  40. }
  41. else if (onerr) { /* found next ',' update endptr to next ',' */
  42. endptr = onerr;
  43. }
  44. else { /* no next ',' after error, break */
  45. break;
  46. }
  47. /* if at end of string - done, break loop */
  48. if (!*endptr) {
  49. break;
  50. }
  51. nptr = endptr + 1; /* update nptr to 1-past ',' */
  52. }
  53. for (int i = 0; i < ndx; i++) { /* output array content */
  54. printf (" %d", arr[i]);
  55. }
  56. putchar ('\n'); /* tidy up with newline */
  57. }

使用/输出示例

这将处理您的正常情况,例如

  1. $ ./bin/argv1csvints 1,2,3,4,5
  2. 1 2 3 4 5

它会警告列表中的错误参数,同时将所有正确的参数保存到数组中:

  1. $ ./bin/argv1csvints 1,my-cow,3,my-cat,5
  2. error: no digits converted.
  3. error: no digits converted.
  4. 1 3 5

以及处理完全错误的输入:

  1. $ ./bin/argv1csvints my-cow
  2. error: no digits converted.

或者根本没有争论:

  1. $ ./bin/argv1csvints
  2. error: no argument provided.

或超过预期的1个参数:

  1. $ ./bin/argv1csvints 1,2,3,4,5 6,7,8
  2. warning: more than one argument provided.
  3. 1 2 3 4 5

这里要说明的是,通过一点额外的代码,您可以使您的参数解析例程尽可能健壮。虽然您使用带有逗号分隔值的单个参数是不常见的,但它是可行的。(拆分)逗号上的数字用strtok()(或strchr()strspn()strcspn()组合),使用类似于"%d%n"字符串的内容以sscanf()循环,以获得最小成功/失败指示,或者使用strtol()并利用其错误报告。这由您决定。
把事情看一遍,如果有问题就告诉我。

展开查看全部
epggiuax

epggiuax3#

这就是我如何使用strtol()来处理您的要求。这不会损坏输入字符串,不像使用strtok()的解决方案。它还可以正确处理溢出和下溢,不像使用atoi()或其相关的解决方案。代码假设您要存储long类型的数组;如果要使用int,可以添加测试以查看转换后的值是大于INT_MAX还是小于INT_MIN,如果它不是有效的int值,则报告相应的错误。
请注意,处理strtol()中的错误是一件棘手的事情,尤其是因为每个返回值(从LONG_MINLONG_MAX)也是一个有效的结果。它允许逗号后面有空格(这样你就可以运行./csa43 '1, 2, -3, 4, 5'了)。2它不允许逗号前面有空格。3它允许前导空格,但不能使用尾随空格。这些问题可以通过更多的工作来解决-可能主要是在read_value()函数中。主循环中的验证工作可能应该委托给read_value()函数-这将给予一个更好的职责分离。OTOH,这里的内容在一定的范围内工作。如果你选择允许尾随空格或逗号前的空格,这是可行的。如果你选择禁止前导空格和逗号后的空格,这也是同样可行的。

  1. #include <errno.h>
  2. #include <limits.h>
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. static int read_val(const char *str, char **eov, long *value)
  6. {
  7. errno = 0;
  8. char *eon;
  9. if (*str == '\0')
  10. return -1;
  11. long val = strtol(str, &eon, 0);
  12. if (eon == str || (*eon != '\0' && *eon != ',') ||
  13. ((val == LONG_MIN || val == LONG_MAX) && errno == ERANGE))
  14. {
  15. fprintf(stderr, "Could not convert '%s' to an integer "
  16. "(the leftover string is '%s')\n", str, eon);
  17. return -1;
  18. }
  19. *value = val;
  20. *eov = eon;
  21. return 0;
  22. }
  23. int main(int argc, char **argv)
  24. {
  25. if (argc != 2)
  26. {
  27. fprintf(stderr, "Usage: %s n1,n2,n3,...\n", argv[0]);
  28. exit(EXIT_FAILURE);
  29. }
  30. enum { NUM_ARRAY = 200 };
  31. long array[NUM_ARRAY];
  32. size_t nvals = 0;
  33. char *str = argv[1];
  34. char *eon;
  35. long val;
  36. while (read_val(str, &eon, &val) == 0 && nvals < NUM_ARRAY)
  37. {
  38. array[nvals++] = val;
  39. str = eon;
  40. if (str[0] == ',' && str[1] == '\0')
  41. {
  42. fprintf(stderr, "%s: trailing comma in number string\n", argv[1]);
  43. exit(EXIT_FAILURE);
  44. }
  45. else if (str[0] == ',')
  46. str++;
  47. }
  48. for (size_t i = 0; i < nvals; i++)
  49. printf("[%zu] = %ld\n", i, array[i]);
  50. return 0;
  51. }

输出(从csa43.c编译的程序csa43):

  1. $ csa43 1,2,3,4,5
  2. [0] = 1
  3. [1] = 2
  4. [2] = 3
  5. [3] = 4
  6. [4] = 5
  7. $
展开查看全部

相关问题