Yanıtlar:
Dinamik bellek yönetimine ihtiyacınız var ve fgets
satırınızı okumak için işlevi kullanın. Ancak, kaç karakter okuduğunu görmenin bir yolu yok gibi görünüyor. Yani fgetc kullanıyorsunuz:
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;
}
Not : Asla alır! Sınır kontrolü yapmaz ve arabelleğinizden taşabilir
fgetc_unlocked
parçacığı güvenliği önemli değilse de performans önemliyse daha az taşınabilir olanı kullanabilirsiniz.
getline()
POSIX standart getline()
işlevinden farklı olduğunu unutmayın .
Statik ayırma satırını okumak için çok basit ama güvenli olmayan bir uygulama:
char line[1024];
scanf("%[^\n]", line);
Arabellek taşması olasılığı olmayan, ancak tüm satırı okumama olasılığı olan daha güvenli bir uygulama:
char line[1024];
scanf("%1023[^\n]", line);
Değişkeni bildiren uzunluk ile biçim dizesinde belirtilen uzunluk arasındaki 'bir fark' değil. Tarihi bir eserdir.
gets
standarttan tamamen çıkarıldığının tam olarak aynı sorundan muzdarip
Öyleyse, komut argümanları arıyorsanız, Tim'in cevabına bir bakın. Sadece konsoldan bir satır okumak istiyorsanız:
#include <stdio.h>
int main()
{
char string [256];
printf ("Insert your full address: ");
gets (string);
printf ("Your address is: %s\n",string);
return 0;
}
Evet, güvenli değil, arabellek taşması yapabilirsiniz, dosyanın sonunu kontrol etmez, kodlamaları ve diğer pek çok şeyi desteklemez. Aslında bunlardan HERHANGİ BİRİNİ yapıp yapmadığını bile düşünmedim. Katılıyorum, bir nevi batırdım :) Ama ... "C'de konsoldan bir satır nasıl okunur?" Gibi bir soru gördüğümde, bir kişinin 100 satır kod yerine gets () gibi basit bir şeye ihtiyacı olduğunu varsayıyorum. yukarıdaki gibi. Aslında, bence bu 100 satırlık kodu gerçekte yazmaya çalışırsanız, seçmiş olsaydınız yapacağınızdan çok daha fazla hata yaparsınız;)
gets
artık yok, bu nedenle bu C11'de çalışmıyor.
getline
çalıştırılabilir örnek
Bu cevapta bahsedildi ama işte bir örnek.
Bu ise POSIX 7 , bizim için ayırır bellek ve güzel bir şekilde bir döngü tahsis arabelleği yeniden.
İşaretçi newbs, şunu okuyun: getline'ın ilk argümanı neden "char *" yerine "char **" göstericisine bir gösterici?
#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 uygulaması
POSIX yok mu? Belki glibc 2.23 uygulamasına bakmak istersiniz .
Rasgele bir satır sonlandırıcı ile getdelim
basit bir POSIX üst kümesi olan olarak çözülür getline
.
Her artış gerektiğinde ayrılan belleği iki katına çıkarır ve iş parçacığı açısından güvenli görünür.
Biraz makro genişletme gerektirir, ancak daha iyisini yapma olasılığınız düşüktür.
len
Buradaki amaç nedir, okumak da uzunluğu sağlar
man getline
. len
mevcut tamponun uzunluğu 0
, sihirlidir ve ona ayırmasını söyler. Okuma, okunan karakter sayısıdır. Arabellek boyutu daha büyük olabilir read
.
Benim gibi pek çok kişi, açıklama değişken uzunluk hakkında söylese de, arananla eşleşen başlık ile bu gönderiye geliyor. Çoğu durumda, uzunluğu önceden biliyoruz.
Elinizden önce uzunluğu biliyorsanız, aşağıdakileri deneyin:
char str1[1001] = { 0 };
fgets(str1, 1001, stdin); // 1000 chars may be read
kaynak: https://www.tutorialspoint.com/c_standard_library/c_function_fgets.htm
Önerildiği gibi, bir satır sonu veya bir EOF döndürülene kadar konsoldan okumak için getchar () kullanabilir ve kendi tamponunuzu oluşturabilirsiniz. Makul bir maksimum satır boyutu ayarlayamazsanız, dinamik olarak büyüyen tampon oluşabilir.
Fgets'ı C boş sonlu dizge olarak bir satırı elde etmenin güvenli bir yolu olarak da kullanabilirsiniz:
#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);
Konsol girişini bitirdiyseniz veya işlem herhangi bir nedenle başarısız olduysa, eof == NULL döndürülür ve satır arabelleği değişmemiş olabilir (bu nedenle ilk karakterin '\ 0' olarak ayarlanması kullanışlıdır).
fgets, [] satırını aşmaz ve başarılı bir dönüşte son kabul edilen karakterden sonra bir boş değer olmasını sağlar.
Satırın sonuna ulaşılmışsa, '\ 0' ile biten karakterin önündeki karakter '\ n' olacaktır.
'\ 0' bitiminden önce sonlandırıcı bir '\ n' yoksa, daha fazla veri olabilir veya bir sonraki istek dosya sonunu bildirebilir. Hangisinin hangisi olduğunu belirlemek için başka bir fget yapmanız gerekecek. (Bu bağlamda getchar () ile döngü yapmak daha kolaydır.)
Yukarıdaki (güncellenmiş) örnek kodda, başarılı fgets'den sonra satır [sizeof (line) -1] == '\ 0' ise, arabelleğin tamamen dolduğunu bilirsiniz. Bu pozisyon bir '\ n' ile ilerletilirse, şanslı olduğunuzu bilirsiniz. Aksi takdirde, stdin'de ya daha fazla veri ya da ileride bir dosya sonu vardır. (Arabellek tamamen doldurulmadığında, hala bir dosyanın sonunda olabilirsiniz ve mevcut satırın sonunda da bir '\ n' olmayabilir. Bulmak için dizeyi taramanız gerektiğinden ve / veya dizenin sonundan önceki herhangi bir '\ n' yi ortadan kaldırın (tampondaki ilk '\ 0'), ilk etapta getchar () kullanmayı tercih etme eğilimindeyim.)
Hala ilk yığın olarak okuduğunuz miktardan daha fazla satır olmasıyla başa çıkmak için yapmanız gerekenleri yapın. Dinamik olarak büyüyen bir tamponun örnekleri getchar veya fgets ile çalışmak üzere yapılabilir. Dikkat edilmesi gereken bazı zorlu uç durumlar vardır (bir sonraki girdinin, arabellek uzatılmadan önceki girdiyi sonlandıran '\ 0' konumunda depolamaya başlaması gibi).
C'de konsoldan bir satır nasıl okunur?
Kendi işlevinizi oluşturmak, konsoldan bir satır okumanıza yardımcı olacak yollardan biridir.
Gereken bellek miktarını ayırmak için dinamik bellek ayırma kullanıyorum
Ayrılan belleği tüketmek üzereyken, bellek boyutunu iki katına çıkarmaya çalışırız.
Ve burada getchar()
, kullanıcı '\n'
veya EOF
karakter girene kadar dizenin her karakterini işlevi kullanarak tek tek taramak için bir döngü kullanıyorum.
Son olarak, satırı döndürmeden önce ek olarak ayrılmış bellekleri kaldırıyoruz
//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;
}
Şimdi tam bir satırı şu şekilde okuyabilirsiniz:
char *line = NULL;
line = scan_line(line);
İşte işlevi kullanan örnek bir programscan_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
}
örnek giriş:
Twinkle Twinkle little star.... in the sky!
örnek çıktı:
Twinkle Twinkle little star.... in the sky!
Bir süre önce aynı problemle karşılaştım, bu benim çözümümdü, umarım yardımcı olur.
/*
* 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;
}
goto
Hata durumunu işlemek için kullanırsanız kodunuz ÇOK daha basit hale gelir . Yine de, aynı boyutta döngü içinde tekrar tekrar kullanmak tmp_buf
yerine yeniden kullanabileceğinizi düşünmüyor musunuz malloc
?
has_err
Hataları bildirmek için tek bir global değişken kullanmak , bu işlevi iş parçacığını güvensiz ve kullanımı rahat hale getirir. Bunu bu şekilde yapma. NULL döndürerek zaten bir hatayı belirtiyorsunuz. Ayrıca, yazdırılan hata mesajlarının genel amaçlı bir kütüphane işlevinde iyi bir fikir olmadığını düşünmeye de yer vardır.
BSD sistemlerinde ve Android'de şunları da kullanabilirsiniz fgetln
:
#include <stdio.h>
char *
fgetln(FILE *stream, size_t *len);
Şöyle:
size_t line_len;
const char *line = fgetln(stdin, &line_len);
line
Boş sonlandırılmış değildir ve içerdiği \n
(veya platformu kullanarak ne olursa olsun) sonunda. Akıştaki bir sonraki G / Ç işleminden sonra geçersiz hale gelir.
Bunun gibi bir şey:
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;
}
Bir konsoldan bir satırı okumanın en iyi ve en basit yolu getchar () işlevini kullanmaktır, böylece bir dizide her seferinde bir karakter saklayacaksınız.
{
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 );
}
Bu işlev istediğinizi yapmalıdır:
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 );
Umarım bu yardımcı olur.
fgets( buffer, sizeof(buffer), file );
değil sizeof(buffer)-1
. fgets
sonlandırıcı null için boşluk bırakır.
while (!feof(file))
her zaman yanlış olduğunu ve bunun hatalı kullanımın sadece bir örneği olduğunu unutmayın.