确定文本文件 C 中的数字或字符

Determine number or character in textfile C

我有一个包含以下数字和字符的文本文件。

36@xL!?
28?>
42<pX%7
37@#5
31kL%^?>\<#%5

现在,我想得到第一个整数 36,然后用它减去最后一个整数 8。我想逐行执行此操作。

你想读入一行,解析开头和结尾的数字,然后将它们转换为整数。这是一个简单的例子:

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

main()
{
    FILE *file = fopen("input.txt", "r");
    char line[256];

    while (fgets(line, sizeof(line), file))
    {
    char num[15];
    int firstNumber = 0;
    int secondNumber = 0;

    line[strcspn(line, "\r\n")] = 0;

    for (int x = 0; x < 256; x++)
    {
        if (isdigit(line[x]))
        {
            num[x] = line[x];
        } 
        else 
        {
            num[x] = 0;
            break;
        }
    }        
    firstNumber = atoi(num);

    int length = strlen(line);
    int ndx = 0;
    while (length >=0 && isdigit(line[length - 1]))
    {
        num[ndx] = line[length - 1];
        ndx++;
        length--;
    }
    num[ndx] = 0;
    secondNumber = atoi(num);

    printf("%d - %d = %d\n", firstNumber, secondNumber, firstNumber - secondNumber);
    }

    fclose(file);
}

对于作为问题的一部分发布的文件,您已经有了一个很好的答案,但是,如果您的文本行在数字前包含 '-' 符号,first - last 的结果将不正确表示负符号值。所有 C 字符串到整数的转换都将在要转换的值之前接受前导 +/-,指示正数或负数。如果您的输入可以包含负值,则需要通过将 '-' 符号包含在要转换的数字中来保留该符号。例如,如果您的输入文件是:

36@xL!?\-8
28?&gt;
-42&lt;pX%7
37@#-5
31kL%^?&gt;\&lt;#%5

如果 -8, -42-5 作为文件中的整数值,答案将大不相同。现在,如果根据您的具体分配,这不可能,那么您可以跳过保留 '-',但对于 read-world 将文本转换为有符号值,这很关键。

在字符串中查找用于转换的带符号数字的开头的一种方法是简单地在字符串中向前扫描(类似于 strpbrk() 所做的)查找开头数字或 '-'(以先发生者为准)。如果 '-' 先出现,则检查下一个字符是否为数字。你可以用一个简单的循环来完成,例如

/** scan forward in 'p' to find beginning of next valid signed integer.
 *  returns pointer to beginning of signed int on success, NULL otherwise.
 */
const char *nextdigit (const char *p)
{
    while (*p) {
        /* if digit or +/- followed by a digit */
        if (isdigit(*p) ||
            ((*p == '-' || *p == '+') && isdigit(*(p + 1))))
            return p;
        p++;
    }
    return NULL;
}

一旦找到数字的开头,就需要使用提供 error-checking 转换的函数。 atoi() 为转换提供零诊断,并会默默地 return 0 作为转换 atoi("my cow"); 的有效数字 您将不知道数字是否实际转换或是否结果超出了所有整数类型的存储大小。 atoi() 根本不会发出任何错误,即使提供了 200 位数字的字符串作为输入也是如此。至少,使用 sscanf 至少会提供 yes/notrue/false 关于是否发生有效转换,或者更好的是,使用 strtol 提供完整的错误报告转换。

例如,您可以编写一个获取字符串指针地址的短函数,使用上面的 nextdigit() 函数,然后使用 strtol 完全验证结果,设置 errno 用于在调用方返回任何错误并 returning 整数转换结果(或 0 错误),如下所示:

/** returns next integer in string pointed to by p, or sets errno and returns
 *  zero on error.
 */
int getnextint (char **p)
{
    int nextint = 0;

    errno = 0;
    if ((*p = (char*)nextdigit(*p))) {
        char *endptr;
        long tmp = strtol (*p, &endptr, 0);

        if (*p == endptr) { /* validate digits converted */
            fputs ("error: no digits converted.\n", stderr);
            errno = EINVAL;
        }
        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)
            nextint = tmp;
        else {
            fputs ("error: value exceeds range of int.\n", stderr);
            errno = ERANGE;
        }
        *p = (char*)nextdigit(endptr);
    }
    else
        errno = EINVAL;     /* if no digits found, set EINVAL */

    return nextint;
}

(注意:指针的地址被传递,以便指针可以在函数内更新到下一个整数的开头,以在字符串中转换(或NULL 如果没有了)

要完成示例,您可以添加所需的 headers 并编写一个简短的 main() 以从作为第一个参数提供的文件名中读取(或默认从 stdin 中读取如果没有提供参数),它将定位每行中的第一个和最后一个整数并减去 first - last 输出结果:

#include <stdio.h>
#include <stdlib.h> /* for strtol   */
#include <string.h> /* for strcspn */
#include <limits.h> /* for INT_MIN/INT_MAX */
#include <errno.h>  /* for errno    */
#include <ctype.h>  /* for isdigit */

#define ARSZ  100
#define MAXC 1024
...                                  /* insert functions here */
int main (int argc, char **argv) {

    char buf[MAXC] = "";
    /* 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 */
        int arr[ARSZ] = {0};
        char *p = buf;
        size_t n = 0;
        buf[strcspn(buf, "\r\n")] = 0;
        while (n < ARSZ && p) {
            arr[n] = getnextint (&p);
            if (!errno)
                n++;
        }
        if (n > 1)
            printf ("%-19s  :  % 2d - % 2d = % 3d\n", 
                    buf, *arr, arr[n-1], *arr - arr[n-1]);
        else
            fprintf (stderr, "%zu integer(s) in: '%s'\n", n, buf);
    }
    if (fp != stdin)   /* close file if not stdin */
        fclose (fp);
}

示例输入文件

您的原始输入文件:

$ cat dat/last-first.txt
36@xL!?
28?&gt;
42&lt;pX%7
37@#5
31kL%^?&gt;\&lt;#%5

另一个带有负值和额外的无关行:

$ cat dat/last-first2.txt
36@xL!?\-8
28?&gt;
-42&lt;pX%7
Nothing to see!
37@#-5
31kL%^?&gt;\&lt;#%5

示例Use/Output

$ ./bin/fgets_strtol_any_last-first dat/last-first.txt
36@xL!?            :   36 -  8 =  28
28?&gt;            :   28 -  4 =  24
42&lt;pX%7           :   42 -  7 =  35
37@#5                :   37 -  5 =  32
31kL%^?&gt;\&lt;#%5  :   31 -  5 =  26

当 运行 文件上有负值和无关行时:

$ ./bin/fgets_strtol_any_last-first dat/last-first2.txt
36@xL!?\-8           :   36 - -8 =  44
28?&gt;            :   28 -  4 =  24
-42&lt;pX%7          :  -42 -  7 = -49
0 integer(s) in: 'Nothing to see!'
37@#-5               :   37 - -5 =  42
31kL%^?&gt;\&lt;#%5  :   31 -  5 =  26

从不同文件之间相减的结果可以看出,在转换有符号值时是否保留前导'-'会有很大的不同。需要考虑的事情。

检查一下,如果您还有其他问题,请告诉我。