Ta funkcja sprawdza czy podłańcuch needle
istnieje w innym ciągu haystack
i zwraca pozycję, jeśli tak, lub 0, jeśli nie, chyba że pozycja wynosi 0, w takim przypadku nie zostanie zlokalizowana.
Poszukiwanie sposobów na ulepszenie tego kodu, w szczególności lepszej obsługi błędów.
#include <stdio.h> #include <stdlib.h> #include <string.h> size_t contains(const char * needle, const char *haystack); int main(void) { char *needle = "test"; char *haystack = "This is a dinosaurtest."; printf("Position: %lu", contains(needle, haystack)); return EXIT_SUCCESS; } size_t contains(const char * needle, const char *haystack) { if(needle == NULL || haystack == NULL) { return 0; } long int first_char_pos = -1; size_t len_h = strlen(haystack); size_t len_n = strlen(needle); size_t i, j; size_t exist_count = 0; // Find the first character. If it doesn"t exist, we"re done. for(i = 0; i < len_h; i++) { if((haystack[i] == needle[0]) && (first_char_pos == -1)) { first_char_pos = i; exist_count++; } } if(first_char_pos == -1) { return 0; } printf("First char match index: %li\n", first_char_pos); printf("Char: %c\n", haystack[first_char_pos]); size_t current_index = (size_t) first_char_pos; for(i = first_char_pos; i < len_h; i++) { if(haystack[i] == needle[exist_count] && (i == (current_index + 1))) { current_index = i; exist_count++; } printf("Exist count: %lu\n", exist_count); //<--Debugging if(exist_count == len_n) { return first_char_pos; } } return 0; }
Komentarze
Odpowiedź
Tylko kilka uwag:
-
Należy dodać nową linię po ostatniej linii:
$ ./nh First char match index: 18 Char: t Exist count: 1 Exist count: 2 Exist count: 3 Exist count: 4 Position: 18 $
-
Nie wiem co kompilator, którego używasz, ale skompilowany z
gcc
i-Wall -Wextra -pedantic
otrzymujesz:gcc -O2 nh.c -lm -o nh -Wall -Wextra -pedantic nh.c: In function ‘contains’: nh.c:25:15: warning: unused variable ‘j’ [-Wunused-variable] size_t i, j; ^
-
Formatowanie kodu powinno być bardziej spójne. Na przykład w tym wierszu umieszcza się białą spację przed
needle
, ale nie należy umieszczać spacji przedhaystack
:size_t contains(const char * needle, const char *haystack);
-
%lu
nie jest przenośnym specyfikatorem dla typusize_t
, należy użyć%zu
wprowadzonego w C99. -
Powiedziałeś:
zwraca pozycję, jeśli tak, lub 0, jeśli tak nie jest, chyba że pozycja wynosi 0, w takim przypadku nie zostanie zlokalizowana.
To naprawdę nie jest dobre. Na przykład z tym zwraca 0:
char *needle = "This"; char *haystack = "This is a dinosaurtest.";
W tym przypadku zwraca również zero:
char *needle = "non-existent"; char *haystack = "This is a dinosaurtest.";
W tych dwóch przykładach nie można odróżnić sukcesu od porażki. W rzeczywistości atoi()
ma ten sam problem. Nie wiem, jakiego systemu operacyjnego używasz ale może mógłbyś użyć ssize_t
jako typu zwracanego, jeśli jest dostępny i zwrócić -1 w przypadku niepowodzenia.
Odpowiedź
Dodanie do poprzedniej odpowiedzi autorstwa @Arkadiusza Drabczyka:
Prosta, trywialna implementacja contains
może być zrobione w ten sposób:
ssize_t contains(const char * needle, const char *haystack) { char *needle_in_haystack; if(!needle || !haystack) return -1; needle_in_haystack = strstr(haystack, needle); return needle_in_haystack ? needle_in_haystack - haystack : -1; }
Następnie ten program (z kilkoma zmianami, jak wspomniano powyżej) powinien działać:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> ssize_t contains(const char * needle, const char *haystack) { char *needle_in_haystack; if(!needle || !haystack) return -1; needle_in_haystack = strstr(haystack, needle); return needle_in_haystack ? needle_in_haystack - haystack : -1; } int main(void) { char *needle = "test"; char *haystack = "This is a dinosaurtest."; char *haystack2 = "This does not contain the string."; printf("Position: %zd\n", contains(needle, haystack)); printf("Position: %zd\n", contains(needle, haystack2)); return EXIT_SUCCESS; }
Wynik:
Stanowisko: 18
Stanowisko: -1
Komentarze
- Chciałbym usunąć sprawdzanie, czy dane wejściowe nie mają wartości NULL i ju st użyj do tego rozszerzenia języka (
__attribute__((nonnull))
w GCC).NULL
to coś, czego nigdy byś się nie spodziewał jako dane wejściowe dla tej funkcji, i dodaje jeden lub dwa niepotrzebne wiersze kodu. Wolałbym napisać w dokumentacji funkcji coś takiego: ” Jeśli dane wejściowe do tej funkcji to wskaźnik NULL, zachowanie jest niezdefiniowane. „. - @CacahueteFrito Wykonał to oryginalny kod, a ja chcę dążyć do kompatybilności (kto wie, jak korzystał z tego OP?).
- Brak uwzględnienia dla
ssize_t
:#include <sys/types.h>
. Inną opcją byłoby użycie zamiast tegoptrdiff_t
z#include <stddef.h>
; w rzeczywistości zwracasz różnicę wskaźnika:? needle_in_haystack - haystack :
Odpowiedź
Twój kod nie „nie działa. Zwraca 0
dla haystack
"abbc"
i needle
"bc"
, mimo że haystack
zawiera needle
.
Odpowiedź
Nie potrzebujesz pierwszej pętli i wszystkich obliczeń długości. Przy okazji, funkcja nie powiedzie się, jeśli pierwszy znak zostanie znaleziony, ale tylko drugie wystąpienie pierwszego znaku pasuje do igły.
Zadanie można zredukować do kilku wierszy:
int contains(char *buf, char *needle) { char *src, *srch, *srcp; for(src=buf; *src; src++) { for(srch = needle, srcp = src; *srch && *srcp && *srch == *srcp; srch++, srcp++); if(!*srch) return src - buf; } return -1; }
Komentarze
- Jaki jest dobry sposób na ulepszenie pisania bardziej zwartego i wydajnego kodu C, takiego jak ten ?To trochę przypomina mi K & R C.
strstr()
. Jest ' jest bezpieczniejsza wersja o nazwiestrnstr()
. Implementację można znaleźć tutaj: github.com/lattera/freebsd/blob/master/lib/libc/string/…contains("tt", "test")
zwraca prawdę?