# Mit Scanner-Klasse eine Datei zeilenweise einlesen und nur bestimmte Zeichen abfragen



## Jay1980 (18. Aug 2009)

Servus,

ich habe eine Datei die lese ich via Scanner und Schleife zeilenweise ein - passt.
Erwartet wird eine Zeile mit
123 456

Jetzt haben aber bestimmte Testdateien, Störkommentare drin, etwas #ha 123 456, oft in der ersten Zeile und einmal auch mitten drin, wie mache ich das, dass meine Zeilenscanner immer wenn er mit nextInt() scheitert die Zeile einfach überspringt. Ich las es gibt eine Skip-Methode, die einen String oder ein Pattern aufnimmt. Da ich Novize bin, bin ich für eine Hilfe dankbar. 


```
// Punkte laden
		ArrayList<Point> tempPunkte = new ArrayList<Point>();
		
		File fileToRead = new File( fileName );
		
		// String einlesen über Scanner+File-Objektkombo
		Scanner scanner;
		try {
			scanner = new Scanner( fileToRead );
			while( scanner.hasNextLine() == true )
			{
				// TODO Test Datei kommentar.test nicht bestanden
				// Umgang mit anderen Sachen, die Zeile rausfeuern
				
				int x = 0;
				int y = 0;
				
				// 34 456 erwartet
				if ( scanner.hasNextInt() == true )
				{
					x = scanner.nextInt();
				}
				
				if ( scanner.hasNextInt() == true )
				{
					y = scanner.nextInt();
				}
				
				if ( x == 0 && y == 0 )
				{
					// Zeilenumbruch am Ende
					break;
				}
				Point currentPoint = new Point( x, y );
				tempPunkte.add(currentPoint);
			}
			
			if ( punkte != null ) 
			{ 
				punkte.clear();
				punkte.addAll(tempPunkte);
			}
			
		} catch (FileNotFoundException exc) {
			// TODO Auto-generated catch block
			exc.printStackTrace();
		} catch (IOException exc) {
			// TODO Auto-generated catch block
			exc.printStackTrace();
		}
```


----------



## Atze (18. Aug 2009)

wenn deine liste nicht homogen ist, also nicht ausschließlich ints enthält, dann bleibt dir nix übrig dir objekte zu holen und zu casten.


----------



## Jay1980 (18. Aug 2009)

Meine Liste ist homogen, wenn du damit meinst, dass da nur Point-Objekte drin sind.
Was meinst du mit casten, ich dachte eher daran, dass ich da irgendwo eine if -Abfrage einbauen muss, ob ein Token nur Ziffern beinhaltet - ich kann aus deinem Tipp nicht ableiten was ich machen soll, sorry.


----------



## Atze (18. Aug 2009)

sorry, hab mich wphl nicht richtig ausgedrückt, außerdem ha ich mich auch n bischen vertan. ich nahm an, dass die methode next() ein object zurückgibt. sie gibt aber einen string, was aber auch nicht weiter schlimm ist. du musst dann wohl statt der methode nextInt() die methode next() aufrufen und versuchen den string in eine zahl zu parsen. vorher solltest du wie gesagt noch gucken, ob dies möglich ist, vielleicht mit nem regulären ausdruck.


----------



## Jay1980 (18. Aug 2009)

Servus,

ich habe nun nochmal das Kapitel Strings und deren Anwendung in der Insel überflogen. Es gibt sehr viele Möglichkeiten wie mir scheint, leider steh ich weiterhin ziemlich auf dem Schlauch, was die Umsetzung von 'huepfe eine Zeile weiter, wenn die Zeile nicht

inteins intzwei

aufgebaut ist. Ich glaube da hilft mir zum Verständnis auch nur die konkrete if-Abfrage die ich vor den hasNextInt() Blöcken schreiben muss, die mir in die nächste Iteration springt - diese Lösung kann ich dann durchdenken und nachvollziehen. hasNextInt() macht soweit ich das beurteilen kann genau, das was du gesagt hast Atze, also via next() den Token liefern lassen und in einen Integer parsen.


----------



## Atze (18. Aug 2009)

ja, nextInt macht das, aber du weißt ja nicht, ob du immer nen string hast, den du parsen kannst. und kommt eine zeichenkette, fliegt er raus. deswegen sofort den string nehmen und selbst gucken, ob es eine zahl sein kann.
dass du halt so komische strings drin hast, das meinte ich mit nicht-homogen


----------



## bygones (18. Aug 2009)

mal ehrlich... versteht einer diese skip Methode ?

wuerde mich auch brennend interessieren. Sie einfach mal zu rufen ist ja kein Problem aber geht das ueberhaupt in einer schleife ?

wuesste auch nix anderes als einfach ueber next() alles einlesen und schauen ob es eine Zahl ist


----------



## Jay1980 (18. Aug 2009)

Kann mir bitte einer diese eine Zeile als Code posten (das einfach über next() einlesen und schauen ob es eine Zahl ist) - danke.


----------



## Atze (19. Aug 2009)

```
String stringX = scanner.next();
if(stringX.matches("(-?)(\\d+)")){
	int intX = Integer.parseInt(stringX);
}
```


----------



## Jay1980 (21. Aug 2009)

Servus, es geht langsam weiter mit meiner Einleseaktion, jetzt kann ich mir folgendes nicht erklären: die erste Zeile wird wie geplant uebersprungen nachdem das erste Zeichen ein '#' ist. Die zweite Zeile wird dann ja geliefert. Nun sagt mir Eclipse in dieser Zeile liegen '100 100\n', also sollte daraus eigentlich der Punkt(100, 100) gebastelt werden. Aber obwohl in dieser Zeile gar kein 200 vorkommt, ist y 200 statt 100 - mir unerklärlich. Es scheint daran zu liegen, dass da irgendwas mit nextInt() ist, was ich nicht vorhersehe - ich dachte, das erste nextInt() liefert mir die 100 und der zweite nextInt() die zweite 100 ?!

Hier die Textdatei aus der gelesen wird: 
# 90 90
100 100
100 200
90 XXXX 210
200 200
200 100
230
90

Hier meine Einlese-Methode:


```
LaedtPunktemengeAusDateiK(Punktemenge pm, AktionsManager am)
	{
		// Punkte laden
		ArrayList<Punkt> tempPoints = new ArrayList<Punkt>();
		
		JFileChooser fc = new JFileChooser();
		int state = fc.showOpenDialog(null);
		
		if ( state == JFileChooser.APPROVE_OPTION )
		{
			File fileToRead = fc.getSelectedFile();
			// String einlesen über Scanner+File-Objektkombo
			Scanner scanner;
			try {
				scanner = new Scanner( fileToRead );
				while( scanner.hasNextLine() == true )
				{
					int x = 0;
					int y = 0;
					// 34 456 erwartet
					
					// sammle alle Zeichen in einem Array
					// und wenn eines dabei ist, dass weder Leerzeichen noch Ziffer ist, 
					// springe in die naechste Zeile
					String zeilenZeichen = scanner.nextLine();
					
					// Abbruchbedingungen vor der Zuweisung
					
					// es ist nur das abschliessende Zeilenumbruchzeichen
					if ( zeilenZeichen.length() == 1 && Character.isDigit('\n'))
					{
						// an den Schleifenkopf springen und danach wird die Schleife ohnehin nicht mehr ausgefuehrt
						continue;
					}
					
					// ein anderes Zeichen als Leerzeichen, Zeilenumbruch oder Ziffer
					boolean zeilenSprung = false;
					
					for ( int i = 0; i < zeilenZeichen.length(); i++ )
					{
						char aktuellesZeichen = zeilenZeichen.charAt(i);
						
						// Debugging um das Zeichen auf linebreak zu pruefen
						if ( aktuellesZeichen == '\n' && zeilenZeichen.length() == 1 )
						{
							System.out.println("Leerzeichen-Only-String erkannt.");
						}
						
						
						if ( Character.isDigit(aktuellesZeichen) == false && Character.isWhitespace(aktuellesZeichen) == false  && aktuellesZeichen != '\n' )
						{
							zeilenSprung = true;
							break;
						}
					}
					
					if ( zeilenSprung == true )
					{
						continue;
					}
					
					if ( scanner.hasNextInt() == true )
					{
						x = scanner.nextInt();
						System.out.println();
					}
					if ( scanner.hasNextInt() == true )
					{
						y = scanner.nextInt();
						System.out.println();
					}
					
					Punkt currentPoint = new Punkt( x, y );
					tempPoints.add(currentPoint);
				}
				
				pm.addPunkte(tempPoints);
				am.meldeNeueAktion(pm);
				// Zeichenflaeche wird in der NutzerAS-Instanz revalidated und repainted.
				
			} catch (FileNotFoundException exc) {
				// TODO Auto-generated catch block
				exc.printStackTrace();
			} catch (IOException exc) {
				// TODO Auto-generated catch block
				exc.printStackTrace();
			}
		}
	}
```
Danke für einen Tipp.


----------



## Atze (21. Aug 2009)

du kannst ja auch nicht nextLine() und nextInt() (dies sogar 2 mal ) aufrufen und erwarten, dass du den gleichen wert wiederbekommst! 

du rufst erst nextLine auf, bekommst 100 100, überprüfst diese auf das #, und um dann an die beiden 100er zu kommen rufst du nextInt auf. dann bekommst du aber schon die auf der nächsten zeile. du hast den string doch schon in aktuellesZeichen. du musst dann nur noch am leerzeichen dazwischen teilen


----------



## Jay1980 (21. Aug 2009)

Sodala,

jetzt gings, ich nutze nun den Scanner zum zeilenweisen einlesen, dann hau ich den Zeilenstring mit dem Tokenizer beim Leerzeichen auseinander und parse die Teilstuecke in einen Integer, aus dem ich denn später einen Punkt baue.
Dabei gibt es ein paar if-Abfragen, damit keine Exceptions auftreten.

Für Optimierungen bin ich natürlich dankbar, markiere den Thread trotzdem mal als gelöst. 


```
LaedtPunktemengeAusDateiK(Punktemenge pm, AktionsManager am)
	{
		// Punkte laden
		ArrayList<Punkt> tempPoints = new ArrayList<Punkt>();
		
		JFileChooser fc = new JFileChooser();
		int state = fc.showOpenDialog(null);
		
		if ( state == JFileChooser.APPROVE_OPTION )
		{
			File fileToRead = fc.getSelectedFile();
			// String einlesen über Scanner+File-Objektkombo
			Scanner scanner;
			try {
				scanner = new Scanner( fileToRead );
				while( scanner.hasNextLine() == true )
				{
					int x = 0;
					int y = 0;
					// 34 456  in einer Zeile erwartet
					
					// sammle alle Zeichen einer Zeile in einem Array
					String zeilenZeichen = scanner.nextLine();
					
					// ------------------------------------------
					// Abbruchbedingungen fuer eine Zeile
					// ------------------------------------------
					
					// es ist nur das abschliessende Zeilenumbruchzeichen im String, 
					// wie bei den Dateien die mit einem zusaetzlichen Zeilenumbruch schliessen
					if ( zeilenZeichen.length() == 1 && Character.isWhitespace('\n'))
					{
						// an den Schleifenkopf springen und danach wird die Schleife ohnehin nicht mehr ausgefuehrt
						continue;
					}
					
					// ein anderes Zeichen als Leerzeichen, Zeilenumbruch oder Ziffer
					// ist im String
					boolean zeilenSprung = false;
					
					for ( int i = 0; i < zeilenZeichen.length(); i++ )
					{
						char aktuellesZeichen = zeilenZeichen.charAt(i);
						
						// ein Zeichen ist eine Ziffer, ein Leerzeichen oder ein 
						// Zeilenumbruch, gehe dann einfach weiter und meckere nicht
						if ( Character.isDigit(aktuellesZeichen) == false && Character.isWhitespace(aktuellesZeichen) == false  && aktuellesZeichen != '\n' )
						{
							zeilenSprung = true;
							break;
						}
					}
					
					if ( zeilenSprung == true )
					{
						continue;
					}
					
					// Einsammeln der ersten beiden Tokens
					StringTokenizer tokenizer = new StringTokenizer(zeilenZeichen, " "); 
					
					
					// Zahl aus erstem Token, wenn die Tokenlaenge groesser 0 ist und wenn ein naechster Token vorhanden ist
					boolean tokenEinsDa = false;
					if ( tokenizer.hasMoreTokens())
					{
						String ersterToken = tokenizer.nextToken();
						if ( ersterToken.length() > 0 ) 
						{
							x = Integer.parseInt( ersterToken);
							tokenEinsDa = true;
						}
						else 
						{
							// keine ganze Zahl möglich, nextLine
							continue;
						}
					}
					
					boolean tokenZweiDa = false;
					if ( tokenizer.hasMoreTokens() )
					{
						String zweiterToken = tokenizer.nextToken();
						if ( zweiterToken.length() > 0 )
						{
							// Zahl aus zweitem Token
							y = Integer.parseInt( zweiterToken );
							tokenZweiDa = true;
						}
						else 
						{
							// keine ganze Zahl möglich, nextLine
							continue;
						}
					}
					
					// Zuweisung wenn beide Tokens da sind
					if ( tokenEinsDa == true && tokenZweiDa == true )
					{
						Punkt currentPoint = new Punkt( x, y );
						tempPoints.add(currentPoint);
					}
				}
				
				pm.addPunkte(tempPoints);
				am.meldeNeueAktion(pm);
				// Zeichenflaeche wird in der NutzerAS-Instanz revalidated und repainted.
				
			} catch (FileNotFoundException exc) {
				// TODO Auto-generated catch block
				exc.printStackTrace();
			} catch (IOException exc) {
				// TODO Auto-generated catch block
				exc.printStackTrace();
			}
		}
	}
```


----------

