Używając poniższego kodu:
char *name = malloc(sizeof(char) + 256);
printf("What is your name? ");
scanf("%s", name);
printf("Hello %s. Nice to meet you.\n", name);
Użytkownik może wpisać swoje imię, ale kiedy wpisuje nazwę ze spacją Lucas Aardvark
, scanf()
po prostu odcina wszystko Lucas
. Jak zrobić scanf()
zezwalanie na spacje
sizeof(char) + 256
była to literówka.Odpowiedzi:
Ludzie (a zwłaszcza początkujący) nigdy nie powinni używać
scanf("%s")
anigets()
żadnych innych funkcji, które nie mają ochrony przed przepełnieniem bufora, chyba że wiesz na pewno, że dane wejściowe zawsze będą w określonym formacie (a może nawet wtedy).Pamiętaj, że
scanf
oznacza to „skan sformatowany” i jest niewiele mniej sformatowanych niż dane wprowadzone przez użytkownika. Jest to idealne rozwiązanie, jeśli masz całkowitą kontrolę nad formatem danych wejściowych, ale generalnie nie nadaje się do wprowadzania danych przez użytkownika.Użyj
fgets()
(który ma ochronę przed przepełnieniem buforu), aby uzyskać dane wejściowe w łańcuchu isscanf()
ocenić je. Ponieważ chcesz tylko tego, co wprowadził użytkownik bez przetwarzania, tak naprawdę nie potrzebujeszsscanf()
w tym przypadku:#include <stdio.h> #include <stdlib.h> #include <string.h> /* Maximum name size + 1. */ #define MAX_NAME_SZ 256 int main(int argC, char *argV[]) { /* Allocate memory and check if okay. */ char *name = malloc(MAX_NAME_SZ); if (name == NULL) { printf("No memory\n"); return 1; } /* Ask user for name. */ printf("What is your name? "); /* Get the name, with size limit. */ fgets(name, MAX_NAME_SZ, stdin); /* Remove trailing newline, if there. */ if ((strlen(name) > 0) && (name[strlen (name) - 1] == '\n')) name[strlen (name) - 1] = '\0'; /* Say hello. */ printf("Hello %s. Nice to meet you.\n", name); /* Free memory and exit. */ free (name); return 0; }
źródło
fgets()
. W rzeczywistości wygląda to na łatwiejsze w użyciuscanf()
. +1scanf
ma niezdefiniowane zachowanie, jeśli przepełnienie konwersji numerycznej ( N1570 7.21.6.2p10 , ostatnie zdanie, sformułowanie niezmienione od C89), co oznacza, że żadna zscanf
funkcji nie może być bezpiecznie używana do konwersji numerycznej niezaufanych danych wejściowych.scanf
w zadaniu, zrobili to w błędzie i możesz im powiedzieć, że tak powiedziałem, i jeśli chcą się ze mną spierać o to mój adres e-mail można łatwo znaleźć w moim profilu.Próbować
char str[11]; scanf("%10[0-9a-zA-Z ]", str);
Mam nadzieję, że to pomoże.
źródło
s
z końca ciągu wejściowego, ponieważ w niektórych przypadkach jest on zarówno zbędny, jak i niepoprawny (jak wskazano we wcześniejszych komentarzach).[
jest to własny specyfikator formatu, a nie jakąś odmianę tegos
.W tym przykładzie zastosowano odwrócony zestaw skanowania, więc scanf pobiera wartości, dopóki nie napotka „\ n” - nowej linii, więc spacje również zostaną zapisane
#include <stdio.h> int main (int argc, char const *argv[]) { char name[20]; scanf("%[^\n]s",name); printf("%s\n", name); return 0; }
źródło
%20[^\n]s
aby zapobiec przepełnieniu buforas
!Możesz tego użyć
char name[20]; scanf("%20[^\n]", name);
Albo to
void getText(char *message, char *variable, int size){ printf("\n %s: ", message); fgets(variable, sizeof(char) * size, stdin); sscanf(variable, "%[^\n]", variable); } char name[20]; getText("Your name", name, 20);
PRÓBNY
źródło
scanf("%19[^\n]", name);
(nadal +1 za zwięzłą odpowiedź)sizeof(char)
z definicji zawsze wynosi 1, więc nie ma potrzeby mnożenia przez to.Nie używaj
scanf()
do odczytywania ciągów bez określenia szerokości pola. Powinieneś również sprawdzić zwracane wartości pod kątem błędów:#include <stdio.h> #define NAME_MAX 80 #define NAME_MAX_S "80" int main(void) { static char name[NAME_MAX + 1]; // + 1 because of null if(scanf("%" NAME_MAX_S "[^\n]", name) != 1) { fputs("io error or premature end of line\n", stderr); return 1; } printf("Hello %s. Nice to meet you.\n", name); }
Alternatywnie użyj
fgets()
:#include <stdio.h> #define NAME_MAX 80 int main(void) { static char name[NAME_MAX + 2]; // + 2 because of newline and null if(!fgets(name, sizeof(name), stdin)) { fputs("io error\n", stderr); return 1; } // don't print newline printf("Hello %.*s. Nice to meet you.\n", strlen(name) - 1, name); }
źródło
Możesz użyć tej
fgets()
funkcji do odczytania ciągu lub użyć,scanf("%[^\n]s",name);
aby odczyt ciągu zakończył się po napotkaniu znaku nowego wiersza.źródło
s
nie należy tamgetline()
Teraz jednak część POSIX.
Zajmuje się również problemem alokacji bufora, o który pytałeś wcześniej, chociaż musisz zadbać o
free
pamięć.źródło
Jeśli ktoś nadal szuka, oto co zadziałało dla mnie - odczyt dowolnej długości łańcucha ze spacjami.
Dzięki wielu plakatom w sieci za udostępnienie tego prostego i eleganckiego rozwiązania. Jeśli to zadziała, zasługa ich, ale wszelkie błędy są moje.
char *name; scanf ("%m[^\n]s",&name); printf ("%s\n",name);
źródło
errno
i wyczyścić przydzieloną pamięć.s
nie pasuje tam po scansecieMożesz użyć
scanf
do tego celu z małą sztuczką. Właściwie powinieneś pozwolić użytkownikowi na wprowadzanie danych, dopóki użytkownik nie naciśnie Enter (\n
). Uwzględni to każdy znak, w tym spację . Oto przykład:int main() { char string[100], c; int i; printf("Enter the string: "); scanf("%s", string); i = strlen(string); // length of user input till first space do { scanf("%c", &c); string[i++] = c; // reading characters after first space (including it) } while (c != '\n'); // until user hits Enter string[i - 1] = 0; // string terminating return 0; }
Jak to działa? Gdy użytkownik wprowadza znaki ze standardowego wejścia, będą one przechowywane w zmiennej łańcuchowej do pierwszej spacji. Następnie reszta wpisu pozostanie w strumieniu wejściowym i będzie czekać na następne skanowanie. Następnie mamy
for
pętlę, która pobiera znak po znaku ze strumienia wejściowego (do\n
) i dołącza je do końca łańcucha zmiennej , tworząc w ten sposób pełny ciąg, taki sam jak dane wejściowe użytkownika z klawiatury.Mam nadzieję, że to komuś pomoże!
źródło
Chociaż naprawdę nie powinieneś używać
scanf()
tego typu rzeczy, ponieważ istnieją znacznie lepsze wywołania, takie jakgets()
lubgetline()
, można to zrobić:#include <stdio.h> char* scan_line(char* buffer, int buffer_size); char* scan_line(char* buffer, int buffer_size) { char* p = buffer; int count = 0; do { char c; scanf("%c", &c); // scan a single character // break on end of line, string terminating NUL, or end of file if (c == '\r' || c == '\n' || c == 0 || c == EOF) { *p = 0; break; } *p++ = c; // add the valid character into the buffer } while (count < buffer_size - 1); // don't overrun the buffer // ensure the string is null terminated buffer[buffer_size - 1] = 0; return buffer; } #define MAX_SCAN_LENGTH 1024 int main() { char s[MAX_SCAN_LENGTH]; printf("Enter a string: "); scan_line(s, MAX_SCAN_LENGTH); printf("got: \"%s\"\n\n", s); return 0; }
źródło
gets
został wycofany i usunięty ( stackoverflow.com/questions/30890696/why-gets-is-deprecated ) ze standardu. Jest jeszcze gorzej ,scanf
bo przynajmniej ta ostatnia ma sposoby, aby to zabezpieczyć./*reading string which contains spaces*/ #include<stdio.h> int main() { char *c,*p; scanf("%[^\n]s",c); p=c; /*since after reading then pointer points to another location iam using a second pointer to store the base address*/ printf("%s",p); return 0; }
źródło
s
nie należy tam po scansecie