Jawaban:
Ini adalah versi tercepat yang saya temukan sejauh ini, sekitar 6 kali lebih cepat daripada readLines. Pada file log 150MB ini membutuhkan 0,35 detik, dibandingkan 2,40 detik saat menggunakan readLines (). Hanya untuk bersenang-senang, perintah linux 'wc -l membutuhkan waktu 0,15 detik.
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();
}
}
EDIT, 9 1/2 tahun kemudian: Praktis saya tidak punya pengalaman java, tapi bagaimanapun saya telah mencoba untuk membandingkan kode ini dengan LineNumberReader
solusi di bawah ini karena itu mengganggu saya bahwa tidak ada yang melakukannya. Tampaknya terutama untuk file besar solusi saya lebih cepat. Meskipun tampaknya perlu beberapa kali hingga pengoptimal melakukan pekerjaan yang layak. Saya telah bermain sedikit dengan kode, dan telah menghasilkan versi baru yang secara konsisten tercepat:
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();
}
}
Resuls benchmark untuk file teks 1.3GB, sumbu y dalam hitungan detik. Saya telah melakukan 100 berjalan dengan file yang sama, dan diukur masing-masing berjalan dengan System.nanoTime()
. Anda dapat melihat bahwa countLinesOld
memiliki beberapa outlier, dan countLinesNew
tidak memiliki outlier dan meskipun hanya sedikit lebih cepat, perbedaannya signifikan secara statistik. LineNumberReader
jelas lebih lambat.
Saya telah menerapkan solusi lain untuk masalah ini, saya merasa lebih efisien dalam menghitung baris:
try
(
FileReader input = new FileReader("input.txt");
LineNumberReader count = new LineNumberReader(input);
)
{
while (count.skip(Long.MAX_VALUE) > 0)
{
// Loop just in case the file is > Long.MAX_VALUE or skip() decides to not read the entire file
}
result = count.getLineNumber() + 1; // +1 because line index starts at 0
}
LineNumberReader
's lineNumber
lapangan adalah bilangan bulat ... Akan bukan hanya membungkus untuk file lama dari Integer.MAX_VALUE? Kenapa repot-repot melewatkan waktu lama di sini?
wc -l
menghitung jumlah karakter baris baru dalam file. Ini berfungsi karena setiap baris diakhiri dengan baris baru, termasuk baris terakhir dalam file. Setiap baris memiliki karakter baris baru, termasuk baris kosong, sehingga jumlah baris baris baru == jumlah baris dalam file. Sekarang, lineNumber
variabel dalam FileNumberReader
juga mewakili jumlah karakter baris baru yang terlihat. Itu dimulai dari nol, sebelum baris baru ditemukan, dan ditingkatkan dengan setiap baris baris baru dilihat. Jadi jangan menambahkan satu ke nomor baris.
wc -l
juga cara melaporkan jenis file ini. Lihat juga stackoverflow.com/questions/729692/…
wc -l
akan kembali 1. Saya menyimpulkan bahwa semua metode memiliki kekurangan, dan menerapkan satu berdasarkan pada bagaimana saya ingin berperilaku, lihat jawaban saya yang lain di sini.
Jawaban yang diterima memiliki kesalahan satu demi satu untuk file multi-baris yang tidak berakhir pada baris baru. File satu baris yang berakhir tanpa baris baru akan mengembalikan 1, tetapi file dua baris yang berakhir tanpa baris baru akan mengembalikan 1 juga. Berikut ini adalah implementasi dari solusi yang diterima yang memperbaikinya. Pemeriksaan endsWithoutNewLine sia-sia untuk semuanya kecuali membaca akhir, tetapi harus sepele waktu bijaksana dibandingkan dengan fungsi keseluruhan.
public int count(String filename) throws IOException {
InputStream is = new BufferedInputStream(new FileInputStream(filename));
try {
byte[] c = new byte[1024];
int count = 0;
int readChars = 0;
boolean endsWithoutNewLine = false;
while ((readChars = is.read(c)) != -1) {
for (int i = 0; i < readChars; ++i) {
if (c[i] == '\n')
++count;
}
endsWithoutNewLine = (c[readChars - 1] != '\n');
}
if(endsWithoutNewLine) {
++count;
}
return count;
} finally {
is.close();
}
}
Dengan java-8, Anda dapat menggunakan stream:
try (Stream<String> lines = Files.lines(path, Charset.defaultCharset())) {
long numOfLines = lines.count();
...
}
Jawaban dengan hitungan metode () di atas memberi saya kesalahan perhitungan baris jika file tidak memiliki baris baru di akhir file - gagal menghitung baris terakhir dalam file.
Metode ini bekerja lebih baik untuk saya:
public int countLines(String filename) throws IOException {
LineNumberReader reader = new LineNumberReader(new FileReader(filename));
int cnt = 0;
String lineRead = "";
while ((lineRead = reader.readLine()) != null) {}
cnt = reader.getLineNumber();
reader.close();
return cnt;
}
cnt
.
Saya tahu ini adalah pertanyaan lama, tetapi solusi yang diterima tidak cocok dengan yang saya butuhkan. Jadi, saya memperbaikinya untuk menerima berbagai terminator garis (bukan hanya umpan baris) dan menggunakan pengkodean karakter yang ditentukan (bukan ISO-8859- n ). Semua dalam satu metode (refactor yang sesuai):
public static long getLinesCount(String fileName, String encodingName) throws IOException {
long linesCount = 0;
File file = new File(fileName);
FileInputStream fileIn = new FileInputStream(file);
try {
Charset encoding = Charset.forName(encodingName);
Reader fileReader = new InputStreamReader(fileIn, encoding);
int bufferSize = 4096;
Reader reader = new BufferedReader(fileReader, bufferSize);
char[] buffer = new char[bufferSize];
int prevChar = -1;
int readCount = reader.read(buffer);
while (readCount != -1) {
for (int i = 0; i < readCount; i++) {
int nextChar = buffer[i];
switch (nextChar) {
case '\r': {
// The current line is terminated by a carriage return or by a carriage return immediately followed by a line feed.
linesCount++;
break;
}
case '\n': {
if (prevChar == '\r') {
// The current line is terminated by a carriage return immediately followed by a line feed.
// The line has already been counted.
} else {
// The current line is terminated by a line feed.
linesCount++;
}
break;
}
}
prevChar = nextChar;
}
readCount = reader.read(buffer);
}
if (prevCh != -1) {
switch (prevCh) {
case '\r':
case '\n': {
// The last line is terminated by a line terminator.
// The last line has already been counted.
break;
}
default: {
// The last line is terminated by end-of-file.
linesCount++;
}
}
}
} finally {
fileIn.close();
}
return linesCount;
}
Solusi ini sebanding dalam kecepatannya dengan solusi yang diterima, sekitar 4% lebih lambat dalam pengujian saya (meskipun tes waktu di Jawa sangat tidak dapat diandalkan).
Saya menguji metode di atas untuk menghitung garis dan inilah pengamatan saya untuk metode yang berbeda seperti yang diuji pada sistem saya
Ukuran File: 1,6 Gb Metode:
Selain itu Pendekatan Java8 tampaknya cukup berguna:
Files.lines(Paths.get(filePath), Charset.defaultCharset()).count()
[Return type : long]
/**
* Count file rows.
*
* @param file file
* @return file row count
* @throws IOException
*/
public static long getLineCount(File file) throws IOException {
try (Stream<String> lines = Files.lines(file.toPath())) {
return lines.count();
}
}
Diuji pada JDK8_u31. Tetapi memang kinerjanya lambat dibandingkan dengan metode ini:
/**
* Count file rows.
*
* @param file file
* @return file row count
* @throws IOException
*/
public static long getLineCount(File file) throws IOException {
try (BufferedInputStream is = new BufferedInputStream(new FileInputStream(file), 1024)) {
byte[] c = new byte[1024];
boolean empty = true,
lastEmpty = false;
long count = 0;
int read;
while ((read = is.read(c)) != -1) {
for (int i = 0; i < read; i++) {
if (c[i] == '\n') {
count++;
lastEmpty = true;
} else if (lastEmpty) {
lastEmpty = false;
}
}
empty = false;
}
if (!empty) {
if (count == 0) {
count = 1;
} else if (!lastEmpty) {
count++;
}
}
return count;
}
}
Diuji dan sangat cepat.
Stream<String> - Time consumed: 122796351 Stream<String> - Num lines: 109808 Method - Time consumed: 12838000 Method - Num lines: 1
Dan jumlah kalimatnya bahkan salah
BufferedInputStream
kapan Anda akan membaca ke buffer Anda sendiri. Selain itu, bahkan jika metode Anda mungkin memiliki sedikit keunggulan kinerja, metode ini kehilangan fleksibilitas, karena tidak lagi mendukung satu-satunya \r
terminator (MacOS lama) dan tidak mendukung setiap penyandian.
Cara mudah menggunakan Scanner
static void lineCounter (String path) throws IOException {
int lineCount = 0, commentsCount = 0;
Scanner input = new Scanner(new File(path));
while (input.hasNextLine()) {
String data = input.nextLine();
if (data.startsWith("//")) commentsCount++;
lineCount++;
}
System.out.println("Line Count: " + lineCount + "\t Comments Count: " + commentsCount);
}
Saya menyimpulkan bahwa wc -l
: metode penghitungan baris baru baik-baik saja tetapi mengembalikan hasil non-intuitif pada file di mana baris terakhir tidak berakhir dengan baris baru.
Dan solusi @ er.vikas berdasarkan LineNumberReader tetapi menambahkan satu ke jumlah baris menghasilkan hasil yang tidak intuitif pada file di mana baris terakhir diakhiri dengan baris baru.
Karena itu saya membuat algo yang menangani sebagai berikut:
@Test
public void empty() throws IOException {
assertEquals(0, count(""));
}
@Test
public void singleNewline() throws IOException {
assertEquals(1, count("\n"));
}
@Test
public void dataWithoutNewline() throws IOException {
assertEquals(1, count("one"));
}
@Test
public void oneCompleteLine() throws IOException {
assertEquals(1, count("one\n"));
}
@Test
public void twoCompleteLines() throws IOException {
assertEquals(2, count("one\ntwo\n"));
}
@Test
public void twoLinesWithoutNewlineAtEnd() throws IOException {
assertEquals(2, count("one\ntwo"));
}
@Test
public void aFewLines() throws IOException {
assertEquals(5, count("one\ntwo\nthree\nfour\nfive\n"));
}
Dan terlihat seperti ini:
static long countLines(InputStream is) throws IOException {
try(LineNumberReader lnr = new LineNumberReader(new InputStreamReader(is))) {
char[] buf = new char[8192];
int n, previousN = -1;
//Read will return at least one byte, no need to buffer more
while((n = lnr.read(buf)) != -1) {
previousN = n;
}
int ln = lnr.getLineNumber();
if (previousN == -1) {
//No data read at all, i.e file was empty
return 0;
} else {
char lastChar = buf[previousN - 1];
if (lastChar == '\n' || lastChar == '\r') {
//Ending with newline, deduct one
return ln;
}
}
//normal case, return line number + 1
return ln + 1;
}
}
Jika Anda menginginkan hasil yang intuitif, Anda dapat menggunakan ini. Jika Anda hanya ingin wc -l
kompatibilitas, gunakan solusi sederhana @ er.vikas, tapi jangan tambahkan satu ke hasilnya dan coba lagi lewati:
try(LineNumberReader lnr = new LineNumberReader(new FileReader(new File("File1")))) {
while(lnr.skip(Long.MAX_VALUE) > 0){};
return lnr.getLineNumber();
}
Bagaimana kalau menggunakan kelas Proses dari dalam kode Java? Dan kemudian membaca output dari perintah.
Process p = Runtime.getRuntime().exec("wc -l " + yourfilename);
p.waitFor();
BufferedReader b = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = "";
int lineCount = 0;
while ((line = b.readLine()) != null) {
System.out.println(line);
lineCount = Integer.parseInt(line);
}
Namun perlu dicoba. Akan memposting hasilnya.
Jika Anda tidak memiliki struktur indeks apa pun, Anda tidak akan bisa menyelesaikan pembacaan file lengkap. Tetapi Anda dapat mengoptimalkannya dengan menghindari membacanya baris demi baris dan menggunakan regex untuk mencocokkan semua terminator baris.
Solusi lucu ini bekerja sangat baik sebenarnya!
public static int countLines(File input) throws IOException {
try (InputStream is = new FileInputStream(input)) {
int count = 1;
for (int aChar = 0; aChar != -1;aChar = is.read())
count += aChar == '\n' ? 1 : 0;
return count;
}
}
Pada sistem berbasis Unix, gunakan wc
perintah pada baris perintah.
Satu-satunya cara untuk mengetahui berapa banyak baris yang ada dalam file adalah dengan menghitungnya. Anda tentu saja dapat membuat metrik dari data Anda, memberi Anda rata-rata panjang satu baris dan kemudian mendapatkan ukuran file dan membaginya dengan rata-rata. panjang tapi itu tidak akan akurat.
Kode Dioptimalkan Terbaik untuk file multi-baris yang tidak memiliki karakter baris baru ('\ n') di EOF.
/**
*
* @param filename
* @return
* @throws IOException
*/
public static int countLines(String filename) throws IOException {
int count = 0;
boolean empty = true;
FileInputStream fis = null;
InputStream is = null;
try {
fis = new FileInputStream(filename);
is = new BufferedInputStream(fis);
byte[] c = new byte[1024];
int readChars = 0;
boolean isLine = false;
while ((readChars = is.read(c)) != -1) {
empty = false;
for (int i = 0; i < readChars; ++i) {
if ( c[i] == '\n' ) {
isLine = false;
++count;
}else if(!isLine && c[i] != '\n' && c[i] != '\r'){ //Case to handle line count where no New Line character present at EOF
isLine = true;
}
}
}
if(isLine){
++count;
}
}catch(IOException e){
e.printStackTrace();
}finally {
if(is != null){
is.close();
}
if(fis != null){
fis.close();
}
}
LOG.info("count: "+count);
return (count == 0 && !empty) ? 1 : count;
}
Pemindai dengan regex:
public int getLineCount() {
Scanner fileScanner = null;
int lineCount = 0;
Pattern lineEndPattern = Pattern.compile("(?m)$");
try {
fileScanner = new Scanner(new File(filename)).useDelimiter(lineEndPattern);
while (fileScanner.hasNext()) {
fileScanner.next();
++lineCount;
}
}catch(FileNotFoundException e) {
e.printStackTrace();
return lineCount;
}
fileScanner.close();
return lineCount;
}
Belum melihatnya.
jika Anda menggunakan ini
public int countLines(String filename) throws IOException {
LineNumberReader reader = new LineNumberReader(new FileReader(filename));
int cnt = 0;
String lineRead = "";
while ((lineRead = reader.readLine()) != null) {}
cnt = reader.getLineNumber();
reader.close();
return cnt;
}
Anda tidak dapat lari ke baris angka besar, menyukai baris 100K, karena kembali dari reader.getLineNumber adalah int. Anda memerlukan tipe data yang panjang untuk memproses baris maksimum ..
int
dapat menyimpan nilai hingga, sekitar 2 miliar. Jika Anda memuat file dengan lebih dari 2 miliar baris, Anda memiliki masalah overflow. Yang mengatakan, jika Anda memuat file teks yang tidak diindeks dengan lebih dari dua miliar baris, Anda mungkin memiliki masalah lain.