Determine number or character in textfile C [closed]

风格不统一 提交于 2021-02-04 08:42:45

问题


I have a textfile with these following numbers and characters inside of it.

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

Now, i want to get the first integer which is 36 and subtract it on the last integer which is 8. I want to do this line by line.


回答1:


You want to read in the line, parse the numbers at the beginning and end, then convert them to integers. Here is a simple example:

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



回答2:


You already have a good answer for the file posted as part of your question, however, the result of first - last will be incorrect if you line of text contains a '-' sign before the digits indicating a negative signed value. All of the C string to integer conversions will accept a leading +/- before the value to be converted indicating a positive or negative number. If your input can contain negative values, you need to preserve the '-' sign by including that with the digits to be converted. For example if your input file was:

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

The answers would be quite different with -8, -42 and -5 as the integer values from the file. Now if that isn't a possibility based on your specific assignment, then you can skip preserving the '-', but for read-world text conversion to signed values, it is critical.

One way of finding the beginning of a signed number for conversion in a string is simply scan forward in the string (similar to what strpbrk() would do) looking for either a beginning digit or '-' (whichever occurs first). If the '-' occurs first, you then check that the next character is a digit. You can do it with a simple loop, e.g.

/** 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;
}

Once you have found the beginning of a digit, then you need to use a function that provides error-checking on the conversion. atoi() provides zero diagnostics for the conversion and will silently return 0 as a valid number for the conversion of atoi("my cow"); You will have no indication whether digits were actually converted or whether the result exceeds the storage size for all integer types. atoi() issues no errors at all, even if provided with a 200 digit string as input. At minimum, use sscanf that will at least provide a yes/no, true/false as to whether a valid conversion took place, or better, use strtol which provides full error reporting on the conversion.

For example, you can write a short function that take the address of a pointer to string, use nextdigit() function above, and then use strtol to fully validate the result, setting errno for validation back in the caller on any error and returning the results of the integer conversion (or 0 on error) as follows:

/** 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: the address of the pointer is passed so that the pointer can be updated within the function to the beginning of the next integer to convert in the string (or NULL if no more remain)

To complete the example, you can add the needed headers and write a short main() to read from your filename provided as the first argument (or read from stdin by default if no argument is provided) that will locate the first and last integers in each line and subtract first - last outputting the result:

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

Example Input Files

Your original input file:

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

And another with negative values and additional extraneous lines:

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

Example Use/Output

$ ./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

When run on the file with negative values and the extraneous line:

$ ./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

As you can see from the result of the subtractions between the different files, it makes a great deal of difference whether you preserve the leading '-' when converting signed values. Something to consider going forward.

Look things over and let me know if you have additional questions.



来源:https://stackoverflow.com/questions/60273294/determine-number-or-character-in-textfile-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!