【问题标题】:Is there a function that can convert numbers inputted from char array to an int array?是否有可以将从 char 数组输入的数字转换为 int 数组的函数?
【发布时间】:2019-01-23 02:30:26
【问题描述】:

我正在尝试通过重载函数传递数组。该数组是由空格分隔的一串数字。我正在尝试将 char 数组转换为 int 数组,因为我有另一个函数正在使用指向 char 的指针调用。

这是我目前的解决方案。我尝试将每个元素单独复制到一个 int 数组,但是当我尝试从该数组中读取时,我得到了一堆垃圾数字。 它传递给我创建的正确的重载函数。

  char str[100];             //string with inputted ints
int numStr[100];
int i = 0;
int count = 0;

fgets(str, 100, stdin);

while (str[i] != '\0')
{
    if (str[i] == ' ')
    {
        count++;
    }
    i++;
}
if (count >= 1)       //checks if there is more then one int
{
    for (int i = 0; str[i] != '\0'; i++)
    {
        numStr[i] = str[i];
        numStr[i] = numStr[i] - '/0';
    }
    printf("Going into the int array function\n");
    assessGrade(numStr);    //passing int array
}

程序应该将空格之间的每个数字一起复制到另一个 int 数组中。

【问题讨论】:

  • 使用strtok分隔数字,strtol转换每个数字。
  • 没有必要同时使用strtokstrtolstrtol 提供 endptr 参数以允许从单个缓冲区转换多个值。
  • 您是否希望将自己限制为仅转换单个数字0-9?或者您是否需要能够转换缓冲区内的任何整数值(例如9231867)?
  • @DavidC.Rankin 任何整数。它的等级,所以它们的范围是 0-100。
  • 在您的上下文中什么是“重载函数”?在 C 中没有重载之类的东西。

标签: c arrays char


【解决方案1】:
char str[100];
int numStr[100]; 
char *token;
int count = 0;
int spaceCounter = 0;

fgets(str, 100, stdin);

int i = 0;
while (str[i] != '\0')
{
    if (str[i] == ' ')
    {
        spaceCounter++;
    }
    i++;
}
if (spaceCounter >= 1)       //checks if there is more then one int
{
    token = strtok(str, " ");

    while (token != NULL)
    {
        //printf("%s \n", token);
        numStr[count] = atoi(token);
        token = strtok(NULL, " ");
        count++;
    }
 }

【讨论】:

  • 所以我昨晚从大家那里得到了所有信息,这就是我想出的。现在看它在我看来应该是非常直接的。我意识到获取数据并将它们分成令牌,然后使用 atoi 将它们转换为整数可以有效地工作,并且足够简单,易于理解和遍历。感谢大家的回复,对我很有帮助!
【解决方案2】:

让您的问题简单化。

首先假设我们有一个包含数字的 char 数组:char str[100]="1 2 3 4 5 6";,而不是从输入中填充它。

通过执行此操作,我们有一些小的变化:


代码

char str[100]="1 2 3 4 5 6";             //string with inputted ints
int numStr[100]={0};
int i = 0;
int count = 0;

while (str[i] != '\0')
{
    if (str[i] != ' ')
    {
        numStr[count]=str[i]-'0';
        count++;
    }
    i++;
}   
for (int i = 0; i<count; i++)
{
    printf("%d ", numStr[i]);
}

现在我们在 numStr 整数数组中从 [0:count] 索引了数字。

【讨论】:

  • 好的,这适用于不高于 10 位值的数字。如果我输入任何更高的值,那么它会将这些数字分开。即如果输入是随机的 { "23 103 45 -23 45" }
【解决方案3】:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

long *numbersToArray(const char *str, long *len) {
    const char *pos;
    char *endptr;
    long size, count, *ret, *tmp;
    size = 4;
    count = 0;
    ret = calloc(size, sizeof(long));
    if (!ret) return NULL;
    pos = str;
    while (isspace(pos[0])) ++pos;
    while (pos[0]) {
        if (count == size) {
            size += 4;
            tmp = realloc(ret, size * sizeof(long));
            if (tmp) ret = tmp;
            else {
                free(ret);
                return NULL;
            }
        }
        ret[count] = strtol(pos, &endptr, 0);
        // if (errno) ...
        ++count;
        pos = endptr;
        while (isspace(pos[0])) ++pos;
    }
    if (len) *len = count;
    return ret;
}

int main(int argc, char **argv) {
    if (argc < 2) {
        printf("usage: %s numbers\n", argv[0]);
        return 0;
    }
    long *arr, len, i;
    arr = numbersToArray(argv[1], &len);
    for (i = 0L; i < len; ++i) {
        printf("%ld\n", arr[i]);
    }
    return 0;
}

【讨论】:

    【解决方案4】:

    继续您对能够将缓冲区中的任何整数值表示形式转换为整数的评论,strtol 没有替代品。虽然您可以使用sscanf 使用%n 说明符来获取消耗的字符数以了解指针前进多少——为什么?除了成功/失败之外,您的任何scanf 转换几乎都没有错误报告功能。 strtol 具有内置的能力,可以在你使用时将缓冲区转换为 long

    怎么样? strtol 的原型是:

    long int strtol(const char *nptr, char **endptr, int base);
    

    如果包含数字的字符串由nptr 提供,endptr 将设置为在成功转换后使用的最后一个数字之后的一个字符,base 提供转换数字的基数(例如 base 2, 8, 10, 16 等),其中0 的基数将允许从八进制、十进制或十六进制转换,具体取决于字符串表示的开头是0(八进制)还是0x(十六进制)或1-9(十进制)。

    因此,如果您给strtol 一个包含数字的字符串,则在成功转换时,返回一个long 值,可以针对INT_MIN/INT_MAX 进行测试以确定它是否在int、@ 的范围内987654342@ 将设置为最后一位转换后的数字(设置为用于下一个值),您只需设置p = endptr; 并继续。

    此外,您知道nptr == endptr 在转换之后 - 没有数字被转换。 strtol 还将errno 设置为上溢/下溢,因此您可以验证是否成功转换为long。此外,如果您想检查接下来会发生什么,endptr 指向缓冲区中包含的其余内容的开始字符。您知道转换过程中发生了什么以及需要转换的内容。

    那么你如何使用它呢?这真的很简单。首先让我们为数组中的整数个数和缓冲区中的最大字符数定义几个常量,例如

    #include <stdio.h>
    #include <stdlib.h> /* for strtol   */
    #include <limits.h> /* for INT_MIN/INT_MAX */
    #include <errno.h>  /* for errno    */
    
    #define ARSZ  100
    #define MAXC 1024
    

    现在声明你的数组,你的缓冲区来保存你的输入,和一个计数器来跟踪转换和存储在数组中的整数值的数量:

        int arr[ARSZ] = {0};
        char buf[MAXC] = "";
        size_t n = 0;
    

    现在让我们使用作为程序第一个参数提供的文件名打开一个文件进行读取(如果没有提供参数,则默认从stdin 读取)并验证我们有一个有效的打开文件流,例如

        /* use filename provided as 1st argument (stdin by default) */
        FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
    
        if (!fp) {  /* validate file open for reading */
            perror ("file open failed");
            return 1;
        }
    

    现在是时候着手处理读取输入行并将任何字符引用转换为数值到 longstrtol 的业务了。首先,使用fgets 将一行输入读入您的缓冲区并声明您的nptr(缩短为p)、endptr 和一个临时的longtmp 以保存来自strtol 的返回,例如

        while (fgets (buf, MAXC, fp)) {     /* read each line of input */
            char *p = buf, *endptr;         /* nptr & endptr for strtol */
            long tmp;                       /* temp long for strtol */
    

    使用一行输入,您现在继续使用 strtol 循环转换值,验证 (1) 数字已转换,(2) 没有发生上溢/下溢(例如,适合 long 的值),( 3) 该值在int 的范围内,或 (4) 警告该值超出int 的大小。 (后面会讲到nextdigit这个辅助函数):

            /* protect array bounds, loop while not end of buf */
            while (n < ARSZ && *p && *p != '\n') {
                errno = 0;                      /* reset errno each iteration */
                tmp = strtol (p, &endptr, 0);   /* call strtol, update endptr */
    
                if (p == endptr)    /* validate digits converted */
                    fputs ("error: no digits converted.\n", stderr);
                else if (errno)     /* validate conversion */
                    fputs ("error: over/underflow occurred.\n", stderr);
                /* validate tmp is in range of integer */
                else if (INT_MIN <= tmp && tmp <= INT_MAX)
                    arr[n++] = tmp;
                else
                    fputs ("error: value exceeds range of int.\n", stderr);
    
                if (!(p = (char *)nextdigit (endptr)))  /* get next digit */
                    break;
            }
    

    (注意:while 循环的条件,n &lt; ARSZ——不要存储超过你有空间的整数,而*p &amp;&amp; *p != '\n'(你不在末尾字符串,例如 nul-character 或换行符)。你可以省略换行符检查,它会在循环体中被无害地处理,但是为什么?一个简单的检查避免了循环体完全循环)

    什么是nextdigit() 辅助函数?从您对strtol(3) - Linux manual page 的阅读中,您知道strtol 将跳过任何前导空格直到要转换的下一个数字的开头,但是如果您有一个逗号分隔的文件,或者您的数字之间有其他字符怎么办?处理起来非常简单,只需在缓冲区中向前扫描,检查字符直到找到下一个0-9,或者找到下一个+/-,而显式符号之后的下一个字符是0-9

    这就是nextdigit 所做的一切,返回下一个要转换的值的开头地址,如果没有其他数字要转换,则返回NULL

    /* scan forward in 'p' to find next valid signed integer beginning */
    const char *nextdigit (const char *p)
    {
        while (*p) {
            if (('0' <= *p && *p <= '9') || 
                ((*p == '-' || *p == '+') && '0' <= *(p + 1) && *(p + 1) <= '9'))
                return p;
            p++;
        }
        return NULL;
    }
    

    (注意:您可以并且应该包含ctype.h 并将('0' &lt;= *p &amp;&amp; *p &lt;= '9') 的检查替换为简单的isdigit(*p),与isdigit(*(p+1)) 相同,但完整的手册测试仅用于说明目的)

    注意代码中endptr(前向扫描的起始地址)如何传递给nextdigit(),结果是分配给p,并且在下一次迭代开始之前验证不是NULL(使用p 准备再次传递给 strtol 以进行下一次转换)- 冲洗并重复,直到缓冲区中的字符用完。

    以这种方式将缓冲区转换为整数值可以让您挑选并转换行中的所有整数值——无论格式多么混乱。

    总之你可以做到:

    #include <stdio.h>
    #include <stdlib.h> /* for strtol   */
    #include <limits.h> /* for INT_MIN/INT_MAX */
    #include <errno.h>  /* for errno    */
    
    #define ARSZ  100
    #define MAXC 1024
    
    /* scan forward in 'p' to find next valid signed integer beginning */
    const char *nextdigit (const char *p)
    {
        while (*p) {
            if (('0' <= *p && *p <= '9') || 
                ((*p == '-' || *p == '+') && '0' <= *(p + 1) && *(p + 1) <= '9'))
                return p;
            p++;
        }
        return NULL;
    }
    
    int main (int argc, char **argv) {
    
        int arr[ARSZ] = {0};
        char buf[MAXC] = "";
        size_t n = 0;
        /* use filename provided as 1st argument (stdin by default) */
        FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
    
        if (!fp) {  /* validate file open for reading */
            perror ("file open failed");
            return 1;
        }
    
        while (fgets (buf, MAXC, fp)) {     /* read each line of input */
            char *p = buf, *endptr;         /* nptr & endptr for strtol */
            long tmp;                       /* temp long for strtol */
    
            /* protect array bounds, loop while not end of buf */
            while (n < ARSZ && *p && *p != '\n') {
                errno = 0;                      /* reset errno each iteration */
                tmp = strtol (p, &endptr, 0);   /* call strtol, update endptr */
    
                if (p == endptr)    /* validate digits converted */
                    fputs ("error: no digits converted.\n", stderr);
                else if (errno)     /* validate conversion */
                    fputs ("error: over/underflow occurred.\n", stderr);
                /* validate tmp is in range of integer */
                else if (INT_MIN <= tmp && tmp <= INT_MAX)
                    arr[n++] = tmp;
                else
                    fputs ("error: value exceeds range of int.\n", stderr);
    
                if (!(p = (char *)nextdigit (endptr)))  /* get next digit */
                    break;
            }
        }
        if (fp != stdin) fclose (fp);   /* close file if not stdin */
    
        for (size_t i = 0; i < n; i++) {    /* output results */
            if (i && i %10 == 0)            /* 10-values per row */
                putchar ('\n');
            printf (" %4d", arr[i]);
        }
        putchar ('\n');     /* tidy up with newline */
    }
    

    现在让我们看看这种方法可以处理转换为整数值的行,例如

    输入文件示例

    空格分隔值:

    $ cat dat/10int_space.txt
    8572 -2213 6434 16330 3034 12346 4855 16985 11250 1495
    

    换行分隔值:

    $ cat dat/10int_nl.txt
    8572
    -2213
    6434
    16330
    3034
    12346
    4855
    16985
    11250
    1495
    

    混乱中的整数:

    $ cat dat/10intmess.txt
    8572,;a -2213,;--a 6434,;
    a- 16330,;a
    
    - The Quick
    Brown%3034 Fox
    12346Jumps Over
    A
    4855,;*;Lazy 16985/,;a
    Dog.
    11250
    1495
    

    使用/输出示例

    转换例程如何公平?

    空格分隔:

    $ ./bin/fgets_strtol_any_fixed <dat/10int_space.txt
     8572 -2213 6434 16330 3034 12346 4855 16985 11250 1495
    

    换行符分隔:

    $ ./bin/fgets_strtol_any_fixed <dat/10int_nl.txt
     8572 -2213 6434 16330 3034 12346 4855 16985 11250 1495
    

    邪恶的混乱:

    $ ./bin/fgets_strtol_any_fixed <dat/10intmess.txt
    error: no digits converted.
    error: no digits converted.
    error: no digits converted.
    error: no digits converted.
    error: no digits converted.
     8572 -2213 6434 16330 3034 12346 4855 16985 11250 1495
    

    所有值,无论是空格分隔、换行分隔,还是散布在“A quick brown fox...”中间,均已正确转换。

    花时间消化strtol 的手册页,然后消化上面的实现方式。一旦你掌握了strtol(和strtoul(对于unsigned long)、strtod(对于double)等等......它们的工作原理都非常相似,几乎没有数字转换可以' t 处理。如果您还有其他问题,请告诉我。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-11-09
      • 1970-01-01
      • 2014-03-02
      • 1970-01-01
      • 1970-01-01
      • 2016-08-13
      • 2013-05-16
      • 2020-11-12
      相关资源
      最近更新 更多