Programm stürzt bei größeren Listen ab

Conax

Mitglied
Ich wollte ja ein Programm schreiben das eine Textdatei (Wörterbuch) einliest und diese dann mit Zusätzen (in dem Fall Zahlen) wieder ausgibt siehe hier:
http://www.java-forum.org/java-basics-anfaenger-themen/124761-textdatei-einlesen.html

Ich habe das Programm jetzt etwas überarbeitet um an meine Vorstellungen anzupassen. Siehe hier:

Java:
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
 
public class pwgenerator {
 
    // Datei, die ausgelesen wird
    private static File file;
    
    // Diese Reader sind am Auslesen der Datei beteiligt
    private static FileReader fileReader;
    private static BufferedReader bufferedFileReader;
    
    // Gesamter Inhalt der Datei
    private static String fileContent = "";
    
    // Ein Array mit den Zeilen der Datei
    private static String[] lines;
    
    public static void main(String[] args) throws IOException {
        init();
        getFileContent();
        getLines();
        printWords();
    }
 
    // Initialisierung der am Auslesen beteiligten Reader
    private static void init() 
    {
        String fileName = getFileName();
        file = new File(fileName);
        try 
    {
            fileReader = new FileReader(file);
    } 
        catch (FileNotFoundException e) 
    {
            System.err.println("Die Datei wurde unter dem angegebenen " +
                               "Pfad nicht gefunden.");
    }
        bufferedFileReader = new BufferedReader(fileReader);
    }
    
    // Auslesen des Dateinamens von der Standardeingabe (Konsole)
    private static String getFileName() {
        InputStream standardInputStream = System.in;
        InputStreamReader standardInputStreamReader = 
            new InputStreamReader(standardInputStream);
        BufferedReader bufferedStandardInputStreamReader =
            new BufferedReader(standardInputStreamReader);
        
        String fileName = null;
        System.out.print("Dateiname: ");
        try 
        {
            fileName = bufferedStandardInputStreamReader.readLine();
        } 
         catch (IOException e) 
        {
            System.err.println("Fehler bei der Eingabe des Dateinamens.");
        }
        
         return fileName;
        }
    
    // Auslesen des Dateiinhalts
    private static void getFileContent() 
    {
        String currentLine;
        try 
    {
            // Der Dateiinhalt wird zeilenweise ausgelesen
            // Jede Zeile wird an den String mit dem Dateiinhalt angehängt
            // Das "\n" signalisiert einen Zeilenumbruch
            while((currentLine = bufferedFileReader.readLine()) != null)
                fileContent = fileContent + currentLine + "\n";
     } 
        catch (IOException e) 
     {
            System.err.println("Fehler beim Lesen der Datei.");
     }
    }
    
    // Auslesen der einzelnen Zeilen der Datei
    private static void getLines() {
        // Wir splitten den String überall wo sich ein Zeilenumbruch befindet
        lines = fileContent.split("\n");
    }
    
    // Auslesen der Wörter in den einzelnen Zeilen der Datei
    // und Ausgabe des gewünschten Textmusters auf der Standardausgabe
    private static void printWords() throws IOException 
    {
        String currentLine;
     //   String[] currentWords;
        int numberOfLines = lines.length;
        /*
         int numberOfWords;
         String currentWord;
        
         Zeilenweises Durchlaufen der Datei und Extrahieren der Wörter aus
         einer Zeile (jede Zeile wird dort gesplittet, wo sich ein
         Leerzeichen befindet)
        */
        BufferedReader stdin = new BufferedReader(
	   			               new InputStreamReader( System.in )); 
        System.out.print("Zahlenraum eingeben: ");
        int l;
        String eingabe = stdin.readLine();
        l = Integer.parseInt(eingabe);
        
       
        for(int i = 0; i < numberOfLines; i++) 
        {
            currentLine = lines[i];
            /*
            currentWords = currentLine.split(" ");
            numberOfWords = currentWords.length;
            
            Jedes Wort wird nun in der gewünschten Form ausgegeben.
            for(int j = 0; j < numberOfWords; j++)
            for(int j = 0; j < currentLine; j++)
            */
            {
         //       currentWord = currentWords[j];
                for(int k = 1; k <= l; k++)
         //           System.out.println(currentWord + k);
                System.out.println(currentLine + k);
                //System.out.print(currentWord + 100 + ".\n\n");
            }
        }   
       }
    }

Das Problem ist bei größeren Wörterbüchern hängt er sich auf. Also "top" zeigt mir eine CPU Auslastung von 100% an aber es tut sich nichts.

Code:
stefan@linux-ufn8:~/src/java/pwgenerator/bin> java pwgenerator
Dateiname: masterwordlist.txt
^Cstefan@linux-ufn8:~/src/java/pwgenerator/bin>

Bei kleineren Wortlisten mit nur ein paar Einträgen klappt es ohne Probleme nur die großen mag er irgendwie nicht.
 
Bei kleineren Wortlisten mit nur ein paar Einträgen klappt es ohne Probleme nur die großen mag er irgendwie nicht.
Was heißt "groß"? Solange die Größe der Datei nicht die des freien Arbeitsspeichers übersteigt sollte es funktioneren - vorausgesetzt, du teilst der JVM genug Speicher zu!

Siehe dazu: Java Memory -Xmx256m -Xmx512m How-To Video

Allerdings solltest du einen JVM-Absturz mit entsprechender Meldung bekommen wenn es daran liegt. Bau ein paar System.out.println()-Befehle in deinen Code, um zu sehen wo er hängt, wahrscheinlich irgendeine Endlosschleife auf Grund falscher Zählerwerte oder sowas.

Benutzt du eigentlich keine IDE? Eclipse z.B., die haben einen Debugger mit dem man solche Freezes sehr gut nachverfolgen kann.

PS: Du darfst dich nicht wundern wenn hier keiner antwortet. Hunderte Zeilen Quellcode will sich keiner ansehen, und das wirkt so als hättest du dich gar nicht mit dem Problem beschäftigt (eingrenzen). Außerdem startet hier keiner einfach irgendwelche Programme um das zu testen. Erst recht nicht wenn sie auf dem Dateisystem arbeiten und dabei auch noch "irgendwie hängen" 😉
 
Zuletzt bearbeitet:
Also die Liste ist 6,5 MB groß am Speicher dürfte es da wohl eher nicht liegen. Ich habe das auch in eclipse durchlaufen lassen aber es zeigt mir dort auch keinen Fehler etc. an.
 
Kannst Du vielleicht das Wörterbuch mal hochladen?

BITTE NIE EIN KLEINES L ALS VARIABLENNAMEN VERWENDEN! DAS SIEHT AUS WIE EINE 1!
 
Zuletzt bearbeitet:
Wie gesagt, debuggen. Das ist in Eclipse oben neben dem Start-(Play)Button der button mit dem Käfer (Bug). Setze einen Debug-Point an den Start deiner Applikation (Doppelklick links auf die Leiste im Textedtior, am besten bei der ersten Zeile der main-Methode) und führ den Debugger aus. Dann kannst du mit Knöpfen Zeile für Zeile durchgehen, und so sehen wo er letztendlich hängt.

Und wenn du dazu zu faul bist, eben per sysouts. kann aber im Endeffekt viel länger dauern.
 
Diese Stelle könnte verantwortlich sein.
Java:
fileContent = fileContent + currentLine + "\n";


setze dort einen StringBuilder ein.
siehe auch:
http://www.java-forum.org/java-basi...extdatei-zeilenweise-auslesen.html#post802013

Was ich mich aber frage: Warum liest du den Dateiinhalt in einen String mit angefügten "\n" pro Zeile ein,
um ihn dann später wieder per split in ein (Zeilen)Array umzuwandeln?

Edit: Das split über den dann doch rel. großen String dürfte ausserdem (oder vor allem) für das Problem veranwortlich sein.

Lies doch die Zeilen schon beim einlesen in eine Datenstruktur ein z.B. in eine Arraylist.
 
Zuletzt bearbeitet:
Hab' doch gewusst, dass es passt... nimmst du so:
Java:
import java.io.FileReader;
import java.io.StreamTokenizer;
import java.util.Map;
import java.util.TreeMap;

public final class CountWords
{
	public static void main(String[] args)
	throws Throwable
	{
		if(args == null || args.length <= 0) {
			args = new String[] {"wordlist.txt"};
		}
		final Map<String, IntHolder> wordStats = new TreeMap<String, IntHolder>()
		{
			private static final long serialVersionUID = 1L;

			@Override
			public String toString()
			{
				StringBuilder sb = new StringBuilder();
				for(Map.Entry<String, IntHolder> e : entrySet()) {
					sb.append(e.getKey());
					sb.append(": ");
					sb.append(e.getValue());
					sb.append("\n");
				}
				return sb.toString();
			}
		};
		long time = System.currentTimeMillis();
		int words = 0;
		StreamTokenizer st = new StreamTokenizer(new FileReader("./" + args[0]));
		st.ordinaryChar('.');
		st.ordinaryChars('0', '9');
		st.ordinaryChar('-');
		st.wordChars('0', '9');
		int token;
		do {
			if((token = st.nextToken()) == StreamTokenizer.TT_WORD) {
				if(!wordStats.containsKey(st.sval)) {
					wordStats.put(st.sval, new IntHolder());
				}
				wordStats.get(st.sval).value++;
				words++;
			}
		} while(token != StreamTokenizer.TT_EOF);
//		System.out.println(wordStats);
		System.out.println("Anzahl Wörter : " + words + "; davon verschieden: " + wordStats.size());
		System.out.println("Benötigte Zeit: " + (System.currentTimeMillis() - time) + "ms");
	}

	private static final class IntHolder
	{
		private int value;

		private IntHolder()
		{
			// nothing
		}

		@Override
		public String toString()
		{
			return String.valueOf(value);
		}
	}
}
Ausgabe:
Code:
Anzahl Wörter : 570472; davon verschieden: 569278
Benötigte Zeit: 1399ms
Okay, zugegeben, die Auswahl der zu ladenden Datei ist etwas anders. Ein von "wordlist.txt" abweichender Dateiname muss per Argument beim Programmstart angegeben werden, aber was soll's, die Anpassung bekommst sicher noch alleine hin.
Im übrigen... welchen Zeichensatz hat die Datei? Bei Umlauten usw. bekomme ich nur Hieroglyphen.
 
Das Wörterbuch ist nicht schlecht oder? Zeichensatz ist ganz normal UTF-8

@Spacerat
ähm der gepostete Code hat doch nicht mehr viel mit dem original zu tun. Ich versteh nicht so ganz inwiefern mir das jetzt helfen soll?
 
@Conax: Wieso sollte der Code noch irgend etwas mit dem Original zu tun haben? Das Original funktioniert doch nicht. Gibt's etwa spezielle Vorgaben, von denen hier nirgends etwas steht?
 
Nein gibt keine speziellen Vorgaben. Das sollte auch keine Kritik oder dergleichen an dich sein - sorry wenn es so rübergekommen sein sollte.

Das ursprüngliche Programm sollte aber das Wort aus der Wortliste herausziehen und dann eine Ziffer hinten an das Wort anfügen (dafür hatte ich ja den int l generiert damit wenn ich z.B. 2000 eingebe er hinter jedes Wort eine Zahl hinzufügt also z.B. beim Wort Apfel dann Apfel1 bis Apfel2000 ausgibt das ganze sollte dann eben so weiter gehen bis er dann beim letzten wort z.B. zzzz ist und zzzz2000 ausgibt und mit dem letzten Wort sollte sich das Programm auch beenden.
 
Also die gewünschte Ausgabe ist auch nicht weiter schwer:[JAVA=48] for(String s : wordStats.keySet()) {
for(int count = 1; count <= 2000; count++) {
System.out.println(s + count);
}
}[/code]...muss ab Zeile 48 nur in meinen obigen Code eingefügt werden.
 
Vielen Dank Spacerat ja genau so wollte ich das. Nur die 2000 als Fixwert habe ich durch einen Wert ersetzt den ich dann x beliebig setzen kann.

Java:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
import java.util.Map;
import java.util.TreeMap;
 
public final class pwgen
{
    public static void main(String[] args)
    throws Throwable
    {
        if(args == null || args.length <= 0) {
            args = new String[] {"wordlist.txt"};
        }
        final Map<String, IntHolder> wordStats = new TreeMap<String, IntHolder>()
        {
            private static final long serialVersionUID = 1L;
 
            @Override
            public String toString()
            {
                StringBuilder sb = new StringBuilder();
                for(Map.Entry<String, IntHolder> e : entrySet()) {
                    sb.append(e.getKey());
                    sb.append(": ");
                    sb.append(e.getValue());
                    sb.append("\n");
                }
                return sb.toString();
            }
        };
        long time = System.currentTimeMillis();
        int words = 0;
        StreamTokenizer st = new StreamTokenizer(new FileReader("./" + args[0]));
        st.ordinaryChar('.');
        st.ordinaryChars('0', '9');
        st.ordinaryChar('-');
        st.wordChars('0', '9');
        int token;
        do {
            if((token = st.nextToken()) == StreamTokenizer.TT_WORD) {
                if(!wordStats.containsKey(st.sval)) {
                    wordStats.put(st.sval, new IntHolder());
                }
                wordStats.get(st.sval).value++;
                words++;
            }
        } while(token != StreamTokenizer.TT_EOF);
//      System.out.println(wordStats);
        BufferedReader stdin = new BufferedReader(
	                           new InputStreamReader( System.in )); 
        System.out.print("Zahlenraum eingeben: ");
        int m;
        String eingabe = stdin.readLine();
        m = Integer.parseInt(eingabe);
        
        for(String s : wordStats.keySet()) {
            for(int count = 1; count <= m; count++) {
                System.out.println(s + count);
            }
        }
        System.out.println("Anzahl Wörter : " + words + "; davon verschieden: " + wordStats.size());
        System.out.println("Benötigte Zeit: " + (System.currentTimeMillis() - time) + "ms");
    }
 
    private static final class IntHolder
    {
        private int value;
 
        private IntHolder()
        {
            // nothing
        }
 
        @Override
        public String toString()
        {
            return String.valueOf(value);
        }
    }
}

Den Fehler mit den int l habe ich jetzt auch nicht mehr gemacht. Ich habe das mal getestet und diesmal läuft es problemlos durch also auch mit der großen Wortliste. Ich habe vor das Programm noch zu erweitern das z.B. die Zahlen vor die Wörter gestellt werden oder Buchstaben wie a durch @ ausgetauscht werden oder aber das die Wörter eingelesen werden und z.B. sämtliche Wörter mit Umlauten wie ä durch ae ersetzt werden. Der schwierige Teil ist aber das mit dem einlesen der Textdatei was ja jetzt zum glück gelöst ist.

Naja das gute finde ich an dem Programm man sieht live wie schnell der PC arbeitet wenn er die ganzen Wörter ausspuckt.
 

Zurück
Oben