Respostas:
Esta é a versão mais rápida que encontrei até agora, cerca de 6 vezes mais rápida que a readLines. Em um arquivo de log de 150 MB, leva 0,35 segundos, contra 2,40 segundos ao usar readLines (). Apenas por diversão, o comando linux 'wc -l leva 0,15 segundos.
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 anos e meio depois: praticamente não tenho experiência em java, mas, de qualquer forma, tentei comparar esse código com a LineNumberReader
solução abaixo, pois me incomodava o fato de ninguém fazer isso. Parece que, especialmente para arquivos grandes, minha solução é mais rápida. Embora pareça levar algumas execuções até que o otimizador faça um trabalho decente. Eu brinquei um pouco com o código e produzi uma nova versão que é consistentemente mais rápida:
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();
}
}
O resultado de referência resulta em um arquivo de texto de 1,3 GB, eixo y em segundos. Eu executei 100 execuções com o mesmo arquivo e medi cada execução com System.nanoTime()
. Você pode ver que countLinesOld
possui alguns valores discrepantes e countLinesNew
nenhum, e embora seja um pouco mais rápido, a diferença é estatisticamente significativa. LineNumberReader
é claramente mais lento.
Eu implementei outra solução para o problema, achei mais eficiente na contagem de linhas:
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
O lineNumber
campo de é um número inteiro ... Não será apenas agrupado para arquivos maiores que Integer.MAX_VALUE? Por que se preocupar em pular um longo tempo aqui?
wc -l
conta o número de caracteres de nova linha no arquivo. Isso funciona, pois todas as linhas são finalizadas com uma nova linha, incluindo a linha final em um arquivo. Toda linha tem um caractere de nova linha, incluindo as linhas vazias, portanto, o número de linhas nova = = número de linhas em um arquivo. Agora, a lineNumber
variável in FileNumberReader
também representa o número de caracteres de nova linha vistos. Começa em zero, antes que qualquer nova linha seja encontrada e aumenta a cada caractere de nova linha visto. Portanto, não adicione um ao número da linha, por favor.
wc -l
também informa esse tipo de arquivo. Veja também stackoverflow.com/questions/729692/…
wc -l
retornaria 1. Concluí que todos os métodos têm falhas e implementei um com base em como gostaria que ele se comportasse. Veja minha outra resposta aqui.
A resposta aceita tem um erro de um por um para arquivos com várias linhas que não terminam em nova linha. Um arquivo de uma linha que termina sem uma nova linha retornará 1, mas um arquivo de duas linhas que termina sem uma nova linha retornará 1 também. Aqui está uma implementação da solução aceita que corrige isso. As verificações endsWithoutNewLine são um desperdício para tudo, exceto a leitura final, mas devem ser triviais em termos de tempo em comparação com a função geral.
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();
}
}
Com java-8, você pode usar fluxos:
try (Stream<String> lines = Files.lines(path, Charset.defaultCharset())) {
long numOfLines = lines.count();
...
}
A resposta com o método count () acima me deu erros de linha se um arquivo não tivesse uma nova linha no final do arquivo - ele falhou ao contar a última linha do arquivo.
Este método funciona melhor para mim:
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
.
Sei que essa é uma pergunta antiga, mas a solução aceita não corresponde exatamente ao que eu precisava. Portanto, refinei-o para aceitar vários terminadores de linha (em vez de apenas feed de linha) e usar uma codificação de caracteres especificada (em vez da ISO-8859- n ). Tudo em um método (refatorar conforme apropriado):
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;
}
Essa solução é comparável em velocidade à solução aceita, cerca de 4% mais lenta em meus testes (embora os testes de temporização em Java sejam notoriamente não confiáveis).
Testei os métodos acima para contar linhas e aqui estão minhas observações para diferentes métodos testados no meu sistema
Tamanho do arquivo: 1.6 Gb Métodos:
Além disso, a abordagem Java8 parece bastante útil:
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();
}
}
Testado em JDK8_u31. Mas, na verdade, o desempenho é lento em comparação com este método:
/**
* 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;
}
}
Testado e muito rápido.
Stream<String> - Time consumed: 122796351 Stream<String> - Num lines: 109808 Method - Time consumed: 12838000 Method - Num lines: 1
E o número de linhas também está errado também
BufferedInputStream
quando quiser ler o seu próprio buffer. Além disso, mesmo que seu método possa ter uma pequena vantagem de desempenho, ele perde flexibilidade, pois não suporta mais \r
terminadores de linha única (MacOS antigo) e não suporta todas as codificações.
Uma maneira direta de usar o 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);
}
Concluí que wc -l
: o método de contar novas linhas é bom, mas retorna resultados não intuitivos em arquivos nos quais a última linha não termina com uma nova linha.
E a solução @ er.vikas baseada em LineNumberReader, mas a adição de uma na contagem de linhas retornou resultados não intuitivos em arquivos nos quais a última linha termina com nova linha.
Portanto, fiz um algo que lida com o seguinte:
@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"));
}
E fica assim:
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;
}
}
Se você deseja resultados intuitivos, pode usar isso. Se você deseja apenas wc -l
compatibilidade, use a solução @ er.vikas, mas não adicione uma ao resultado e tente pular novamente:
try(LineNumberReader lnr = new LineNumberReader(new FileReader(new File("File1")))) {
while(lnr.skip(Long.MAX_VALUE) > 0){};
return lnr.getLineNumber();
}
Que tal usar a classe Process a partir do código Java? E então lendo a saída do comando.
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);
}
Precisa tentar embora. Irá publicar os resultados.
Se você não possui nenhuma estrutura de índice, não contorna a leitura do arquivo completo. Mas você pode otimizá-lo, evitando lê-lo linha por linha e usar um regex para corresponder a todos os terminadores de linha.
Esta solução divertida funciona realmente muito bem!
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;
}
}
Em sistemas baseados em Unix, use o wc
comando na linha de comandos.
A única maneira de saber quantas linhas existem no arquivo é contá-las. Obviamente, você pode criar uma métrica a partir dos seus dados, fornecendo um comprimento médio de uma linha e, em seguida, obter o tamanho do arquivo e dividi-lo com o valor médio. comprimento, mas isso não será preciso.
Melhor código otimizado para arquivos de várias linhas sem caracteres de nova linha ('\ n') no 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;
}
Scanner com 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;
}
Ainda não cronometrei.
se você usar isso
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;
}
você não pode executar linhas grandes, gosta de 100 mil linhas, porque o retorno de reader.getLineNumber é int. você precisa de um tipo longo de dados para processar o máximo de linhas.
int
pode armazenar valores de até aproximadamente 2 bilhões. Se você estiver carregando um arquivo com mais de 2 bilhões de linhas, há um problema de estouro. Dito isto, se você estiver carregando um arquivo de texto não indexado com mais de dois bilhões de linhas, provavelmente terá outros problemas.