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

问题描述 投票:0回答:2

我有一个文本文件,其中包含以下这些数字和字符。

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

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

c gcc text-files file-handling
2个回答
0
投票

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

#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);
}

0
投票

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

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

答案将与文件中的整数值-8, -42-5完全不同。现在,如果根据您的指定任务不可行,则可以跳过保留'-'的步骤,但是对于将阅读世界的文本转换为带符号的值,这至关重要。

查找字符串中要转换的带符号数字的开始的一种方法是简单地向前扫描字符串(类似于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;
}

一旦找到数字的开头,则需要使用一个提供对转换进行错误检查的功能。 atoi()为转换提供零诊断,并且将以静默方式将0返回为atoi("my cow");转换的有效数字。您将无法指示数字是否已实际转换或结果是否超过所有整数类型的存储大小。即使提供了200位字符串作为输入,atoi()也不会发出任何错误。至少,使用sscanf至少会提供yes/notrue/false有关是否进行了有效的转换,或者更好的是,使用strtol会提供有关该转换的完整错误报告。

例如,您可以编写一个短函数,该函数使用指向字符串的指针的地址,在上面使用nextdigit()函数,然后使用strtol来完全验证结果,并在调用方中将errno设置为验证任何错误并返回整数转换的结果(或错误时返回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;
}

(<< [note:传递了指针的地址,以便可以在函数内将指针更新为要转换为字符串的下一个整数的开头(如果没有剩余,则为NULL)] >>为了完成示例,您可以添加所需的标头,并写一个简短的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!?\8 28?&gt;\4 42&lt;pX%7 37@#5 31kL%^?&gt;\&lt;#%5

还有另一个带有负值和其他多余的行:

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

示例使用/输出

$ ./bin/fgets_strtol_any_last-first dat/last-first.txt 36@xL!?\8 : 36 - 8 = 28 28?&gt;\4 : 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;\4 : 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

正如您从不同文件之间的减法结果所看到的,在转换带符号值时是否保留前导'-'会产生很大的不同。需要考虑的事情。

仔细检查,如果还有其他问题,请告诉我。

© www.soinside.com 2019 - 2024. All rights reserved.