Używam dużych plików danych, czasem muszę tylko znać liczbę wierszy w tych plikach, zwykle otwieram je i czytam wiersz po wierszu, aż dojdę do końca pliku
Zastanawiałem się, czy jest na to lepszy sposób
Używam dużych plików danych, czasem muszę tylko znać liczbę wierszy w tych plikach, zwykle otwieram je i czytam wiersz po wierszu, aż dojdę do końca pliku
Zastanawiałem się, czy jest na to lepszy sposób
To najszybsza wersja, jaką do tej pory znalazłem, około 6 razy szybsza niż readLines. W przypadku pliku dziennika 150 MB zajmuje to 0,35 sekundy, w porównaniu do 2,40 sekundy w przypadku użycia readLines (). Dla zabawy polecenie wc -l linuksa zajmuje 0,15 sekundy.
public static int countLinesOld(String filename) throws IOException {
InputStream is = new BufferedInputStream(new FileInputStream(filename));
try {
byte[] c = new byte[1024];
int count = 0;
int readChars = 0;
boolean empty = true;
while ((readChars = is.read(c)) != -1) {
empty = false;
for (int i = 0; i < readChars; ++i) {
if (c[i] == '\n') {
++count;
}
}
}
return (count == 0 && !empty) ? 1 : count;
} finally {
is.close();
}
}
EDYCJA, 9 1/2 lat później: praktycznie nie mam doświadczenia w Javie, ale tak czy inaczej próbowałem porównać ten kod z LineNumberReader
rozwiązaniem poniżej, ponieważ przeszkadzało mi, że nikt tego nie zrobił. Wydaje się, że szczególnie w przypadku dużych plików moje rozwiązanie jest szybsze. Chociaż wydaje się, że optymalizacja zajmuje sporo czasu. Grałem trochę z kodem i stworzyłem nową wersję, która jest konsekwentnie najszybsza:
public static int countLinesNew(String filename) throws IOException {
InputStream is = new BufferedInputStream(new FileInputStream(filename));
try {
byte[] c = new byte[1024];
int readChars = is.read(c);
if (readChars == -1) {
// bail out if nothing to read
return 0;
}
// make it easy for the optimizer to tune this loop
int count = 0;
while (readChars == 1024) {
for (int i=0; i<1024;) {
if (c[i++] == '\n') {
++count;
}
}
readChars = is.read(c);
}
// count remaining characters
while (readChars != -1) {
System.out.println(readChars);
for (int i=0; i<readChars; ++i) {
if (c[i] == '\n') {
++count;
}
}
readChars = is.read(c);
}
return count == 0 ? 1 : count;
} finally {
is.close();
}
}
Benchmark uruchamia się dla pliku tekstowego 1,3 GB, oś y w sekundach. Wykonałem 100 przebiegów z tym samym plikiem i zmierzyłem każdy z nich System.nanoTime()
. Widać, że countLinesOld
ma kilka wartości odstających i countLinesNew
nie ma go, a chociaż jest tylko nieco szybszy, różnica jest istotna statystycznie. LineNumberReader
jest wyraźnie wolniejszy.
Wdrożyłem inne rozwiązanie problemu, uważam, że jest bardziej wydajne w liczeniu wierszy:
źródło
LineNumberReader
„slineNumber
pole jest liczbą całkowitą ... nie będzie to tylko owinąć plików dłużej niż Integer.MAX_VALUE? Po co męczyć się tutaj tak długo?wc -l
zlicza znaki nowego wiersza w pliku. Działa to, ponieważ każda linia kończy się nową linią, w tym ostatnią linią w pliku. Każda linia ma znak nowej linii, w tym puste linie, stąd liczba znaków nowej linii == liczba linii w pliku. TerazlineNumber
zmienna wFileNumberReader
reprezentuje również liczbę znaków nowego wiersza, które widzimy. Zaczyna się od zera, zanim zostanie znaleziona nowa linia, i jest zwiększana z każdym znakiem nowej linii. Więc nie dodawaj go do numeru linii.wc -l
też raportuje ten rodzaj pliku. Zobacz także stackoverflow.com/questions/729692/…wc -l
zwróci 1. Doszedłem do wniosku, że wszystkie metody mają wady i zaimplementowałem jedną w oparciu o to, jak chciałbym się zachowywać, zobacz moją drugą odpowiedź tutaj.Zaakceptowana odpowiedź ma wyłączony jeden błąd dla plików wieloliniowych, które nie kończą się na nowej linii. Plik jednowierszowy kończący się bez nowej linii zwraca 1, ale plik dwuliniowy kończący się bez nowej linii również zwraca 1. Oto implementacja zaakceptowanego rozwiązania, które to rozwiązuje. Kontrole EndWithoutNewLine są marnotrawstwem dla wszystkiego oprócz odczytu końcowego, ale powinny być trywialne pod względem czasu w porównaniu do ogólnej funkcji.
źródło
Z java-8, możesz używać strumieni:
źródło
Odpowiedź z powyższą metodą count () dała mi błędne informacje o wierszach, jeśli plik nie miał nowej linii na końcu pliku - nie udało się zliczyć ostatniej linii w pliku.
Ta metoda działa dla mnie lepiej:
źródło
cnt
.Wiem, że to stare pytanie, ale zaakceptowane rozwiązanie nie do końca pasuje do tego, czego potrzebowałem. Udoskonaliłem go tak, aby akceptował różne terminatory linii (zamiast tylko przejścia do nowego wiersza) i używał określonego kodowania znaków (zamiast ISO-8859- n ). Wszystko w jednej metodzie (odpowiednio refaktor):
To rozwiązanie jest porównywalne pod względem prędkości do przyjętego rozwiązania, około 4% wolniej w moich testach (chociaż testy czasowe w Javie są notorycznie niewiarygodne).
źródło
Przetestowałem powyższe metody zliczania linii i oto moje obserwacje dotyczące różnych metod testowanych w moim systemie
Rozmiar pliku: 1,6 Gb Metody:
Co więcej, podejście Java8 wydaje się całkiem przydatne:
źródło
Testowane na JDK8_u31. Ale w rzeczywistości wydajność jest niska w porównaniu do tej metody:
Testowany i bardzo szybki.
źródło
Stream<String> - Time consumed: 122796351 Stream<String> - Num lines: 109808 Method - Time consumed: 12838000 Method - Num lines: 1
Liczba wierszy jest nawet błędnaBufferedInputStream
jeśli i tak chcesz czytać w swoim buforze. Poza tym, nawet jeśli twoja metoda może mieć niewielką przewagę wydajnościową, traci elastyczność, ponieważ nie obsługuje już\r
terminatorów pojedynczych linii (stary MacOS) i nie obsługuje każdego kodowania.Prosty sposób za pomocą skanera
źródło
Doszedłem do wniosku, że
wc -l
: metoda liczenia znaków nowej linii jest w porządku, ale zwraca nieintuicyjne wyniki w plikach, w których ostatni wiersz nie kończy się znakiem nowego wiersza.I rozwiązanie @ er.vikas oparte na LineNumberReader, ale dodanie jednego do liczby wierszy zwróciło nieintuicyjne wyniki w plikach, w których ostatni wiersz kończy się znakiem nowej linii.
Dlatego stworzyłem algo, które działa w następujący sposób:
I wygląda to tak:
Jeśli chcesz uzyskać intuicyjne wyniki, możesz tego użyć. Jeśli chcesz tylko
wc -l
kompatybilności, użyj prostego rozwiązania @ er.vikas, ale nie dodawaj jednego do wyniku i ponów próbę:źródło
Co powiesz na użycie klasy Process z kodu Java? A następnie odczytanie wyniku polecenia.
Trzeba jednak spróbować. Opublikuje wyniki.
źródło
Jeśli nie masz żadnych struktur indeksu, nie obejdziesz się odczytywania pełnego pliku. Ale możesz to zoptymalizować, unikając czytania linii po linii i użyj wyrażenia regularnego, aby dopasować wszystkie terminatory linii.
źródło
To zabawne rozwiązanie działa naprawdę dobrze!
źródło
W systemach uniksowych użyj
wc
polecenia w wierszu polecenia.źródło
Jedynym sposobem, aby dowiedzieć się, ile wierszy jest w pliku, jest ich policzenie. Możesz oczywiście utworzyć metrykę na podstawie danych, podając średnią długość jednej linii, a następnie uzyskać rozmiar pliku i podzielić go za pomocą śr. długość, ale to nie będzie dokładne.
źródło
Najlepszy kod zoptymalizowany dla plików wieloliniowych nieposiadających znaku nowej linii („\ n”) w EOF.
źródło
Skaner z wyrażeniem regularnym:
Nie zarejestrowałem tego.
źródło
jeśli tego użyjesz
nie możesz biegać do dużych liczb, lubi 100 000 wierszy, ponieważ return from reader.getLineNumber is int. potrzebujesz długiego typu danych do przetworzenia maksymalnej liczby wierszy ..
źródło
int
Może pomieścić maksymalnie do wartości, w przybliżeniu, 2 mld. Jeśli ładujesz plik zawierający ponad 2 miliardy linii, masz problem z przepełnieniem. To powiedziawszy, jeśli ładujesz nieindeksowany plik tekstowy z ponad dwoma miliardami linii, prawdopodobnie masz inne problemy.