Use scanf to read lines or break on special charac

2019-04-28 16:51发布

问题:

Is it possible to read lines of text with scanf() - excluding \n and break on special(chosen) character, but include that character

This is my current expression: while(scanf("%49[^:\n]%*c", x)==1) but this one excludes :. Is it possible to break reading on : but read that character too?

回答1:

Ok I am using Johannes-Schaub-litb's code.

char * getline(char cp) {
    char * line = malloc(100), * linep = line;
    size_t lenmax = 100, len = lenmax;
    int c;


    if(line == NULL)
        return NULL;

    for(;;) {
        c = fgetc(stdin);
        if(c == EOF)
            break;

        if(--len == 0) {
            len = lenmax;
            intptr_t diff = line - linep;
            char * linen = realloc(linep, lenmax *= 2);

            if(linen == NULL) {
                free(linep);
                return NULL;
            }
            line = linen + diff;
            linep = linen;
        }

        if((*line++ = c) == cp)
            break;
    }
    *line = '\0';
    return linep;
}

Still I use this code ...and it works fine. The code will be modified a bit more later.



回答2:

I have done this in a little different way. Maybe this can crash on windows. But, anyways here it is. Hope it helps. Merry Christmas.

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

int main() {
    char *input_str;

    /**
     * Dynamic memory allocation.
     * Might crash on windows.
     */
    int status = scanf("%m[^.]", &input_str);

    /**
     * If the first character is the 
     * terminating character then scanf scans nothing
     * and returns 0.
     */
    if (status > 0) {
        /**
         * Calculate the length of the string.
         */
        size_t len = strlen(input_str);

        /**
         * While allocating memory provide
         * two extra cell. One for the character 
         * you want to include.
         * One for the NULL character.
         */
        char *new_str = (char*) calloc (len + 2, sizeof(char));

        /**
         * Check for memory allocation.
         */
        if(new_str == NULL) {
            printf("Memory Allocation failed\n");
            exit(1);
        }

        /**
         * Copy the string.
         */
        strcpy(new_str, input_str, len);

        /**
         * get your desired terminating character 
         * from buffer
         */
        new_str[len++] = getc(stdin);

        /**
         * Append the NULL character
         */
        new_str[len++] = '\0';

        /**
         * eat other characters
         * from buffer.
         */
        while(getc(stdin) != '\n');

        /**
         * Free the memory used in
         * dynamic memory allocation
         * in scanf. Which is a must
         * according to the scanf man page.
         */
        free(input_str);
    } else {
        char new_str[2] = ".\0";
        /**
         * eat other characters
         * from buffer.
         */
        while(getc(stdin) != '\n');
    }
}

EDIT:- I have used dot as a terminating character.



回答3:

Is it possible to read lines of text with scanf() - excluding \n and break on special(chosen) character, but include that character(?)

Yes. But scanf() is notorious for being used wrong and difficult to use right. Certainly the scanf() approach will work for most user input. Only a rare implementation will completely meet OP's goal without missing corner cases. IMO, it is not worth it.

Alternatively, let us try the direct approach, repeatedly use fgetc(). Some untested code:

char *luvatar_readline(char *destination, size_t size, char special) {
  assert(size > 0);
  char *p = destitution;

  int ch;
  while (((ch = fgetc(stdin)) != EOF) && (ch != '\n')) {
    if (size > 1) {
      size--;  
      *p++ = ch; 
    } else {
      // Ignore extra input or 
      // TBD what should be done if no room left
    }
    if (ch == (unsigned char) special) {
      break;
    }
  }
  *p = '\0';

  if (ch == EOF) {
    // If rare input error
    if (ferror(stdin)) {
      return NULL;
    }
    // If nothing read and end-of-file  
    if ((p == destination) && feof(stdin)) {
      return NULL;
    }
  }
  return destination;
}      

Sample usage

char buffer[50];
while (luvatar_readline(buffer, sizeof buffer_t, ':')) { 
  puts(buffer);
}

Corner cases TBD: Unclear what OP wants if special is '\n' or '\0'.


OP's while(scanf("%49[^:\n]%*c", x)==1) has many problems.

  1. Does not cope with input the begins with : or '\n', leaving x unset.

  2. Does not know if the character after the non-:, non-'\n' input was a :, '\n', EOF.

  3. Does not consume extra input past 49.

  4. Uses a fixed spacial character ':', rather than a general one.



回答4:

I think that you want to do that:

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

int main(void) {
  char *line = NULL;
  size_t len = 0;
  ssize_t read;
  while ((read = getline(&line, &len, stdin)) != -1) {
    if (read > 0 && line[read - 1] == '\n') {
      if (read > 1 && line[read - 2] == '\r') {
        line[read - 2] = '\0'; // we can remove the carriage return
      }
      else {
        line[read - 1] = '\0'; // we can remove the new line
      }
    }

    char const *delim = ":";
    printf("parsing line :\n");
    char *token = strtok(line, delim);

    while (token != NULL) {
      printf("token: %s\n", token);
      token = strtok(NULL, delim);
    }
  }
  free(line);
}


标签: c scanf