2016-02-07 17 views
5

Widziałem kilka przykładów, w których ludzie przekazują scanf ciąg znaków w formacie "%[^\n]\n", aby odczytać całą linię danych wprowadzanych przez użytkownika. Jeśli moje zrozumienie jest poprawne, przeczyta każdy znak, dopóki nie zostanie osiągnięty znak nowego wiersza, a następnie nowy wiersz zostanie zużyty przez scanf (i nie zostanie uwzględniony w wynikowym wprowadzeniu).Dlaczego ten ciąg formatu scanf nie działa? "% [^ n] n"

Ale nie mogę tego uruchomić na mojej maszynie. Prostym przykładem Próbowałem:

#include <stdio.h> 

int main(void) 
{ 
    char input[64]; 

    printf("Enter some input: "); 
    scanf("%[^\n]\n", input); 

    printf("You entered %s\n", input); 
} 

Kiedy uruchamiam to mam poproszony o wejście, wpisuję kilka znaków, I naciśnij Enter, a kursor przechodzi do początku następnego wiersza, ale scanf połączenie nie kończy się.

scanf not finished

mogę naciśnij Enter tyle razy, ile mi się podoba, i nigdy nie będzie końca.

scanf still not finished

jedynymi sposobami Znalazłem zawrzeć wezwanie scanf są:

  • wprowadzić \n jako pierwszy (i jedyny) w charakterze szybkiego
  • wprowadzić Ctrl-D jako pierwszy (i jedyny) znak na zachęcie
  • wprowadź dane wejściowe, jeden lub więcej \n, zero lub więcej innych znaków, i wprowadź Ctrl-d

Nie wiem, czy to zależy od komputera, ale jestem bardzo ciekaw, co się dzieje. Jestem na OS X, jeśli to istotne.

+3

Nie zamieszczaj obrazów tekstu! – Olaf

+1

to% [^ \ n], spróbuj tego –

+0

@ user3121023 Dzięki, właśnie dlatego chciałem użyć '"% [^ \ n] \ n ". – ivan

Odpowiedz

5

Według documentation dla scanf (nacisk moje):

Ciąg formatu składa się z białych znaków (każdy pojedynczy biały znak w ciągu formatu zużywa cały dostępny kolejne znaki odstępu od wejścia), wielobajtowe znaki spoza białych znaków, z wyjątkiem % (każdy taki znak w ciągu znaków formatowania pochłania dokładnie jedną identyczną e wejście) i specyfikacje konwersji.

Zatem Twój format string %[^\n]\n najpierw przeczytać (i przechowywać) dowolna liczba znaków nie-białych z wejścia (ze względu na %[^\n] części), a następnie, z powodu następującego linią, czytać (i odrzucić) dowolna liczba białych znaków, takich jak spacje, tabulacje lub znaki nowej linii.

Dlatego, aby uczynić swoje wejście STOP scanf czyta, albo trzeba wpisać przynajmniej jeden non-białymi znak po znaku nowej linii, albo zorganizować strumień wejściowy do końca (na przykład przez naciśnięcie Ctrl + D w systemach Unix-ish).

Zamiast tego, aby twój kod działał zgodnie z oczekiwaniami, po prostu usuń ostatnie \n z końca łańcucha formatu (jak już sugerował Umamahesh P).

Oczywiście spowoduje to pozostawienie znaku nowej linii w strumieniu wejściowym. Aby się go pozbyć (na wypadek, gdybyś chciał przeczytać inną linię później), możesz getc ze strumienia lub po prostu dołączyć %*c (co oznacza "odczytaj jeden znak i odrzuć go") lub nawet %*1[\n] (przeczytaj jeden znak nowej linii i odrzuć it) na końcu łańcucha formatu scanf.

Ps. Pamiętaj, że Twój kod ma kilka innych problemów. Na przykład, aby uniknąć błędów przepełnienia bufora, naprawdę powinieneś użyć %63[^\n] zamiast %[^\n], aby ograniczyć liczbę znaków, które scanf odczyta do twojego bufora. (Limit musi być o jeden mniejszy niż rozmiar twojego bufora, ponieważ scanf będzie zawsze dołączał końcowy pusty znak).

Specyfikacja formatu %[ zawsze oczekuje co najmniej jednego pasującego znaku i zakończy się niepowodzeniem, jeśli żadna nie jest dostępny. Tak więc, jeśli naciśniesz enter natychmiast bez wpisywania czegokolwiek, twój scanf zawiedzie (cicho, ponieważ nie sprawdzasz wartości zwracanej) i pozostawi bufor wejściowy wypełniony losowymi śmieciami. Aby tego uniknąć, powinieneś a) sprawdzić wartość zwracaną przez scanf, b) ustawić input[0] = '\0' przed wywołaniem scanf lub c) najlepiej oba.

Na koniec zwróć uwagę, że jeśli chcesz odczytywać linię wejściową po linii, to jest to o wiele łatwiejsze: łatwiej użyć po prostu fgets. Tak, jeśli nie chcesz, musisz usunąć sam znak końca linii (jeśli jest), ale jest to o wiele łatwiejsze i bezpieczniejsze niż próba użycia skanera do wykonania zadania, które nie jest przeznaczone dla:

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

void chomp(char *string) { 
    int len = strlen(string); 
    if (len > 0 && string[len-1] == '\n') string[len-1] = '\0'; 
} 

int main(void) 
{ 
    char input[64]; 

    printf("Enter some input: "); 
    fgets(input, sizeof(input), stdin); 
    chomp(input); 

    printf("You entered \"%s\".\n", input); 
} 
+1

Twoja odpowiedź świetnie wyjaśnia zarówno to, co się dzieje, jak i jak prawidłowo nią manipulować. Używam scanf, ponieważ jest to w kontekście ćwiczenia w książce, z której pracuję, i pominąłem kilka szczegółów, aby pytanie stało się jaśniejsze, ale twoje punkty dotyczące fgetów i przepełnienia bufora są należycie odnotowane. Dzięki! – ivan

1

Usuń drugi znak nowego wiersza i wystarczy poniższe.

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

Aby odpowiedzieć oryginalny jeden,

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

ten powinien również pracować pod warunkiem, wprowadzić zakaz biały znak spacji po wejściu. Przykład:

Enter some input: lkfjdlfkjdlfjdlfjldj 
t 
You entered lkfjdlfkjdlfjdlfjldj 
+1

Ta odpowiedź zostałaby znacznie poprawiona dzięki wyjaśnieniu * dlaczego * jest wystarczająca i dlaczego rozsądna składnia OP nie działa. –

+0

Zmodyfikowaliśmy odpowiedź, aby podać więcej szczegółów. Dzięki. –

+0

Powinno być "\\ n" zamiast "\ n" w kodzie w twoim przykładzie. –

3

białych znaków w format z scanf() ma szczególne znaczenie:

biały znak: funkcja odczytu i ignoruje wszystkie spacje znaki występujące przed następnym non-spacją (białe znaki znaki zawierają spacje, znaki nowej linii i tabulatory - patrz isspace). Pojedynczy spacja w ciągu znaków formatuje dowolną liczbę białych znaków wyodrębnionych ze strumienia (w tym brak).

Zatem "%[^\n]\n" jest po prostu odpowiednikiem "%[^\n] ", mówiąc scanf() ignorować wszystkie białe znaki po %[^\n]. Dlatego wszystkie '\n' s są ignorowane, dopóki nie zostanie wprowadzony znak odstępu, co ma miejsce w twoim przypadku.

referencyjny: http://www.cplusplus.com/reference/cstdio/scanf/