我试图让用户将命令行参数放入数组中,但我不确定如何处理它。比如说我这样运行程序。./程序1、2、3、4、5我如何存储没有逗号的1 2 3 4 5,并允许它传递给其他函数使用,我确信这与使用argv有关。PS:没有空格分隔,我想把数字解析成整数,我有一个200的数组,我想把这些数字存储在数组中,arr[0] = 1,arr[1] = 2....存储不带逗号的1 2 3 4 5,并允许将其传递给其他函数使用。
dl5txlt91#
PS:没有空格分隔,我想把数字解析成整数空格或逗号分隔并不重要。参数总是以字符串形式出现。你必须使用atoi(Ascii-TO-Integer)将它们转换为整数。在参数之间使用空格是常规约定:./program 1 2 3 4 5。它们在argv中已经分开。循环argv(跳过argv[0],程序名),并通过atoi运行它们。
atoi
./program 1 2 3 4 5
argv
#include <stdio.h>#include <stdlib.h>int main(int argc, char *argv[]) { for(int i = 1; i < argc; i++) { int num = atoi(argv[i]); printf("%d: %d\n", i, num); }}
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
for(int i = 1; i < argc; i++) {
int num = atoi(argv[i]);
printf("%d: %d\n", i, num);
}
使用逗号会使这变得更困难,首先必须使用奇怪的strtok(STRing TOKenizer)来拆分字符串,然后对结果值再次调用atoi。
strtok
#include <stdio.h>#include <stdlib.h>#include <string.h>int main(int argc, char *argv[]) { char *token = strtok(argv[1], ","); while(token) { int num = atoi(token); printf("%d\n", num); token = strtok(NULL, ","); }}
#include <string.h>
char *token = strtok(argv[1], ",");
while(token) {
int num = atoi(token);
printf("%d\n", num);
token = strtok(NULL, ",");
这种方法也比把它们作为单独的参数更脆弱。如果用户键入./program 1, 2, 3, 4, 5,则只会读取1。
./program 1, 2, 3, 4, 5
1
y4ekin9u2#
使用atoi()的一个主要缺点是它不提供对正在处理的字符串的检查,并且会很高兴地接受atoi ("my-cow");,并且静默地失败返回0,而不显示任何问题。虽然有点复杂,但是使用strtol()可以让你确定失败的地方,然后恢复。这可以是你的设计所要求的简单或深入的恢复。正如在注解中提到的,strtol()被设计为处理字符串,将字符串中找到的数字集转换为数值。每次调用时,它都会更新endptr参数,以指向字符串中最后一个转换的数字之后的下一个字符(在您的例子中是每个','--或者结尾的空终止字符)。man 3 strtol提供了详细信息。由于strtol()将endptr更新为最后一个转换数字之后的字符,因此您需要检查nptr == endptr是否在没有转换数字时捕获错误。您需要检查errno是否存在数字转换错误(如溢出)。最后,因为返回类型是long,所以在赋值给int数组之前,需要检查返回的值是否在int的范围内。把它与一个非常小的错误处理放在一起,你可以做这样的事情:
atoi()
atoi ("my-cow");
0
strtol()
endptr
','
nptr == endptr
errno
long
int
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <limits.h>#include <errno.h>#define NELEM 200 /* if you need a constant, #define one (or more) */int main (int argc, char **argv) { int arr[NELEM] = {0}, ndx = 0; /* array and index */ char *nptr = argv[1], *endptr = nptr; /* nptr and endptr */ if (argc < 2) { /* if no argument, handle error */ fputs ("error: no argument provided.\n", stderr); return 1; } else if (argc > 2) { /* warn on more than 2 arguments */ fputs ("warning: more than one argument provided.\n", stdout); } while (ndx < NELEM) { /* loop until all ints processed or arr full */ int error = 0; /* flag indicating error occured */ long tmp = 0; /* temp var to hold strtol return */ char *onerr = NULL; /* pointer to next comma after error */ errno = 0; /* reset errno */ tmp = strtol (nptr, &endptr, 0); /* attempt conversion to long */ if (nptr == endptr) { /* no digits converted */ fputs ("error: no digits converted.\n", stderr); error = 1; onerr = strchr (endptr, ','); } else if (errno) { /* overflow in conversion */ perror ("strtol conversion error"); error = 1; onerr = strchr (endptr, ','); } else if (tmp < INT_MIN || INT_MAX < tmp) { /* check in range of int */ fputs ("error: value outside range of int.\n", stderr); error = 1; onerr = strchr (endptr, ','); } if (!error) { /* error flag not set */ arr[ndx++] = tmp; /* assign integer to arr, advance index */ } else if (onerr) { /* found next ',' update endptr to next ',' */ endptr = onerr; } else { /* no next ',' after error, break */ break; } /* if at end of string - done, break loop */ if (!*endptr) { break; } nptr = endptr + 1; /* update nptr to 1-past ',' */ } for (int i = 0; i < ndx; i++) { /* output array content */ printf (" %d", arr[i]); } putchar ('\n'); /* tidy up with newline */}
#include <limits.h>
#include <errno.h>
#define NELEM 200 /* if you need a constant, #define one (or more) */
int main (int argc, char **argv) {
int arr[NELEM] = {0}, ndx = 0; /* array and index */
char *nptr = argv[1], *endptr = nptr; /* nptr and endptr */
if (argc < 2) { /* if no argument, handle error */
fputs ("error: no argument provided.\n", stderr);
return 1;
else if (argc > 2) { /* warn on more than 2 arguments */
fputs ("warning: more than one argument provided.\n", stdout);
while (ndx < NELEM) { /* loop until all ints processed or arr full */
int error = 0; /* flag indicating error occured */
long tmp = 0; /* temp var to hold strtol return */
char *onerr = NULL; /* pointer to next comma after error */
errno = 0; /* reset errno */
tmp = strtol (nptr, &endptr, 0); /* attempt conversion to long */
if (nptr == endptr) { /* no digits converted */
fputs ("error: no digits converted.\n", stderr);
error = 1;
onerr = strchr (endptr, ',');
else if (errno) { /* overflow in conversion */
perror ("strtol conversion error");
else if (tmp < INT_MIN || INT_MAX < tmp) { /* check in range of int */
fputs ("error: value outside range of int.\n", stderr);
if (!error) { /* error flag not set */
arr[ndx++] = tmp; /* assign integer to arr, advance index */
else if (onerr) { /* found next ',' update endptr to next ',' */
endptr = onerr;
else { /* no next ',' after error, break */
break;
/* if at end of string - done, break loop */
if (!*endptr) {
nptr = endptr + 1; /* update nptr to 1-past ',' */
for (int i = 0; i < ndx; i++) { /* output array content */
printf (" %d", arr[i]);
putchar ('\n'); /* tidy up with newline */
使用/输出示例
这将处理您的正常情况,例如
$ ./bin/argv1csvints 1,2,3,4,5 1 2 3 4 5
$ ./bin/argv1csvints 1,2,3,4,5
1 2 3 4 5
它会警告列表中的错误参数,同时将所有正确的参数保存到数组中:
$ ./bin/argv1csvints 1,my-cow,3,my-cat,5error: no digits converted.error: no digits converted. 1 3 5
$ ./bin/argv1csvints 1,my-cow,3,my-cat,5
error: no digits converted.
1 3 5
以及处理完全错误的输入:
$ ./bin/argv1csvints my-cowerror: no digits converted.
$ ./bin/argv1csvints my-cow
或者根本没有争论:
$ ./bin/argv1csvintserror: no argument provided.
$ ./bin/argv1csvints
error: no argument provided.
或超过预期的1个参数:
$ ./bin/argv1csvints 1,2,3,4,5 6,7,8warning: more than one argument provided. 1 2 3 4 5
$ ./bin/argv1csvints 1,2,3,4,5 6,7,8
warning: more than one argument provided.
这里要说明的是,通过一点额外的代码,您可以使您的参数解析例程尽可能健壮。虽然您使用带有逗号分隔值的单个参数是不常见的,但它是可行的。(拆分)逗号上的数字用strtok()(或strchr()或strspn()和strcspn()组合),使用类似于"%d%n"字符串的内容以sscanf()循环,以获得最小成功/失败指示,或者使用strtol()并利用其错误报告。这由您决定。把事情看一遍,如果有问题就告诉我。
strtok()
strchr()
strspn()
strcspn()
"%d%n"
sscanf()
epggiuax3#
这就是我如何使用strtol()来处理您的要求。这不会损坏输入字符串,不像使用strtok()的解决方案。它还可以正确处理溢出和下溢,不像使用atoi()或其相关的解决方案。代码假设您要存储long类型的数组;如果要使用int,可以添加测试以查看转换后的值是大于INT_MAX还是小于INT_MIN,如果它不是有效的int值,则报告相应的错误。请注意,处理strtol()中的错误是一件棘手的事情,尤其是因为每个返回值(从LONG_MIN到LONG_MAX)也是一个有效的结果。它允许逗号后面有空格(这样你就可以运行./csa43 '1, 2, -3, 4, 5'了)。2它不允许逗号前面有空格。3它允许前导空格,但不能使用尾随空格。这些问题可以通过更多的工作来解决-可能主要是在read_value()函数中。主循环中的验证工作可能应该委托给read_value()函数-这将给予一个更好的职责分离。OTOH,这里的内容在一定的范围内工作。如果你选择允许尾随空格或逗号前的空格,这是可行的。如果你选择禁止前导空格和逗号后的空格,这也是同样可行的。
INT_MAX
INT_MIN
LONG_MIN
LONG_MAX
./csa43 '1, 2, -3, 4, 5'
read_value()
#include <errno.h>#include <limits.h>#include <stdio.h>#include <stdlib.h>static int read_val(const char *str, char **eov, long *value){ errno = 0; char *eon; if (*str == '\0') return -1; long val = strtol(str, &eon, 0); if (eon == str || (*eon != '\0' && *eon != ',') || ((val == LONG_MIN || val == LONG_MAX) && errno == ERANGE)) { fprintf(stderr, "Could not convert '%s' to an integer " "(the leftover string is '%s')\n", str, eon); return -1; } *value = val; *eov = eon; return 0;}int main(int argc, char **argv){ if (argc != 2) { fprintf(stderr, "Usage: %s n1,n2,n3,...\n", argv[0]); exit(EXIT_FAILURE); } enum { NUM_ARRAY = 200 }; long array[NUM_ARRAY]; size_t nvals = 0; char *str = argv[1]; char *eon; long val; while (read_val(str, &eon, &val) == 0 && nvals < NUM_ARRAY) { array[nvals++] = val; str = eon; if (str[0] == ',' && str[1] == '\0') { fprintf(stderr, "%s: trailing comma in number string\n", argv[1]); exit(EXIT_FAILURE); } else if (str[0] == ',') str++; } for (size_t i = 0; i < nvals; i++) printf("[%zu] = %ld\n", i, array[i]); return 0;}
static int read_val(const char *str, char **eov, long *value)
{
errno = 0;
char *eon;
if (*str == '\0')
return -1;
long val = strtol(str, &eon, 0);
if (eon == str || (*eon != '\0' && *eon != ',') ||
((val == LONG_MIN || val == LONG_MAX) && errno == ERANGE))
fprintf(stderr, "Could not convert '%s' to an integer "
"(the leftover string is '%s')\n", str, eon);
*value = val;
*eov = eon;
return 0;
int main(int argc, char **argv)
if (argc != 2)
fprintf(stderr, "Usage: %s n1,n2,n3,...\n", argv[0]);
exit(EXIT_FAILURE);
enum { NUM_ARRAY = 200 };
long array[NUM_ARRAY];
size_t nvals = 0;
char *str = argv[1];
long val;
while (read_val(str, &eon, &val) == 0 && nvals < NUM_ARRAY)
array[nvals++] = val;
str = eon;
if (str[0] == ',' && str[1] == '\0')
fprintf(stderr, "%s: trailing comma in number string\n", argv[1]);
else if (str[0] == ',')
str++;
for (size_t i = 0; i < nvals; i++)
printf("[%zu] = %ld\n", i, array[i]);
输出(从csa43.c编译的程序csa43):
csa43.c
csa43
$ csa43 1,2,3,4,5[0] = 1[1] = 2[2] = 3[3] = 4[4] = 5$
$ csa43 1,2,3,4,5
[0] = 1
[1] = 2
[2] = 3
[3] = 4
[4] = 5
$
3条答案
按热度按时间dl5txlt91#
PS:没有空格分隔,我想把数字解析成整数
空格或逗号分隔并不重要。参数总是以字符串形式出现。你必须使用
atoi
(Ascii-TO-Integer)将它们转换为整数。在参数之间使用空格是常规约定:
./program 1 2 3 4 5
。它们在argv
中已经分开。循环argv
(跳过argv[0],程序名),并通过atoi
运行它们。使用逗号会使这变得更困难,首先必须使用奇怪的
strtok
(STRing TOKenizer)来拆分字符串,然后对结果值再次调用atoi
。这种方法也比把它们作为单独的参数更脆弱。如果用户键入
./program 1, 2, 3, 4, 5
,则只会读取1
。y4ekin9u2#
使用
atoi()
的一个主要缺点是它不提供对正在处理的字符串的检查,并且会很高兴地接受atoi ("my-cow");
,并且静默地失败返回0
,而不显示任何问题。虽然有点复杂,但是使用strtol()
可以让你确定失败的地方,然后恢复。这可以是你的设计所要求的简单或深入的恢复。正如在注解中提到的,
strtol()
被设计为处理字符串,将字符串中找到的数字集转换为数值。每次调用时,它都会更新endptr
参数,以指向字符串中最后一个转换的数字之后的下一个字符(在您的例子中是每个','
--或者结尾的空终止字符)。man 3 strtol提供了详细信息。由于
strtol()
将endptr
更新为最后一个转换数字之后的字符,因此您需要检查nptr == endptr
是否在没有转换数字时捕获错误。您需要检查errno
是否存在数字转换错误(如溢出)。最后,因为返回类型是long
,所以在赋值给int
数组之前,需要检查返回的值是否在int
的范围内。把它与一个非常小的错误处理放在一起,你可以做这样的事情:
使用/输出示例
这将处理您的正常情况,例如
它会警告列表中的错误参数,同时将所有正确的参数保存到数组中:
以及处理完全错误的输入:
或者根本没有争论:
或超过预期的1个参数:
这里要说明的是,通过一点额外的代码,您可以使您的参数解析例程尽可能健壮。虽然您使用带有逗号分隔值的单个参数是不常见的,但它是可行的。(拆分)逗号上的数字用
strtok()
(或strchr()
或strspn()
和strcspn()
组合),使用类似于"%d%n"
字符串的内容以sscanf()
循环,以获得最小成功/失败指示,或者使用strtol()
并利用其错误报告。这由您决定。把事情看一遍,如果有问题就告诉我。
epggiuax3#
这就是我如何使用
strtol()
来处理您的要求。这不会损坏输入字符串,不像使用strtok()
的解决方案。它还可以正确处理溢出和下溢,不像使用atoi()
或其相关的解决方案。代码假设您要存储long
类型的数组;如果要使用int
,可以添加测试以查看转换后的值是大于INT_MAX
还是小于INT_MIN
,如果它不是有效的int
值,则报告相应的错误。请注意,处理
strtol()
中的错误是一件棘手的事情,尤其是因为每个返回值(从LONG_MIN
到LONG_MAX
)也是一个有效的结果。它允许逗号后面有空格(这样你就可以运行./csa43 '1, 2, -3, 4, 5'
了)。2它不允许逗号前面有空格。3它允许前导空格,但不能使用尾随空格。这些问题可以通过更多的工作来解决-可能主要是在read_value()
函数中。主循环中的验证工作可能应该委托给read_value()
函数-这将给予一个更好的职责分离。OTOH,这里的内容在一定的范围内工作。如果你选择允许尾随空格或逗号前的空格,这是可行的。如果你选择禁止前导空格和逗号后的空格,这也是同样可行的。输出(从
csa43.c
编译的程序csa43
):