C'est la version la plus rapide que j'ai trouvée jusqu'à présent, environ 6 fois plus rapide que readLines. Sur un fichier journal de 150 Mo, cela prend 0,35 seconde, contre 2,40 secondes en utilisant readLines(). Pour l'anecdote, la commande wc -l de linux prend 0,15 seconde.
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 ans et demi plus tard : Je n'ai pratiquement aucune expérience en Java, mais j'ai tout de même essayé de comparer ce code à la norme LineNumberReader
solution ci-dessous car cela me dérangeait que personne ne l'ait fait. Il semble que ma solution soit plus rapide, surtout pour les gros fichiers. Bien qu'il faille quelques exécutions avant que l'optimiseur ne fasse un travail décent. J'ai joué un peu avec le code, et j'ai produit une nouvelle version qui est toujours la plus rapide :
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) {
for (int i=0; i<readChars; ++i) {
if (c[i] == '\n') {
++count;
}
}
readChars = is.read(c);
}
return count == 0 ? 1 : count;
} finally {
is.close();
}
}
Résultats du benchmark pour un fichier texte de 1.3GB, axe y en secondes. J'ai effectué 100 exécutions avec le même fichier, et j'ai mesuré chaque exécution avec System.nanoTime()
. Vous pouvez constater que countLinesOld
comporte quelques valeurs aberrantes, et countLinesNew
n'en a pas et, bien qu'il ne soit que légèrement plus rapide, la différence est statistiquement significative. LineNumberReader
est nettement plus lent.