C में कंसोल से एक लाइन कैसे पढ़ें?


108

C सांत्वना कार्यक्रम में एक पूरी लाइन को पढ़ने का सबसे सरल तरीका क्या है। दर्ज किया गया पाठ एक चर लंबाई हो सकता है और हम इसकी सामग्री के बारे में कोई धारणा नहीं बना सकते।


क्या आप स्पष्ट कर सकते हैं, कृपया? जैसा कि @Tim ने नीचे कहा, यह भ्रमित कर रहा है कि आप क्या पूछ रहे हैं :)
वॉरेन

जवाबों:


81

आपको गतिशील मेमोरी प्रबंधन की आवश्यकता है, और fgetsअपनी लाइन को पढ़ने के लिए फ़ंक्शन का उपयोग करें । हालांकि, यह देखने का कोई तरीका नहीं है कि यह कितने वर्णों को पढ़ता है। तो आप fgetc का उपयोग करें:

char * getline(void) {
    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;
            char * linen = realloc(linep, lenmax *= 2);

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

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

नोट : कभी नहीं उपयोग हो जाता है! यह चेकिंग सीमा नहीं करता है और आपके बफर को ओवरफ्लो कर सकता है


कैविएट - वहाँ realloc के परिणाम की जाँच करने की आवश्यकता है। लेकिन अगर वह विफल हो जाता है, तो सबसे अधिक संभावना बदतर समस्याएं हैं।
टिम

4
आप शायद बफर के साथ फ़्यूज़ करके दक्षता में थोड़ा सुधार कर सकते हैं, और जाँच सकते हैं कि आपके पास अंत में न्यूलाइन वर्ण है या नहीं। यदि आप नहीं करते हैं, तो अपने संचय बफर को फिर से खोलें, उसमें कॉपी करें, और फिर से फिट करें।
पॉल टॉम्बलिन

3
इस फ़ंक्शन को एक सुधार की आवश्यकता है: लाइन "लेन = लेनमैक्स;" के बाद realloc या तो realloc से पहले होना चाहिए या "len = lenmax >> 1" होना चाहिए; - या कुछ अन्य समकक्ष जो इस तथ्य के लिए जिम्मेदार हैं कि आधी लंबाई पहले से ही उपयोग की जाती है।
मैट गलाघेर

1
@Johannes, आपके प्रश्न के उत्तर में, @ पॉल का दृष्टिकोण COULD सबसे अधिक (यानी प्रतिवादी) libc कार्यान्वयन पर अधिक तेज़ होना चाहिए, क्योंकि आपका दृष्टिकोण प्रत्येक वर्ण के लिए स्पष्ट रूप से स्टड को लॉक करता है, जबकि उसका प्रति बफर एक बार लॉक होता है। fgetc_unlockedयदि थ्रेड सुरक्षा चिंता का विषय नहीं है, लेकिन प्रदर्शन कम है तो आप कम पोर्टेबल का उपयोग कर सकते हैं ।
vladr

3
ध्यान दें कि यह getline()POSIX मानक getline()फ़ंक्शन से अलग है ।
जोनाथन लेफ़लर

28

यदि आप GNU C लाइब्रेरी या किसी अन्य POSIX- अनुरूप लाइब्रेरी का उपयोग कर रहे हैं, तो आप फाइल स्ट्रीम के लिए इसका उपयोग कर सकते हैं getline()और पास कर सकते हैं stdin


16

स्थैतिक आवंटन के लिए लाइन पढ़ने के लिए एक बहुत ही सरल लेकिन असुरक्षित कार्यान्वयन:

char line[1024];

scanf("%[^\n]", line);

बफर ओवरफ्लो की संभावना के बिना एक सुरक्षित कार्यान्वयन, लेकिन पूरी लाइन को न पढ़ने की संभावना के साथ:

char line[1024];

scanf("%1023[^\n]", line);

चर घोषित करने वाली लंबाई और प्रारूप स्ट्रिंग में निर्दिष्ट लंबाई के बीच 'अंतर से एक' नहीं। यह एक ऐतिहासिक कला है।


14
यह बिल्कुल भी सुरक्षित नहीं है। यह बिल्कुल उसी समस्या से ग्रस्त है getsजो मानक से पूरी तरह से हटा दिया गया था
एंटटी हवाला

6
मॉडरेटर नोट: उपरोक्त टिप्पणी उत्तर के पिछले संशोधन
रॉबर्ट हार्वे

13

इसलिए, यदि आप कमांड के तर्क की तलाश कर रहे थे, तो टिम के उत्तर पर एक नज़र डालें। यदि आप सिर्फ कंसोल से एक पंक्ति पढ़ना चाहते हैं:

#include <stdio.h>

int main()
{
  char string [256];
  printf ("Insert your full address: ");
  gets (string);
  printf ("Your address is: %s\n",string);
  return 0;
}

हां, यह सुरक्षित नहीं है, आप बफर ओवररन कर सकते हैं, यह फाइल के अंत की जांच नहीं करता है, यह एनकोडिंग और बहुत सारे अन्य सामान का समर्थन नहीं करता है। वास्तव में मैंने यह भी नहीं सोचा कि क्या यह इस सामान में से किसी ने किया। मैं मानता हूं कि मैं थोड़े खराब हो गया हूं। ऊपर की तरह। वास्तव में, मुझे लगता है, यदि आप कोड की उन 100 पंक्तियों को वास्तविकता में लिखने की कोशिश करते हैं, तो आप कई और गलतियाँ करेंगे, जितना आपने किया होगा;


1
तीस लंबे स्ट्रिंग्स के लिए अनुमति नहीं देता है ... - जो मुझे लगता है कि उनके सवाल का क्रूस है।
टिम

2
-1, हो जाता है () का उपयोग नहीं किया जाना चाहिए क्योंकि यह सीमा जाँच नहीं करता है।
खोलना

7
दूसरी ओर यदि आप अपने लिए कोई प्रोग्राम लिख रहे हैं और आपको एक इनपुट पढ़ना है तो यह पूरी तरह से ठीक है। एक कार्यक्रम को कितनी सुरक्षा की जरूरत होती है, यह कल्पना से परे है - आप इसे प्राथमिकता के रूप में नहीं रखना चाहते हैं।
मार्टिन बेकेट

4
@ समय - मैं सभी इतिहास को बनाए रखना चाहता हूं :)
पॉल कपस्टिन

4
Downvoted। getsकोई और अधिक मौजूद नहीं है, इस प्रकार यह C11 में काम नहीं करता है।
अंती हापाला

11

आपको यह सुनिश्चित करने के लिए कि आपको कोई बफर ओवरफ्लो नहीं है और इनपुट को छोटा नहीं करना है, चरित्र (getc ()) लूप द्वारा एक चरित्र का उपयोग करने की आवश्यकता हो सकती है।


9

getline चल उदाहरण

इस उत्तर पर उल्लेख किया गया है लेकिन यहाँ एक उदाहरण है।

यह POSIX 7 है , हमारे लिए मेमोरी आवंटित करता है, और एक लूप पर आवंटित बफर का अच्छी तरह से पुन: उपयोग करता है।

पॉइंटर न्यूब्स, इसे पढ़ें: पॉइंटर को पॉइंटर करने का पहला तर्क "चार *" के बजाय पॉइंटर "चार **" क्यों है?

#define _XOPEN_SOURCE 700

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

int main(void) {
    char *line = NULL;
    size_t len = 0;
    ssize_t read = 0;
    while (read != -1) {
        puts("enter a line");
        read = getline(&line, &len, stdin);
        printf("line = %s", line);
        printf("line length = %zu\n", read);
        puts("");
    }
    free(line);
    return 0;
}

glibc कार्यान्वयन

कोई POSIX? शायद तुम देखना चाहते हो glibc 2.23 कार्यान्वयन

इसका समाधान होता है getdelim , जो getlineएक मनमाने ढंग से लाइन टर्मिनेटर के साथ एक सरल POSIX सुपरसेट है ।

जब भी वृद्धि की आवश्यकता होती है, यह आवंटित मेमोरी को दोगुना कर देता है, और थ्रेड-सुरक्षित दिखता है।

इसके लिए कुछ स्थूल विस्तार की आवश्यकता है, लेकिन आप बहुत बेहतर करने की संभावना नहीं रखते हैं।


यहाँ क्या उद्देश्य lenहै, जब पढ़ा जाता है तो लंबाई भी प्रदान करता है
अब्दुल

देख @Abdul man getlinelenमौजूदा बफर की लंबाई है, 0जादू है और इसे आवंटित करने के लिए कहता है। पढ़िए चार्ट की संख्या पढ़ी जाती है बफर का आकार इससे बड़ा हो सकता है read
सिरो सेंटिल्ली 郝海东 冠状 iro i 法轮功 '

6

मेरे जैसे कई लोग इस पद पर आते हैं, जो कि खोजे जाने वाले शीर्षक से मेल खाते हैं, हालांकि वर्णन चर लंबाई के बारे में कह रहा है। अधिकांश मामलों के लिए, हम पहले से लंबाई जानते हैं।

यदि आप हाथ से पहले लंबाई जानते हैं, तो नीचे देखें:

char str1[1001] = { 0 };
fgets(str1, 1001, stdin); // 1000 chars may be read

स्रोत: https://www.tutorialspoint.com/c_standard_library/c_function_fgets.htm


5

जैसा कि सुझाव दिया गया है, आप कंसोल से पढ़ने के लिए गेटचेयर () का उपयोग कर सकते हैं जब तक कि एक अंत-पंक्ति या ईओएफ वापस नहीं किया जाता है, अपने स्वयं के बफर का निर्माण। यदि आप एक उचित अधिकतम लाइन आकार सेट करने में असमर्थ हैं, तो गतिशील रूप से बफर बढ़ सकता है।

आप सी-नल-समाप्त स्ट्रिंग के रूप में एक लाइन प्राप्त करने के लिए एक सुरक्षित तरीके के रूप में भी उपयोग कर सकते हैं:

#include <stdio.h>

char line[1024];  /* Generously large value for most situations */

char *eof;

line[0] = '\0'; /* Ensure empty line if no input delivered */
line[sizeof(line)-1] = ~'\0';  /* Ensure no false-null at end of buffer */

eof = fgets(line, sizeof(line), stdin);

यदि आपने कंसोल इनपुट समाप्त कर दिया है या किसी कारणवश ऑपरेशन विफल हो गया है, तो eof == NULL वापस आ गया है और लाइन बफर अपरिवर्तित हो सकता है (यही वजह है कि पहला char को '\ 0' में सेट करना आसान है)।

फ़िज़ूल लाइन खत्म नहीं होगी [और यह सुनिश्चित करेगा कि एक सफल वापसी पर अंतिम-स्वीकृत चरित्र के बाद एक अशक्त हो।

यदि एंड-ऑफ-लाइन पहुंच गया था, तो '\ 0' को समाप्त करने से पहले का वर्ण '\ n' होगा।

यदि '' 0 'को समाप्त करने से पहले कोई समाप्ति' \ n 'नहीं है तो हो सकता है कि अधिक डेटा हो या कि अगला अनुरोध एंड-ऑफ़-फ़ाइल की रिपोर्ट करेगा। आपको यह निर्धारित करने के लिए एक और बजट करना होगा कि कौन सा है। (इस संबंध में, गेटचेयर के साथ लूपिंग आसान है।)

ऊपर (अद्यतित) उदाहरण कोड में, यदि लाइन [sizeof (लाइन) -1] == '0' सफल होने के बाद, आप जानते हैं कि बफर पूरी तरह से भरा हुआ था। यदि वह स्थिति '\ n' द्वारा आगे बढ़ाई जाती है, तो आप जानते हैं कि आप भाग्यशाली थे। अन्यथा, स्टैड में आगे या तो अधिक डेटा या एक एंड-ऑफ-फ़ाइल है। (जब बफर पूरी तरह से भरा नहीं है, तो आप अभी भी एक फ़ाइल के अंत में हो सकते हैं और वर्तमान लाइन के अंत में एक '\ n' भी नहीं हो सकता है। चूंकि आपको खोजने और / के लिए स्ट्रिंग को स्कैन करना होगा। या स्ट्रिंग के अंत से पहले किसी भी '\ n' को समाप्त करें (बफर में पहला '\ 0'), मैं पहली जगह में getchar () का उपयोग करना पसंद कर रहा हूँ।)

पहले चंक के रूप में पढ़ी गई राशि से अधिक लाइन होने के बावजूद आपको जो करने की आवश्यकता है, वह करें। गतिशील रूप से बढ़ते हुए बफर के उदाहरणों को गेटचार या फ़्यूज़ के साथ काम करने के लिए बनाया जा सकता है। बाहर देखने के लिए कुछ ट्रिकी एज केस हैं (जैसे कि याद है कि अगला इनपुट शुरू होने की स्थिति में '\ 0' की स्थिति में बफर इनपुट से पहले पिछले इनपुट को समाप्त कर दिया था)।


2

C में कंसोल से एक लाइन कैसे पढ़ें?

  • अपने स्वयं के फ़ंक्शन का निर्माण करना, उन तरीकों में से एक है जो आपको कंसोल से एक लाइन पढ़ने में मदद करेगा

  • मैं आवश्यक स्मृति की आवश्यक मात्रा आवंटित करने के लिए गतिशील मेमोरी आवंटन का उपयोग कर रहा हूं

  • जब हम आवंटित मेमोरी को समाप्त करने वाले होते हैं, तो हम मेमोरी के आकार को दोगुना करने की कोशिश करते हैं

  • और यहां मैं एक लूप का उपयोग कर रहा हूं ताकि स्ट्रिंग के प्रत्येक चरित्र को एक-एक करके getchar()फ़ंक्शन का उपयोग किया जा सके जब तक कि उपयोगकर्ता प्रवेश '\n'या EOFचरित्र नहीं करता

  • अंत में हम लाइन को वापस करने से पहले किसी भी अतिरिक्त रूप से आवंटित मेमोरी को हटा देते हैं

//the function to read lines of variable length

char* scan_line(char *line)
{
    int ch;             // as getchar() returns `int`
    long capacity = 0;  // capacity of the buffer
    long length = 0;    // maintains the length of the string
    char *temp = NULL;  // use additional pointer to perform allocations in order to avoid memory leaks

    while ( ((ch = getchar()) != '\n') && (ch != EOF) )
    {
        if((length + 1) >= capacity)
        {
            // resetting capacity
            if (capacity == 0)
                capacity = 2; // some initial fixed length 
            else
                capacity *= 2; // double the size

            // try reallocating the memory
            if( (temp = realloc(line, capacity * sizeof(char))) == NULL ) //allocating memory
            {
                printf("ERROR: unsuccessful allocation");
                // return line; or you can exit
                exit(1);
            }

            line = temp;
        }

        line[length] = (char) ch; //type casting `int` to `char`
    }
    line[length + 1] = '\0'; //inserting null character at the end

    // remove additionally allocated memory
    if( (temp = realloc(line, (length + 1) * sizeof(char))) == NULL )
    {
        printf("ERROR: unsuccessful allocation");
        // return line; or you can exit
        exit(1);
    }

    line = temp;
    return line;
}
  • अब आप इस तरह से एक पूरी लाइन पढ़ सकते हैं:

    char *line = NULL;
    line = scan_line(line);

यहाँ एक उदाहरण कार्यक्रम का उपयोग कर कार्यक्रम हैscan_line() :

#include <stdio.h>
#include <stdlib.h> //for dynamic allocation functions

char* scan_line(char *line)
{
    ..........
}

int main(void)
{
    char *a = NULL;

    a = scan_line(a); //function call to scan the line

    printf("%s\n",a); //printing the scanned line

    free(a); //don't forget to free the malloc'd pointer
}

नमूना इनपुट:

Twinkle Twinkle little star.... in the sky!

नमूना उत्पादन:

Twinkle Twinkle little star.... in the sky!

0

मैं कुछ समय पहले इसी समस्या में आया था, यह मेरा विलेयशन था, आशा है कि यह मदद करेगा।

/*
 * Initial size of the read buffer
 */
#define DEFAULT_BUFFER 1024

/*
 * Standard boolean type definition
 */
typedef enum{ false = 0, true = 1 }bool;

/*
 * Flags errors in pointer returning functions
 */
bool has_err = false;

/*
 * Reads the next line of text from file and returns it.
 * The line must be free()d afterwards.
 *
 * This function will segfault on binary data.
 */
char *readLine(FILE *file){
    char *buffer   = NULL;
    char *tmp_buf  = NULL;
    bool line_read = false;
    int  iteration = 0;
    int  offset    = 0;

    if(file == NULL){
        fprintf(stderr, "readLine: NULL file pointer passed!\n");
        has_err = true;

        return NULL;
    }

    while(!line_read){
        if((tmp_buf = malloc(DEFAULT_BUFFER)) == NULL){
            fprintf(stderr, "readLine: Unable to allocate temporary buffer!\n");
            if(buffer != NULL)
                free(buffer);
            has_err = true;

            return NULL;
        }

        if(fgets(tmp_buf, DEFAULT_BUFFER, file) == NULL){
            free(tmp_buf);

            break;
        }

        if(tmp_buf[strlen(tmp_buf) - 1] == '\n') /* we have an end of line */
            line_read = true;

        offset = DEFAULT_BUFFER * (iteration + 1);

        if((buffer = realloc(buffer, offset)) == NULL){
            fprintf(stderr, "readLine: Unable to reallocate buffer!\n");
            free(tmp_buf);
            has_err = true;

            return NULL;
        }

        offset = DEFAULT_BUFFER * iteration - iteration;

        if(memcpy(buffer + offset, tmp_buf, DEFAULT_BUFFER) == NULL){
            fprintf(stderr, "readLine: Cannot copy to buffer\n");
            free(tmp_buf);
            if(buffer != NULL)
                free(buffer);
            has_err = true;

            return NULL;
        }

        free(tmp_buf);
        iteration++;
    }

    return buffer;
}

1
यदि आप gotoत्रुटि मामले को संभालने के लिए उपयोग करते हैं तो आपका कोड बहुत सरल हो जाएगा । फिर भी, आपको नहीं लगता कि आप पुन: उपयोग कर सकता है tmp_buf, बजाय mallocपाश में पर एक ही आकार के साथ यह ing?
शहबाज

has_errत्रुटियों की रिपोर्ट करने के लिए एक एकल वैश्विक चर का उपयोग इस फ़ंक्शन को थ्रेड-असुरक्षित और उपयोग करने के लिए आरामदायक से कम बनाता है। इस तरह मत करो। NULL को वापस करके आप पहले से ही एक त्रुटि दर्शाते हैं। यह सोचने के लिए भी जगह है कि मुद्रित त्रुटि संदेश एक सामान्य-उद्देश्य लाइब्रेरी फ़ंक्शन में एक अच्छा विचार नहीं है।
जोनाथन लेफ़लर

0

बीएसडी सिस्टम और एंड्रॉइड पर भी आप उपयोग कर सकते हैं fgetln:

#include <stdio.h>

char *
fgetln(FILE *stream, size_t *len);

इस तरह:

size_t line_len;
const char *line = fgetln(stdin, &line_len);

lineअशक्त समाप्त नहीं है और होता है \nअंत में (या जो भी अपने मंच का उपयोग कर रहा है)। स्ट्रीम पर अगले I / O ऑपरेशन के बाद यह अमान्य हो जाता है।


हाँ, फ़ंक्शन मौजूद है। यह चेतावनी कि यह एक शून्य-समाप्त स्ट्रिंग प्रदान नहीं करता है पर्याप्त रूप से बड़ी और समस्याग्रस्त है कि इसका उपयोग न करना बेहतर है - यह खतरनाक है।
जोनाथन लेफ़लर

0

कुछ इस तरह:

unsigned int getConsoleInput(char **pStrBfr) //pass in pointer to char pointer, returns size of buffer
{
    char * strbfr;
    int c;
    unsigned int i;
    i = 0;
    strbfr = (char*)malloc(sizeof(char));
    if(strbfr==NULL) goto error;
    while( (c = getchar()) != '\n' && c != EOF )
    {
        strbfr[i] = (char)c;
        i++;
        strbfr = (void*)realloc((void*)strbfr,sizeof(char)*(i+1));
        //on realloc error, NULL is returned but original buffer is unchanged
        //NOTE: the buffer WILL NOT be NULL terminated since last
        //chracter came from console
        if(strbfr==NULL) goto error;
    }
    strbfr[i] = '\0';
    *pStrBfr = strbfr; //successfully returns pointer to NULL terminated buffer
    return i + 1; 
    error:
    *pStrBfr = strbfr;
    return i + 1;
}

0

कंसोल से एक पंक्ति को पढ़ने का सबसे अच्छा और सरल तरीका गेटचार () फ़ंक्शन का उपयोग कर रहा है, जिसके तहत आप एक सरणी में एक बार में एक चरित्र को संग्रहीत करेंगे।

{
char message[N];        /* character array for the message, you can always change the character length */
int i = 0;          /* loop counter */

printf( "Enter a message: " );
message[i] = getchar();    /* get the first character */
while( message[i] != '\n' ){
    message[++i] = getchar(); /* gets the next character */
}

printf( "Entered message is:" );
for( i = 0; i < N; i++ )
    printf( "%c", message[i] );

return ( 0 );

}


-3

इस समारोह में वह करना चाहिए जो आप चाहते हैं:

char* readLine( FILE* file )
 {
 char buffer[1024];
 char* result = 0;
 int length = 0;

 while( !feof(file) )
  {
  fgets( buffer, sizeof(buffer), file );
  int len = strlen(buffer);
  buffer[len] = 0;

  length += len;
  char* tmp = (char*)malloc(length+1);
  tmp[0] = 0;

  if( result )
   {
   strcpy( tmp, result );
   free( result );
   result = tmp;
   }

  strcat( result, buffer );

  if( strstr( buffer, "\n" ) break;
  }

 return result;
 }

char* line = readLine( stdin );
/* Use it */
free( line );

आशा है कि ये आपकी मदद करेगा।


1
आपको fgets( buffer, sizeof(buffer), file );नहीं करना चाहिए sizeof(buffer)-1fgetsसमाप्ति नल के लिए जगह छोड़ देता है।
user102008

ध्यान दें कि while (!feof(file))यह हमेशा गलत है और यह गलत उपयोग का सिर्फ एक और उदाहरण है।
जोनाथन लेफ़लर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.