Відповіді:
Це найшвидша версія, яку я знайшов поки що, приблизно в 6 разів швидше, ніж readLines. Для файлу журналу розміром 150 Мб це займає 0,35 секунди проти 2,40 секунди при використанні readLines (). Для задоволення команда linux 'wc -l займає 0,15 секунди.
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 років пізніше: Я практично не маю досвіду Java, але все одно я намагався порівняти цей код із LineNumberReader
рішенням нижче, оскільки це мені непокоїло, що ніхто цього не робив. Здається, що особливо для великих файлів моє рішення швидше. Хоча, здається, потрібно кілька пробіжок, поки оптимізатор не зробить гідну роботу. Я трохи пограв з кодом і створив нову версію, яка постійно швидша:
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();
}
}
Результати порівняння для текстового файлу 1,3 Гб, вісь y в секундах. Я виконав 100 запусків з тим самим файлом і виміряв кожен пробіг System.nanoTime()
. Ви можете бачити, що він countLinesOld
має кілька людей, що не мають виходу, а countLinesNew
його немає, і хоча це лише трохи швидше, різниця є статистично значущою. LineNumberReader
явно повільніше.
Я реалізував ще одне рішення проблеми, і вважав його більш ефективним при підрахунку рядків:
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
поле являє собою ціле ... Чи не буде просто обернути файли більше ніж Integer.MAX_VALUE? Навіщо турбуватися пропускати тут довго?
wc -l
підраховує кількість символів нового рядка у файлі. Це працює, оскільки кожен рядок закінчується новим рядком, включаючи остаточний рядок у файлі. Кожен рядок має новий рядок, включаючи порожні рядки, отже, кількість символів нового рядка == кількість рядків у файлі. Тепер lineNumber
змінна в FileNumberReader
також представляє кількість переглянутих символів нового рядка. Він починається з нуля до того, як буде знайдено будь-який новий рядок, і збільшується з кожним побаченим символом нового рядка. Тому не додайте його до номера рядка.
wc -l
також повідомляється про такий файл. Також см stackoverflow.com/questions/729692 / ...
wc -l
повернеться 1. Я зробив висновок, що всі методи мають недоліки, і застосував один на основі того, як я хотів би, щоб він поводився, дивіться іншу відповідь тут.
Прийнята відповідь вимикається однією помилкою для багаторядкових файлів, які не закінчуються в новому рядку. Файл одного рядка, що закінчується без нового рядка, повертає 1, а два файли, що закінчуються без нового рядка, також повертають 1. Ось реалізація прийнятого рішення, яке це виправляє. Перевірки з кінцямиWithoutNewLine марно витрачають на все, окрім остаточного прочитаного, але мають бути тривіальними у часі порівняно із загальною функцією.
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();
}
}
З java-8, ви можете використовувати потоки:
try (Stream<String> lines = Files.lines(path, Charset.defaultCharset())) {
long numOfLines = lines.count();
...
}
Відповідь із методом count (), наведеним вище, дав мені рахунки у рядку, якщо в кінці файлу не було нового рядка - не вдалося підрахувати останній рядок у файлі.
Цей метод для мене працює краще:
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
.
Я знаю, що це старе питання, але прийняте рішення не зовсім відповідало тому, що мені потрібно було зробити. Отже, я вдосконалив її для прийому різних термінаторів ліній (а не просто подачі рядків) та використання заданого кодування символів (а не ISO-8859- n ). Все в одному методі (рефактор відповідно)
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;
}
Це рішення порівняно за швидкістю з прийнятим рішенням, на 4% повільніше в моїх тестах (хоча тести на терміни на Java явно сумнівно ненадійні).
Я перевірив вищевказані методи для підрахунку рядків, і ось мої спостереження за різними методами, як перевірені в моїй системі
Розмір файлу: 1,6 Gb Методи:
Більше того, підхід Java8 виглядає досить зручним:
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();
}
}
Тестовано на JDK8_u31. Але насправді продуктивність повільна порівняно з цим методом:
/**
* 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;
}
}
Випробували і дуже швидко.
Stream<String> - Time consumed: 122796351 Stream<String> - Num lines: 109808 Method - Time consumed: 12838000 Method - Num lines: 1
І кількість рядків теж неправильна
BufferedInputStream
у будь-якому випадку ви не повинні використовувати a, коли ви збираєтесь читати у власному буфері. Крім того, навіть якщо ваш метод може мати невелику перевагу в продуктивності, він втрачає гнучкість, оскільки він більше не підтримує єдині \r
лінійні термінатори (старий MacOS) і не підтримує кожне кодування.
Прямий шлях за допомогою сканера
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);
}
Я зробив висновок, що wc -l
: s підрахунок нових рядків чудово, але повертає неінтуїтивні результати у файлах, де останній рядок не закінчується новим рядком.
І рішення @ er.vikas на основі LineNumberReader, але додавши його до числа рядків, повертає неінтуїтивні результати у файлах, де останній рядок закінчується новим рядком.
Тому я створив альго, яке обробляє так:
@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"));
}
І це виглядає приблизно так:
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;
}
}
Якщо ви хочете отримати інтуїтивні результати, ви можете скористатися цим. Якщо ви просто хочете wc -l
сумісності, просто скористайтеся рішенням @ er.vikas, але не додайте його до результату та повторіть спробу:
try(LineNumberReader lnr = new LineNumberReader(new FileReader(new File("File1")))) {
while(lnr.skip(Long.MAX_VALUE) > 0){};
return lnr.getLineNumber();
}
Як щодо використання класу Process з коду Java? А потім зчитування результатів команди.
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);
}
Потрібно все ж спробувати. Опублікуємо результати.
Якщо у вас немає індексних структур, ви не зможете обійти читання повного файлу. Але ви можете оптимізувати його, уникаючи читати його рядок за рядком і використовувати регулярний вираз, щоб відповідати всім терміналам рядків.
Це смішне рішення насправді добре працює!
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;
}
}
У системах на базі Unix використовуйте wc
команду в командному рядку.
Єдиний спосіб дізнатися, скільки рядків у файлі - це порахувати їх. Звичайно, ви можете створити показник зі своїх даних, даючи середню довжину одного рядка, а потім отримати розмір файлу та розділити його на середню. довжина, але це не буде точним.
Найкращий оптимізований код для багаторядкових файлів, що не мають символу newline ('\ n') в 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;
}
Сканер з регулярним виразом:
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;
}
Його не завели.
якщо ви використовуєте це
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;
}
ви не можете переходити до великих числових рядків, любить 100K рядків, тому що return from reader.getLineNumber - це int. вам потрібен довгий тип даних для обробки максимальних рядків ..
int
може містити значення приблизно до 2 мільярдів. Якщо ви завантажуєте файл з більш ніж 2 мільярдами рядків, у вас є проблема переповнення. Однак, якщо ви завантажуєте не доданий текстовий файл з більш ніж двома мільярдами рядків, у вас, ймовірно, є інші проблеми.