Yanıtlar:
Bu şimdiye kadar bulduğum en hızlı sürüm, readLines'den yaklaşık 6 kat daha hızlı. 150MB günlük dosyasında, readLines () kullanılırken 0,35 saniye, 2,40 saniye sürer. Sadece eğlence için, linux 'wc -l komutu 0.15 saniye sürer.
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 yıl sonra: Ben hemen hemen hiç java deneyimim yok, ama LineNumberReader
yine de kimsenin bunu yapma beni rahatsız beri aşağıdaki kodu bu çözümle karşılaştırmak için çalıştım . Özellikle büyük dosyalar için benim çözümüm daha hızlı görünüyor. Her ne kadar optimizer iyi bir iş yapana kadar birkaç çalışma yapmak gibi görünüyor. Kod ile biraz oynadım ve sürekli olarak en hızlı olan yeni bir sürüm ürettim:
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();
}
}
Karşılaştırma, saniye cinsinden y ekseni olan 1,3 GB metin dosyası için yeniden sonuçlanır. Aynı dosya ile 100 çalışır gerçekleştirdim ve her çalıştırmayı ölçtüm System.nanoTime()
. Bunun countLinesOld
birkaç aykırı değeri countLinesNew
vardır ve hiçbiri yoktur ve sadece biraz daha hızlı olsa da, fark istatistiksel olarak önemlidir. LineNumberReader
açıkça daha yavaştır.
Soruna başka bir çözüm uyguladım, satırları saymada daha verimli buldum:
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
adlı kullanıcının lineNumber
alanı bir tamsayıdır ... Yalnızca Integer.MAX_VALUE değerinden daha uzun dosyalar için kaydırılmaz mı? Neden burada uzun bir süre zahmete giriyorsun?
wc -l
dosyadaki yeni satır karakterlerini sayar. Bu, her satır bir dosyadaki son satır da dahil olmak üzere bir satırsonu ile sonlandırıldığından işe yarar. Her satırda boş satırlar da dahil olmak üzere yeni satır karakteri vardır, bu nedenle satır başı karakter sayısı == dosyadaki satır sayısıdır. Şimdi, lineNumber
değişken FileNumberReader
aynı zamanda görülen yeni satır karakterlerini de temsil etmektedir. Herhangi bir yeni satır bulunmadan önce sıfırdan başlar ve görülen her yeni satır karakteri ile artar. Bu yüzden lütfen satır numarasına bir tane eklemeyin.
wc -l
bu tür dosyaları da bu şekilde bildirir. Ayrıca bkz. Stackoverflow.com/questions/729692/…
wc -l
1 döndürür. Tüm yöntemlerin kusurları olduğu sonucuna vardım ve nasıl davranmasını istediğime dayanarak bir tane uyguladım, burada diğer cevabımı görün.
Kabul edilen yanıt, satırsonu ile bitmeyen çok satırlı dosyalar için bire bir kapalı hataya sahiptir. Yeni satır olmadan biten bir satır dosyası 1 döndürür, ancak yeni satır olmadan biten iki satır dosyası da 1 döndürür. İşte bunu düzelten kabul edilen çözümün bir uygulaması. EndWithoutNewLine kontrolleri son okuma dışında her şey için israftır, ancak genel fonksiyona kıyasla akıllıca bir zaman olmalıdır.
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();
}
}
İle Java-8, akışları kullanabilirsiniz:
try (Stream<String> lines = Files.lines(path, Charset.defaultCharset())) {
long numOfLines = lines.count();
...
}
Yukarıdaki yöntem count () yönteminin yanıtı, dosyanın sonunda bir satırsonu yoksa bana satır yanlış sayımları verdi - dosyadaki son satırı sayamadı.
Bu yöntem benim için daha iyi çalışıyor:
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
.
Bunun eski bir soru olduğunu biliyorum, ancak kabul edilen çözüm, yapmam gerekenle tam olarak eşleşmedi. Bu nedenle, çeşitli satır sonlandırıcılarını (yalnızca satır besleme yerine) kabul etmeyi ve belirli bir karakter kodlaması (ISO-8859- n yerine) kullanmayı iyileştirdim . Hepsi tek bir yöntemde (uygun şekilde refactor):
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;
}
Bu çözüm, kabul edilen çözümle karşılaştırılabilir hızdadır, testlerimde yaklaşık% 4 daha yavaştır (ancak Java'daki zamanlama testleri çok güvenilir değildir).
Çizgileri saymak için yukarıdaki yöntemleri test ettim ve burada sistemimde test edilen Farklı yöntemler için gözlemlerim var
Dosya Boyutu: 1.6 Gb Yöntemleri:
Üstelik Java8 Yaklaşımı oldukça kullanışlı görünüyor:
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 üzerinde test edildi. Ama aslında bu yönteme kıyasla performans yavaş:
/**
* 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;
}
}
Test edildi ve çok hızlı.
Stream<String> - Time consumed: 122796351 Stream<String> - Num lines: 109808 Method - Time consumed: 12838000 Method - Num lines: 1
Ve satır sayısı da yanlış
BufferedInputStream
yine de kendi tamponunuza okuyacağınız zamanı kullanmamalısınız. Ayrıca, yönteminiz hafif bir performans avantajına sahip olsa bile, artık tek \r
satır sonlandırıcıları (eski MacOS) desteklemediğinden ve her kodlamayı desteklemediğinden esnekliği kaybeder .
Tarayıcıyı kullanarak basit bir yol
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);
}
Ben şu sonuca vardım wc -l
: s satırları sayma yöntemi gayet iyi ama son satırın satırsonu ile bitmeyen dosyalar üzerinde sezgisel olmayan sonuçlar döndürür.
Ve @ er.vikas çözümü LineNumberReader tabanlı ancak satır sayısına bir tane eklemek son satırın satırsonu ile bittiği dosyalarda sezgisel olmayan sonuçlar verdi.
Bu nedenle aşağıdaki gibi işleyen bir algo yaptım:
@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"));
}
Ve şöyle görünüyor:
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;
}
}
Sezgisel sonuçlar istiyorsanız, bunu kullanabilirsiniz. Sadece wc -l
uyumluluk istiyorsanız , @ er.vikas çözümünü kullanın, ancak sonuca bir tane eklemeyin ve atlamayı tekrar deneyin:
try(LineNumberReader lnr = new LineNumberReader(new FileReader(new File("File1")))) {
while(lnr.skip(Long.MAX_VALUE) > 0){};
return lnr.getLineNumber();
}
Process sınıfını Java kodu içinden kullanmaya ne dersiniz? Ve sonra komutun çıktısını okumak.
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);
}
Yine de denemek gerekiyor. Sonuçları gönderir.
Herhangi bir dizin yapınız yoksa, dosyanın tamamını okuyamazsınız. Ancak satır satır okumaktan kaçınarak optimize edebilir ve tüm satır sonlandırıcılarla eşleştirmek için bir normal ifade kullanabilirsiniz.
Bu komik çözüm aslında gerçekten iyi çalışıyor!
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 tabanlı sistemlerde, wc
komut satırındaki komutu kullanın.
Dosyada kaç satır olduğunu bilmenin tek yolu onları saymaktır. Elbette, verilerinizden bir metrenin ortalama uzunluğunu veren bir metrik oluşturabilir ve ardından dosya boyutunu alabilir ve bunu avg ile bölebilirsiniz. ancak bu doğru olmayacaktır.
En İyi EOF'da yeni satır ('\ n') karakteri olmayan çok satırlı dosyalar için optimize edilmiş kod.
/**
*
* @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;
}
Normal ifade içeren tarayıcı:
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;
}
Saat etmedim.
bunu kullanırsan
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;
}
büyük num satırlarına çalışamazsınız, 100K satırları sever, çünkü reader.getLineNumber öğesinden döndürme int'dir. maksimum satırı işlemek için uzun veri türüne ihtiyacınız vardır.
int
Yaklaşık 2 milyar e kadar değerlerini tutabilir. 2 milyardan fazla satır içeren bir dosya yüklüyorsanız taşma sorununuz var. Bununla birlikte, iki milyardan fazla satır içeren dizine eklenmemiş bir metin dosyası yüklüyorsanız, muhtemelen başka sorunlarınız var demektir.