Próbuję pobrać dane od użytkownika i wysłać je do innej funkcji w gcc. Kod jest mniej więcej taki.
printf("Enter your Name: ");
if (!(fgets(Name, sizeof Name, stdin) != NULL)) {
fprintf(stderr, "Error reading Name.\n");
exit(1);
}
Uważam jednak, że ma on \n
na końcu znak nowej linii . Więc jeśli John
wejdę, wyśle to John\n
. Jak to usunąć \n
i wysłać odpowiedni ciąg.
if (!fgets(Name, sizeof Name, stdin))
(przynajmniej nie używaj dwóch negacji,! i! =)if (fgets(Name, sizeof Name, stdin)) {
.if (fgets(Name, sizeof Name, stdin) == NULL ) {
!
:Odpowiedzi:
Nieco brzydki sposób:
Nieco dziwny sposób:
Zauważ, że
strtok
funkcja nie działa zgodnie z oczekiwaniami, jeśli użytkownik wprowadzi pusty ciąg znaków (tzn. Naciśnie tylko Enter). Pozostawia\n
postać nietkniętą.Oczywiście są też inni.
źródło
strtok()
będzie bezpieczna dla wątków (użyje lokalnej pamięci wątków dla stanu „połączenia”). To powiedziawszy, ogólnie lepiej jest używać niestandardowego (ale dość powszechnego)strtok_r()
wariantu.strtok
podejścia (i działa z pustymi danymi wejściowymi). W rzeczywistości dobrym sposobem na wdrożeniestrtok
jest użyciestrcspn
istrspn
.*strchrnul(Name, '\n') = '\0';
.strchr(Name, '\n') == NULL
oprócz „zbyt długich danych wejściowych dla bufora, błędu flagi” istnieją inne możliwości: Ostatni tekststdin
nie zakończył się znakiem'\n'
lub został odczytany rzadko osadzony znak null.Być może najprostsze rozwiązanie wykorzystuje jedną z moich ulubionych mało znanych funkcji
strcspn()
:Jeśli chcesz, aby również obsługiwał
'\r'
(powiedzmy, jeśli strumień jest binarny):Funkcja zlicza liczbę znaków, aż trafi na a
'\r'
lub a'\n'
(innymi słowy, znajdzie pierwszy'\r'
lub'\n'
). Jeśli niczego nie trafi, zatrzymuje się na'\0'
(zwracając długość łańcucha).Zauważ, że działa to dobrze, nawet jeśli nie ma nowej linii, ponieważ
strcspn
kończy się na'\0'
. W takim przypadku cała linia po prostu zastępuje'\0'
się'\0'
.źródło
buffer
niż zaczyna się'\0'
coś, co powoduje smutek dlabuffer[strlen(buffer) - 1] = '\0';
podejścia.strcspn()
. Jedna z bardziej przydatnych funkcji w bibliotece, IMO. Postanowiłem dziś napisać i opublikować kilka popularnych hacków typu C;strtok_r
realizacja użyciustrcspn
istrspn
był jednym z pierwszych: codepad.org/2lBkZk0w ( Ostrzeżenie: Nie mogę zagwarantować, że to bez błędów; został on napisany w pośpiechu i prawdopodobnie ma kilka). Nie wiem jeszcze, gdzie je opublikuję, ale zamierzam zrobić to w duchu słynnych „nieco kręcących się hacków”.fgets()
. Tostrcspn()
wydaje się być jedynie słuszny-liner.strlen
jest szybszy - choć nie tak prosty.fgets()
wejścia . Który zawsze jest także pierwszą nową linią.źródło
fgets(buf, size, ....)
->strlen(buf) == 0
. 1)fgets()
czyta się jako pierwszychar
a'\0'
. 2)size == 1
3)fgets()
zwraca,NULL
wtedybuf
zawartość może być dowolna. (Kod OP sprawdza, czy ma wartość NULL) Sugeruj:size_t ln = strlen(name); if (ln > 0 && name[ln-1] == '\n') name[--ln] = '\0';
ln
wyniesie -1, z wyjątkiem faktu, że niesize_t
jest podpisany, a zatem zapis do losowej pamięci. Myślę, że chcesz użyćssize_t
i sprawdźln
> 0.strlen
) może być zaimplementowane znacznie wydajniej niż zwykłe wyszukiwanie char-by-char. Z tego powodu uważam to rozwiązanie za lepsze niż oparte na nimstrchr
lubstrcspn
oparte.Poniżej znajduje się szybkie podejście do usunięcia potencjału
'\n'
z ciągu zapisanego przezfgets()
.Używa
strlen()
, z 2 testami.Teraz użyj
buffer
ilen
w razie potrzeby.Ta metoda ma dodatkową zaletę
len
wartości dla kolejnego kodu. Może być łatwo szybszy niżstrchr(Name, '\n')
. Ref YMMV, ale obie metody pracy.buffer
, z oryginałufgets()
nie będzie zawierał w"\n"
pewnych okolicznościach:A) Linia była zbyt długa,
buffer
dlatego zapisano ją tylkochar
przed nią . Nieprzeczytane znaki pozostają w strumieniu. B) Ostatni wiersz w pliku nie kończył się na'\n'
buffer
'\n'
.Jeśli dane wejściowe mają
'\0'
gdzieś osadzone znaki zerowe , podawana przezstrlen()
nie długość nie uwzględni'\n'
lokalizacji.Niektóre inne problemy z odpowiedziami:
strtok(buffer, "\n");
nie usuwa'\n'
kiedybuffer
jest"\n"
. Z tej odpowiedzi - poprawionej po tej odpowiedzi w celu ostrzeżenia o tym ograniczeniu.Następujące przypadki zawodzą w rzadkich przypadkach, gdy pierwszy
char
odczytfgets()
jest'\0'
. Dzieje się tak, gdy dane wejściowe zaczynają się od osadzenia'\0'
. Wtedybuffer[len -1]
staje siębuffer[SIZE_MAX]
dostęp do pamięci z pewnością poza uzasadnionym zakresiebuffer
. Coś, co haker może spróbować znaleźć w niemądrym czytaniu plików tekstowych UTF16. Taki był stan odpowiedzi, kiedy ta odpowiedź została napisana. Później edytor nieobsługujący OP zmodyfikował go, aby uwzględnić kod podobny do sprawdzania tej odpowiedzi""
.sprintf(buffer,"%s",buffer);
jest niezdefiniowane zachowanie: Ref . Ponadto nie zapisuje żadnych początkowych, oddzielających ani końcowych białych znaków. Teraz usunięte .[Edytuj ze względu na dobrą późniejszą odpowiedź ] Nie ma żadnych problemów z 1 wkładką
buffer[strcspn(buffer, "\n")] = 0;
poza wydajnością w porównaniu dostrlen()
podejścia. Wydajność przycinania zwykle nie stanowi problemu, ponieważ kod wykonuje operacje we / wy - czarna dziura czasu procesora. Jeśli następujący kod wymaga długości łańcucha lub jest wysoce świadomy wydajności, użyj tegostrlen()
podejścia. W przeciwnym raziestrcspn()
jest to dobra alternatywa.źródło
strlen(buffer)
gdy rozmiar bufora jest dynamicznie przydzielany za pomocąmalloc
?buffer = malloc(allocation_size); length = strlen(buffer);
jest zły - dane w pamięci wskazywane przezbuffer
są nieznane.buffer = malloc(allocation_size_4_or_more); strcpy(buffer, "abc"); length = strlen(buffer);
jest OKBezpośrednio, aby usunąć „\ n” z wyjścia fgets, jeśli każda linia ma „\ n”
Inaczej:
źródło
strnlen
zamiaststrlen
.n
nie magicznie zwiększa bezpieczeństwa, w tym przypadku w rzeczywistości uczyniłoby kod bardziej niebezpiecznym. Podobnie zstrncpy
niezwykle niebezpieczną funkcją. Wpis, do którego linkujesz, to zła rada.""
).strlen()
Zwraca równieżsize_t
nieint
.W przypadku przycinania pojedynczego „\ n”,
do wielokrotnego przycinania „\ n”,
źródło
if
Po co się zagnieżdżać, skoro można po prostu napisać jeden warunek&&
? Tawhile
pętla ma dziwną strukturę; może po prostu byćwhile (length > 0 && string[length-1] == '\n') { --length; string[length] = '\0'; }
.size_t length = strlen(string); if (length > 0 && string[length-1] == '\n') { string[length-1] = '\0'; }
. To również lepiej odzwierciedla drugą definicję (tylko użycieif
zamiastwhile
).Mój nowy sposób ;-) Daj mi znać, jeśli to prawda. Wygląda na to, że działa we wszystkich moich przypadkach:
źródło
Kroki, aby usunąć znak nowej linii w możliwie najbardziej oczywisty sposób:
NAME
za pomocąstrlen()
nagłówkastring.h
. Zauważ, żestrlen()
nie liczy się zakończenie\0
.\0
znak (pusty ciąg). W takim przypadkusl
byłoby,0
ponieważ,strlen()
jak powiedziałem powyżej, nie liczy się\0
i zatrzymuje przy pierwszym wystąpieniu:'\n'
. W takim przypadku zamień\n
na\0
. Pamiętaj, że liczenie indeksów zaczyna się od,0
więc będziemy musieli zrobićNAME[sl - 1]
:Zauważ, że jeśli naciśniesz Enter tylko na
fgets()
żądanie łańcucha (treść łańcucha składała się tylko ze znaku nowej linii), łańcuchNAME
będzie później pusty.if
zestawieniu, używając operatora logicznego&&
:Jeśli wolisz funkcję używającą tej techniki, obsługującą
fgets
ciągi wyjściowe w ogóle bez ponownego wpisywania za każdym razem, otofgets_newline_kill
:W podanym przykładzie byłoby to:
Zauważ, że ta metoda nie działa, jeśli łańcuch wejściowy ma
\0
w niej osadzone s. W takim przypadkustrlen()
zwracałaby tylko liczbę znaków do pierwszego\0
. Ale nie jest to dość powszechne podejście, ponieważ większość funkcji czytania ciągów zwykle zatrzymuje się na początku\0
i pobiera ciąg znaków aż do znaku o wartości zerowej.Oprócz samego pytania. Staraj się unikać podwójnych negacji, które czynią swój kod unclearer:
if (!(fgets(Name, sizeof Name, stdin) != NULL) {}
. Możesz po prostu zrobićif (fgets(Name, sizeof Name, stdin) == NULL) {}
.źródło
\n
z\0
na końcu ciągu znaków jest sposobem na „usuwaniu” nowej linii. Ale zastąpienie\n
znaków w ciągu zasadniczo zmienia ciąg. Często zdarza się, że ciągi znaków zawierają celowo wiele znaków nowej linii, a to skutecznie odcina ich końce. Aby usunąć takie nowe znaki, zawartość tablicy musi przesunąć w lewo, aby nadpisać\n
.fgets()
?fgets()
. Ale nie rozumiem twojego sprzeciwu: to ty proponujesz kod do obsługi wielu nowych linii.strlen
itp. Uzasadnienie braku bycia duplikatem: 1. Objaśnienie kodu krok po kroku. 2. Dostarczone jako rozwiązanie funkcyjne i kontekstowe. 3. Wskazówka, aby uniknąć wyrażeń podwójnej negacji.Jeden linijka Tim Čas jest niesamowity dla ciągów uzyskanych przez wezwanie do fgets, ponieważ wiesz, że zawierają jedną nową linię na końcu.
Jeśli jesteś w innym kontekście i chcesz obsługiwać ciągi znaków, które mogą zawierać więcej niż jeden znak nowej linii, być może szukasz strrspn. To nie jest POSIX, co oznacza, że nie znajdziesz go na wszystkich Uniksach. Napisałem jeden na własne potrzeby.
Dla tych, którzy szukają Perl chomp równoważnych w C, myślę, że to jest to (chomp usuwa tylko końcowy znak nowej linii).
Funkcja strrcspn:
źródło
'\n'
(lub jeśli ciąg jest""
).strrcspn
gdy nie ma go\n
.goto end;
zamiastreturn len;
?goto
twoim kodzie są dwa rodzaje s: bezużyteczne,goto
które można zastąpićreturn
instrukcją i wstecz,goto
który jest uważany za zły. Korzystaniestrchr
pomaga w implementacjistrrspn
istrrcspn
w prostszy sposób:size_t strrspn(const char *s, const char *accept) { size_t len = strlen(s); while (len > 0 && strchr(accept, s[len - 1])) { len--; } return len; }
orazsize_t strrcspn(const char *s, const char *reject) { size_t len = strlen(s); while (len > 0 && !strchr(reject, s[len - 1])) { len--; } return len; }
Jeśli użycie
getline
jest opcją - Nie zaniedbując jej problemów związanych z bezpieczeństwem i jeśli chcesz nawiasować wskaźniki - możesz uniknąć funkcji łańcuchowych, ponieważgetline
zwraca liczbę znaków. Coś jak poniżejUwaga : W [ kwestie bezpieczeństwa ] z
getline
nie należy lekceważyć, choć.źródło
Poniższa funkcja jest częścią biblioteki przetwarzania ciągów, którą utrzymuję w Github. Usuwa niechciane znaki z łańcucha dokładnie tego, czego chcesz
Przykładem użycia może być
Możesz sprawdzić inne dostępne funkcje, a nawet przyczynić się do projektu :) https://github.com/fnoyanisi/zString
źródło
*
w*src++;
i sprawiająbad
,token
id
const char *
. A dlaczego nie użyćstrchr
zamiastzChrSearch
?*src
nie może być'\0'
w twojejzStrrmv
funkcji.strchr
Powinieneś spróbować. Ten kod zasadniczo przechodzi przez ciąg znaków, aż znajdzie „\ n”. Po znalezieniu „\ n” zostanie zastąpione przez terminator znaku zerowego „\ 0”
Zauważ, że porównujesz znaki, a nie łańcuchy w tym wierszu, więc nie musisz używać strcmp ():
ponieważ będziesz używać pojedynczych cudzysłowów, a nie podwójnych. Oto link o pojedynczych lub podwójnych cudzysłowach, jeśli chcesz dowiedzieć się więcej
źródło
for(int i = 0; i < strlen(Name); i++ )
zadzwonistrlen(Name)
wiele razy (zmiany w pętliName[]
), więc przy długościN
jest toO(N*N)
rozwiązanie.strlen(Name)
Aby uzyskać rozwiązanie O (N), potrzebne jest tylko 1 połączenie z ewentualnym. Niejasne, dlaczegoint i
stosuje się zamiastsize_t i
. Zastanów sięfor(size_t i = 0; i < Name[i]; i++ )
for (size_t i = 0; Name[i]; i++) { if (Name[i] == '\n') { Name[i] = '\0'; break; } }
Spróbuj tego:
źródło
len = strlen(str)
może się przepełnić:strlen
zwracasize_t
, a nieint
. Co jest z tymi dziwnymiif (len>0) if (...)
warunkami? Nie wiesz o&&
? Jeśli zamierzasz usunąć wiele wystąpień CR / LF, po co ograniczać się do 5? Dlaczego nie usunąć ich wszystkich? Dlaczego funkcja maint
typ zwracany, gdy zawsze zwraca0
? Dlaczego po prostu nie wrócićvoid
?