-1

Is crazy enough but there is seems to be very very difficult to read a simple line of chars from the console in C... gets, fgets, getline, scanf.... any of these functions does not seem to achieve such a simple thing, read a simple line, without the new line char...

What is that language? I follow these answers, but I don't understand how a language that is so complex to just read a string from console can exist?

Please, could someone guide me how, having a char* str, to just read in a console text...

dreamcrash
  • 47,137
  • 25
  • 94
  • 117
serge
  • 13,940
  • 35
  • 121
  • 205
  • 2
    Use `getline()` (If targeting a POSIXish system) or `fgets()`, if successful, remove the newline at the end of the string. (If using `fgets()` and there is no new line, you didn't give it a big enough buffer to read the entire line so you have to deal with that.) Never use `gets()`, the function so unsafe it was removed from the standard. – Shawn Nov 23 '20 at 14:17
  • 1
    `fgets()` is the function closest to your needs. Ok, there's that trailing newline, but removing that is just a safe (and elegant) [oneliner](https://stackoverflow.com/a/28462221/11336762) – Roberto Caboni Nov 23 '20 at 14:17
  • *"but I don't understand how a language that is so complex to just read a string from console can exist?"* - It's a very old language. And no one is forcing you to use it. ;) – klutt Nov 23 '20 at 14:43
  • 4
    Languages that make the task seem simple thereby hide a substantial amount of complexity to which you must devote attention in C. Libraries and, yes, higher-level languages are available to relieve you of attending to such low-level details by hand, at the price of not *allowing* you to control details as precisely. – John Bollinger Nov 23 '20 at 14:51
  • You could use [GNU readline](https://www.gnu.org/software/readline/) – Basile Starynkevitch Nov 23 '20 at 16:54
  • @BasileStarynkevitch: The link you posted does not work (at least not with me). But that does not seem to be your fault, it seems to be a server problem. – Andreas Wenzel Nov 23 '20 at 17:03
  • @AndreasWenzel: Try then https://tiswww.cwru.edu/php/chet/readline/rltop.html – Basile Starynkevitch Nov 24 '20 at 10:20

3 Answers3

3

The following will read a line from stdin, then remove the newline:

char buffer[260];
...
fgets(buffer, sizeof(buffer), stdin);
buffer[strcspn(buffer, "\n")] = 0;  

The C library function char *fgets(char *str, int n, FILE *stream) reads a line from the specified stream and stores it into the string pointed to by str. It stops when either (n-1) characters are read, the newline character is read, or the end-of-file is reached, whichever comes first.

To collect larger amounts of input, this construct can be called within a loop. Note that how to accumulate each successive buffer is another topic.

More on fgets()

ryyker
  • 22,849
  • 3
  • 43
  • 87
  • Supposing, of course, that the line is no longer than 259 characters, including newline. The fact that there is no fixed limit on the length of a line is in fact one of the reasons why this is not as easy in C as it is in some higher-level languages. – John Bollinger Nov 23 '20 at 14:42
  • Your edit made it worse. Argument 2 for `fgets` has the precise purpose of preventing overflow. And the invokation of `scanf` is plain wrong. – klutt Nov 23 '20 at 14:48
  • Actually, except the case of lines longer than the size of the buffer (which is secondary, from the perspective of the question), the first answer was OK, as I stated in my comment above. – Roberto Caboni Nov 23 '20 at 14:50
  • 1
    _"fgets() reads in at most one less than size characters from stream and stores them into the buffer pointed to by s"_ ([source](https://linux.die.net/man/3/fgets)). A note, explaining that in case of input lines longer than 259 chars a loop is required, will complete the answer. – Roberto Caboni Nov 23 '20 at 14:54
0

@ryker provided a good answer that explains the most of it. Here is a pretty safe implementation that is 100% portable and allows you to not bother about neither the newline, buffer overflow or allocation. But it returns a pointer that you need to free.

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

char *gline() {
    char *buffer;

    const size_t step = 10;
    size_t size = step;

    if(!(buffer = malloc(size))) goto CLEANUP;

    char *s = buffer;

    // step-1 is not strictly necessary, but some implementations of fgets
    // are buggy. That's why it's step-1 instead of just step
    while(fgets(s, step-1, stdin)) {
        if(strchr(s, '\n')) break;
        size += step;
        if(!(buffer = realloc(buffer, size))) goto CLEANUP;
        s = &buffer[strlen(buffer)];
    }

    buffer[strcspn(buffer, "\n")] = 0;
    return buffer;

CLEANUP:
    free(buffer);
    return NULL;
}

int main(void) {
    char *s = gline();
    if(!s)
        puts("Error reading line");
    else
        puts(s);
    free(s);
}

I have not finetuned it, so it's possible that it allocates a little bit more memory than necessary. It's not very efficient either, because it runs strlen in every iteration. But as long as the allocations does not fail, it's safe and easy to use.

klutt
  • 30,332
  • 17
  • 55
  • 95
0

The built in functions like scanf will do it but the problem I have with them is that you need to know ahead of time how much input you will receive. What if it's 1 character? What if it's 10000? So I prefer to dynamically allocate memory using malloc and realloc and build the string as the input is received. This is what I use when I need to read from stdin or a file. It is simple enough, probably far from perfect, it simply gets a character from stream and if it isn't the newline or EOF it adds it to the string. If the reallocation fails it frees the entire string as I need to have an "all or nothing" approach to building the string. It returns a pointer to the string or NULL or any failure.

char* get_str(FILE* stream)
{
    char *s;
    int ix = 0, sz = 1, rf = 0, ch;

    if(stream == NULL) {
        s = NULL;
    } else {
        s = (char*)malloc(sizeof(char) * sz);
        if(s != NULL) {
            s[ix] = '\0';
            ch = fgetc(stream);
            while(ch != '\n' && ch != EOF && rf == 0) {
                sz++;
                s = (char*)realloc(s, sizeof(char) * sz);
                if(s != NULL) {
                    s[ix] = ch;
                    ix++;
                    s[ix] = '\0';
                    ch = fgetc(stream);
                } else {
                    rf = 1;
                    free(s);
                    s = NULL;
                }
            }
        }
    }

    return s;
}

And then you call it like this:

int main()
{
    char *s = get_str(stdin); /* or a file you opened with fopen, etc */

    printf("%s\n", s);

    free(s);

    return 0;
}

Just remember that memory allocated with either malloc, calloc, or realloc needs to be freed.

Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39
LEF
  • 188
  • 1
  • 9