Robię coś, w którym zdałem sobie sprawę, że chcę policzyć, ile /
s mogę znaleźć w ciągu, a potem uderzyło mnie, że istnieje kilka sposobów, ale nie mogłem zdecydować, co jest najlepsze (lub najłatwiejsze) .
W tej chwili idę z czymś takim jak:
string source = "/once/upon/a/time/";
int count = source.Length - source.Replace("/", "").Length;
Ale w ogóle mi się to nie podoba, jakieś chętne?
Naprawdę nie chcę RegEx
tego robić, prawda?
Wiem, że mój ciąg będzie zawierał szukany termin, więc możesz założyć, że ...
Oczywiście dla ciągów, których długość> 1 ,
string haystack = "/once/upon/a/time";
string needle = "/";
int needleCount = ( haystack.Length - haystack.Replace(needle,"").Length ) / needle.Length;
LEN(ColumnToCheck) - LEN(REPLACE(ColumnToCheck,"N",""))
.Odpowiedzi:
Jeśli korzystasz z .NET 3.5, możesz to zrobić w jednym linku z LINQ:
Jeśli nie chcesz korzystać z LINQ, możesz to zrobić za pomocą:
Możesz być zaskoczony, gdy dowiesz się, że Twoja oryginalna technika wydaje się być o około 30% szybsza niż którakolwiek z nich! Właśnie wykonałem szybki test porównawczy z „/ once / upon / a / time /”, a wyniki są następujące:
(Czasy dotyczą 50 000 000 iteracji, więc nie zauważysz dużej różnicy w prawdziwym świecie).
źródło
f == '\'
dotyczy znaków w łańcuchu, a nie łańcuchów w łańcuchuMusi być szybszy niż
source.Replace()
sam.źródło
źródło
RegexOptions.IgnoreCase
.Regex.Escape(...)
taknew System.Text.RegularExpressions.Regex(needle).Matches(haystack).Count;
Jeśli chcesz wyszukiwać całe ciągi, a nie tylko znaki:
Odczytaj jako „dla każdego znaku w ciągu, weź resztę łańcucha zaczynając od tego znaku jako podłańcuch; policz go, jeśli zaczyna się od ciągu docelowego”.
źródło
Przeprowadziłem badania i odkryłem, że rozwiązanie Richarda Watsona jest najszybsze w większości przypadków. To jest tabela z wynikami każdego rozwiązania w poście (z wyjątkiem tych, które używają Regex, ponieważ zgłasza wyjątki podczas analizowania łańcucha, np. „Test {test”)
Możesz zauważyć, że w przypadku znalezienia liczby wystąpień krótkich podciągów (1-5 znaków) w krótkim ciągu (10-50 znaków) preferowany jest oryginalny algorytm.
Ponadto w przypadku podciągów wieloznakowych należy użyć następującego kodu (opartego na rozwiązaniu Richarda Watsona )
źródło
Regex.Escape(needle)
source="aaa" substring="aa"
spodziewałem się, że odzyskam 2, a nie 1. Aby to naprawić, zmieńn += substring.Length
nan++
overlapped
flagę do swojej sprawy w ten sposób:overlapped=True;.... if(overlapped) {++n;} else {n += substring.Length;}
LINQ działa na wszystkich kolekcjach, a ponieważ ciągi są tylko zbiorem znaków, co powiesz na ten ładny, mały linijka:
Upewnij się, że masz
using System.Linq;
na górze pliku kodu, ponieważ.Count
jest to metoda rozszerzenia z tej przestrzeni nazw.źródło
int
wszystkie litery znajdują się w klawiszach domowych, podczas gdyvar
nie. uh .. czekaj, używam DvorakNa moim komputerze jest to około 2 sekundy szybsze niż rozwiązanie dla każdej postaci na 50 milionów iteracji.
Wersja 2013:
Zmień ciąg na char [] i iteruj przez to. Skraca o kolejny sekundę lub dwie całkowity czas iteracji 50m!
Jest to jeszcze szybsze:
Dla pewności, iteracja od końca tablicy do 0 wydaje się najszybsza, o około 5%.
Zastanawiałem się, dlaczego to mogło być i było Googling w okolicy (przypominam sobie coś o szybszym powtarzaniu iteracji) i natknąłem się na to pytanie SO, które irytująco używa techniki strun do char [] już. Myślę jednak, że sztuczka polegająca na odwróceniu jest nowa w tym kontekście.
Jaki jest najszybszy sposób na iterację pojedynczych znaków w ciągu w C #?
źródło
source.IndexOf('/', n + 1)
i zgubićn++
nawiasy klamrowe chwile :) Równieżstring word = "/"
zamiast znaku wstaw zmienną .źródło
Oba działają tylko w przypadku wyszukiwanych znaków składających się z jednego znaku ...
może okazać się lepszy w przypadku dłuższych igieł ...
Ale musi być bardziej elegancki sposób. :)
źródło
Edytować:
źródło
source.Split(new[]{"//"}, StringSplitOptions.None).Count - 1
dla separatorów wieloznakowych.W języku C # niezłym licznikiem SubString jest ten niespodziewanie trudny facet:
źródło
źródło
stringToMatch
potrzeby uciekają, a nieinput
.Ponieważ oryginalne rozwiązanie było najszybsze dla znaków, przypuszczam, że będzie również dla ciągów znaków. Oto mój wkład.
W kontekście: szukałem w pliku dziennika takich słów, jak „nie powiodło się” i „udało się”.
Gr, Ben
źródło
źródło
Dla każdego, kto chce mieć gotową do użycia metodę rozszerzenia String,
Oto, czego używam, który został oparty na najlepszych z opublikowanych odpowiedzi:
źródło
źródło
Myślę, że najłatwiejszym sposobem na to jest użycie wyrażeń regularnych. W ten sposób możesz uzyskać taką samą liczbę podziałów, jak przy użyciu myVar.Split („x”), ale w ustawieniu wielu znaków.
źródło
To będzie się liczyć za każdym razem, gdy program znajdzie „/ s” dokładnie (rozróżnia małe i wielkie litery), a liczba wystąpień tego zdarzenia zostanie zapisana w zmiennej „wystąpienia”
źródło
Czułem, że brakuje nam pewnego rodzaju liczenia napisów, takich jak niebezpieczne porównania bajt po bajcie. Złożyłem oryginalną metodę plakatu i wszelkie metody, jakie mogłem wymyślić.
To są rozszerzenia, które utworzyłem.
Następnie kod testowy ...
Wyniki: CSX odpowiada CountSubstrX, a CCX odpowiada CountCharX. „chr” wyszukuje ciąg „_”, „i” wyszukuje ciąg „i”, a „mlw” wyszukuje ciąg „longlongerword”
I w końcu miałem plik zawierający 3,6 miliona znaków. Było to „derp adfderdserp dfaerpderp deasderp” powtórzone 100 000 razy. 100 razy te wyniki szukałem „derp” w pliku za pomocą powyższych metod.
Tak więc moja czwarta metoda jest zdecydowanie zwycięzcą, ale, realistycznie, jeśli plik 3,6 miliona znaków 100 razy zajęłby 1586 ms jako najgorszy przypadek, to wszystko to jest bardzo znikome.
Nawiasem mówiąc, przeskanowałem również znak „d” w pliku 3,6 miliona znaków przy użyciu 100 razy metod CountSubstr i CountChar. Wyniki ...
Zgodnie z tym oryginalna metoda plakatów jest bardzo zła dla igieł jednoznakowych w dużym stogu siana.
Uwaga: Wszystkie wartości zostały zaktualizowane do danych wyjściowych wersji Release. Przypadkowo zapomniałem zbudować tryb Release przy pierwszym opublikowaniu tego. Niektóre z moich oświadczeń zostały zmienione.
źródło
Ogólna funkcja dotycząca występowania ciągów:
źródło
Odmiana odpowiedzi Richarda Watsona, nieco szybsza wraz ze wzrostem wydajności, im więcej razy znak występuje w ciągu, i mniej kodu!
Chociaż muszę powiedzieć, że bez szczegółowego testowania każdego scenariusza, zauważyłem bardzo znaczącą poprawę prędkości dzięki zastosowaniu:
źródło
Musiałem zrobić coś podobnego do testowania instrukcji warunkowych z łańcucha.
Zamieniłem to, czego szukałem, na jeden znak i policzyłem wystąpienia tego znaku.
Oczywiście pojedynczy znak, którego używasz, musi zostać zaznaczony, aby nie istniał w ciągu, aby uniknąć niepoprawnego liczenia.
źródło
Ciąg w ciągu:
Znajdź „etc” w „.. JD JD JD JD itp. I itp. JDJDJDJDJDJDJDJD i itp.”
Sprawdź wydajność, zanim odrzucisz tę jako nieudolną / niezdarną ...
źródło
Moje pierwsze ujęcie dało mi coś takiego:
Igła w podejściu do stogu siana przy użyciu zamiany i podziału daje ponad 21 sekund, podczas gdy zajmuje to około 15,2.
Edytuj po dodaniu bitu, który by dodał
substring.Length - 1
dodałby do charIndex (tak jak powinien), ma 11,6 sekundy.Edycja 2: Użyłem ciągu, który miał 26 ciągów dwóch znaków, oto czasy zaktualizowane do tych samych przykładowych tekstów:
Igła w stogu siana (wersja OP): 7,8 sekundy
Sugerowany mechanizm: 4,6 sekundy.
Edycja 3: Dodanie pojedynczego znaku narożnego przypadku zajęło 1,2 sekundy.
Edycja 4: Dla kontekstu: użyto 50 milionów iteracji.
źródło
Pomyślałem, że wrzucę moją metodę rozszerzenia do ringu (zobacz komentarze, aby uzyskać więcej informacji). Nie przeprowadziłem formalnego testu porównawczego, ale myślę, że musi być bardzo szybki w większości scenariuszy.
EDYCJA: OK - więc to pytanie SO skłoniło mnie do zastanowienia się, jak wypadałoby nasze obecne wdrożenie w porównaniu z niektórymi rozwiązaniami przedstawionymi tutaj. Postanowiłem zrobić małe wyciskanie na ławce i stwierdziłem, że nasze rozwiązanie było bardzo zgodne z wydajnością rozwiązania dostarczonego przez Richarda Watsona aż do agresywnego wyszukiwania z dużymi ciągami (100 Kb +), dużymi podciągami (32 Kb + ) i wiele osadzonych powtórzeń (10 KB +). W tym momencie nasze rozwiązanie było około 2 do 4 razy wolniejsze. Biorąc pod uwagę to i fakt, że naprawdę podoba nam się rozwiązanie przedstawione przez Richarda Watsona, odpowiednio zmodyfikowaliśmy nasze rozwiązanie. Chciałem tylko udostępnić to każdemu, kto mógłby z tego skorzystać.
Nasze oryginalne rozwiązanie:
Oto nasze zmienione rozwiązanie:
źródło
źródło
Sprawdza tylko każdy znak w ciągu, jeśli znak jest poszukiwanym znakiem, dodaj jeden, aby policzyć.
źródło
Jeśli przejrzysz tę stronę , zostanie przetestowanych 15 różnych sposobów, w tym przy użyciu równoległych pętli.
Najszybszym sposobem wydaje się być użycie jednowątkowej pętli for (jeśli masz .Net w wersji <4.0) lub równoległej pętli for (jeśli używasz .Net> 4.0 z tysiącami kontroli).
Zakładając, że „ss” jest ciągiem wyszukiwania, „ch” to tablica znaków (jeśli masz więcej niż jeden znak, którego szukasz), oto podstawowa treść kodu, który miał najszybszy jednowątkowy czas działania:
Dostarczono również kod źródłowy testu porównawczego, abyś mógł uruchomić własne testy.
źródło
Służy do liczenia występowania postaci. W tym przykładzie wynikiem będzie „a4b4j3”
źródło
W przypadku ogranicznika łańcucha (nie dla znaku, jak mówi podmiot):
łańcuch źródła = "@@@ raz @@@ na @@@ @ @@ czas @@@";
int count = source.Split (new [] {"@@@"}, StringSplitOptions.RemoveEmptyEntries) .Length - 1;
Naturalnym ogranicznikiem oryginalnej wartości źródłowej plakatu („/ once / upon / a / time /”) jest char '/ ”, a odpowiedzi wyjaśniają opcję source.Split (char []) ...
źródło
using System.Linq;
int CountOf => "A :: BC :: D" .Split ("::"). Długość - 1;
źródło