MezData-Logo

Passwort-Projekt: Formatiertes Ausgeben und Datei-IO

Synopsis: How do I scanf, readln, etc. in Java? http://www.cafeaulait.org/javafaq.html

Klasse Passwort -zwei Listen Name und Passwort

Zwei Felder für Namen und Passwörter sind vorgegeben.

Erstellen Sie eine Methode ausgeben(), die alle Namen nebst Passwörtern auf der Konsole ausgibt.

0. Anna f1c4e
1. Bert 123gaga
2. Kalleman babe

Erstellen Sie eine Methode ausgeben2(), die alle Namen nebst Passwörtern formatiert auf der Konsole ausgibt. Die Namen sollen 10 Zeichen breit dargestellt werden. Tipp: Mit Leerzeichen auffüllen.

1. Anna       f1c4e
2. Bert       123gaga
3. Kalleman   babe

Zeichen Sie ein Struktorgramm für ausgeben2().

LösungStruktogramm

Entwickeln Sie die Methode check(n,p:Text):Boolean, mit Rückgabe true, wenn Name n und dazugehöriges Passwort p in den Feldern vorhanden ist, sonst Rückgabe false.

Entwickeln Sie die Methode aenderePswd(n,ap.np:Text):Boolean, zum Ändern des Passworts, dabei ist n der Name und ap,np altes und neues Passwort. Rückgabe true bei Erfolg, false sonst.

Lösung anzeigen..
Quellcode [Passwortprojekt/PasswortAufgaben.java]
public class PasswortAufgaben{
  static String namen[] = {"Anna","Bert","Kalle"};
  static String pswd[] = {"f1c4e","123gaga","babe"};
 
  static void ausgeben(){
    int i;
    // Aufgabe
  }
  static void ausgeben2(){
    int i,k;
    // Aufgabe
  }
  static boolean check(String n, String p){
    int i;
    // Aufgabe
    return false;
  }
  static boolean aenderePswd(String n,String ap,String np){
    int i;
    // Aufgabe
    return false;
  }
  static void ausgebenPrintf(){
    int i;
    for(i=0;i<namen.length;i++){
      System.out.printf("%2d. %-10s %s %n",i+1,namen[i],pswd[i]);
    }  
  }
}

Formatiertes Ausgeben mit printf

In praktisch jeder Programmierumgebung gibt es Funktionen um Ausgaben zu formatieren. Hier soll die Java-Methode printf vorgestellt werden.

 1. Anna       f1c4e 
 2. Bert       123gaga 
 3. Kalleman   babe 
public static void ausgebenPrintf(){
  int i;
  for(i=0;i<namen.length;i++){
    System.out.printf("%2d. %-10s %s %n",i+1,namen[i],pswd[i]);
  }  
}

Formatstring

Format-Spezifizierer Wirkung
%2d Ausgabe zweistellige Dezimalzahl, rechtsbündig
%-10s Ausgabe als String 10 Zeichen Platz durch - linksbündig
%s Ausgabe String ohne besondere Formatierung
%n Ausgabe neue Zeile-Zeichen

Speichern der Daten in einer Datei

Daten in Dateien (Files) zu speichern folgt diesem Prinzip:

Path pfad= Paths.get(dateipfad); // Pfad als String
Writer aus= Files.newBufferedWriter(pfad); // Datenstrom
aus.write("Daten schreiben"); // in den Strom schreiben
aus.close(); // schliessen, Gepuffertes wegschreiben

Es können dabei Probleme auftreten -Dateipfad existiert nicht, Datei ist schon durch anderes Programm geöffnet usw.

Exceptions (Ausnahmefehler)

In modernen Programmiersystemen werden Probleme in aufgerufenen Methoden durch Ausnahmefehlermeldungen (Exceptions) an die darüber liegenden Programmebenen gemeldet und dort bearbeitet.

In Java wird hierzu das try-catch-Konstrukt verwendet.
Java ist auch eine Insel / Ausnahmen müssen sein

Quellcode [Passwortprojekt/PasswortDateiWrite.java]
import java.io.*; // Writer_Klasse
import java.nio.file.*; // Pfad-Klasse
 
public class PasswortDateiWrite{
  static String namen[] = {"Anna","Bert","Kalle"};
  static String pswd[] = {"f1c4e","123gaga","babe"};
  static String dateiname = "accounts.txt";
 
  static void ausgebenDatei(){
    int i;
    Path pfad = Paths.get(dateiname); // Dateipfad
    try {  // versuchen wir mal folgenden Code aus zu fuehren
      Writer aus = Files.newBufferedWriter(pfad); //Datenschreiber
      for(i=0;i<namen.length;i++){
        aus.write(namen[i]+"\t"+pswd[i]+"\n"); //schreibe
      }  
      aus.close(); // schliesse Datenstrom
    } catch (IOException e) {  //falls ein IO-Ausnahmefehler
      System.out.println("IOException: " + e);  //Fehler ausgeben
    }
  }
}

Struktogramm

Implementieren Sie den Quelltext und testen Sie die Methode ausgebenDatei().

Die ausgegebene Textdatei befindet sich im Projektordner.

Name und Passwort werden bei der Ausgabe durch ein Tabulatorzeichen "\t" getrennt.

Wenn aus.close(); weggelassen (auskommentiert) wird werden die Ausgaben ggfs. nicht abgespeichert, erst durch close() wird der Pufferspeicher im Hauptspeicher auf das Dateisystem weggeschrieben, testen Sie das.

Struktogramm mit try-catch

Ein Strukogramm zu erstellen gestaltet sich schwierig, da try-catch-Blöcke unbekannt waren als Strukogramme entwickelt wurden. Structorizer erlaubt trotzdem eine Darstellung.

String.format(..)

Formatiertes Ausgeben ist auch bei Dateien möglich: JavaInsel

aus.write(String.format("%2d. %-10s %s %n",i+1,namen[i],pswd[i]));

Erstellen Sie eine Methode ausgebenTabelle(filename:Text) für die formatierte Ausgabe einer Tabelle Nettopreis und Bruttopreis mit 19% Steuer, von 1€ bis 9€ in eine Datei filename ausgibt.

Erstellen Sie eine Methode ausgebenQuadrate(n:GZ), die Quadratzahlen von 1 bis n formatiert in eine Datei quadrate.txt ausgibt.

 

Link zur Vertiefung

Java ist auch eine Insel: 18 Einführung in Dateien und Datenströme

Datei auslesen

Auch beim Lesen wird zunächst ein Datenstrom geöffnet. Es können einzelne Informationen oder auch ganze Zeilen gelesen werden.

Am Ende der Datei (End Of File - EOF) gibt die readLine() Methode einen null - Verweis zurück.

 

Dateizeilen einlesen und ausgeben bringt die Information nicht in die Arrays zurück...

Quellcode [Passwortprojekt/PasswortDateiRead.java]
import java.io.*; // Writer_Klasse
import java.nio.file.*; // Pfad-Klasse
 
public class PasswortDateiRead{
  static String dateiname = "accounts.txt";
 
  static void leseDatei()  {
    String zeile;
    Path pfad = Paths.get(dateiname); // Dateipfad
    try { // versuchen wir mal folgenden Code aus zu fuehren
      BufferedReader ein = Files.newBufferedReader(pfad); // oeffne Datenstrom
      while ((zeile = ein.readLine())!= null){ // solange Datensaetze vorhanden, lese Zeile
        System.out.println(zeile); // gib Zeile aus
      }
      ein.close(); // schliesse Datenstrom
    } catch (IOException e) { // falls ein IO-Ausnahmefehler auftritt tue folgendes
       System.out.println("IOException: " + e); // gib den Fehler auf der Konsole aus  
    }
  }
}

Lesen von name und pswd aus Datei

Die Namen und Passwörter sollen wieder in die Arrays eingelesen werden.

Zunächst werden jeweils 5 Strings vorgesehen.

ausgebenPrintf() gibt null bei leeren Strings aus, unterbinden Sie das.

Die aus der Datei eingelesene Zeile soll beim Tabulator zerteilt werden.

Ergänzen Sie lesePasswortDatei() entsprechend.

Lösung anzeigen..

Erstellen Sie ein Struktogramm für lesePasswortDatei().

Sind mehr als 5 Datensätze in der Datei wird eine Exception ausgegeben, wie kann das verhindert werden?

 

Fragen:

  1. Was ist eine Exception?
  2. Wozu ist try-catch nötig?
  3. Warum ist die Anweisung import java.io.*; notwendig?
  4. Welche Schritte sind für Dateiausgabe notwendig?
  5. Warum müssen Dateien wieder geschlossen werden?
Quellcode [Passwortprojekt/PasswortDateiRead2Aufgabe.java]
import java.io.*; // Writer_Klasse
import java.nio.file.*; // Pfad-Klasse
 
public class PasswortDateiRead2Aufgabe{
  static String dateiname = "accounts.txt";
  static String namen[] = new String[5]; // Array fuer 5 Strings
  static String pswd[] = new String[5];  
 
  static void lesePasswortDatei()  {
    int datensatz = 0; // zaehlt die gelesenen Datensaetze
    int i;
    char c;
    String zeile;
    Path pfad = Paths.get(dateiname); // Dateipfad
    try { // versuchen wir mal folgenden Code aus zu fuehren
      BufferedReader ein = Files.newBufferedReader(pfad); // oeffne Datenstrom
      while ((zeile = ein.readLine())!= null){ // solange Datensaetze vorhanden, lese Zeile
        namen[datensatz]="";
        pswd[datensatz]="";
        for(i=0;i<zeile.length();i++){ // die Zeile bis Tab durchgehen
          c=zeile.charAt(i);
          // Aufgabe: namen einlesen
        }
        for(;i<zeile.length();i++){ // bis zum Ende
          pswd[datensatz]+=zeile.charAt(i);
        }
        datensatz++;
      }
      ein.close(); // schliesse Datenstrom
    } catch (IOException e) { // falls ein IO-Ausnahmefehler auftritt tue folgendes
       System.out.println("IOException: " + e); // gib den Fehler auf der Konsole aus  
    }
  }
  static void ausgebenPrintf(){
    int i;
    for(i=0;i<namen.length;i++){
      // Aufgabe: Bei null aufhoeren
      System.out.printf("%2d. %-10s %s %n",i+1,namen[i],pswd[i]);
    }
  }
}

Datensatzanzahl und split-Funktion

Anzahl der zu lesenen Datensätze kennen

write(..) kann auch Werte schreiben: Anzahl der Datensätze am Anfang der Datei schreiben und beim Einlesen entsprechend viele Plätze reservieren.

Eingelesene Datei-Zeilen leichter verarbeiten.

Eingelesenen String mit split(..) zerlegen.
JavaInsel
Wikipedia: Reguläre Ausdrücke

Quellcode [Passwortprojekt/PasswortSplit.java]
import java.io.*;
import java.nio.file.*; // Pfad-Klasse
 
public class PasswortSplit{
  static String namen[] = {"Anna","Bert","Kalle"};
  static String pswd[] = {"f1c4e","123gaga","babe"};
  static String dateiname = "accounts.txt";
 
  public static void ausgebenDatei()  {
    int i;
    try {  // versuchen wir mal folgenden Code aus zu fuehren
      Writer aus = Files.newBufferedWriter(Paths.get(dateiname));
      aus.write(namen.length); // Anzahl der Daten speichern
      for(i=0;i<namen.length;i++){
        aus.write(namen[i]+"\t"+pswd[i]+"\n"); //schreibe mit Tabulator getrennt
      }  
      aus.close(); // schliesse Datenstrom
    } catch (IOException e) {  // falls ein IO-Ausnahmefehler auftritt
      System.out.println("IOException: " + e);  // gib den Fehler aus   
    }
  }
  public static void leseDaten()  {
    int anzahl,i=0;
    String zeile;
    String[] teile;
    try {
      BufferedReader ein = Files.newBufferedReader(Paths.get(dateiname));
      anzahl = ein.read();  // lese Anzahl der Datensaetze
      System.out.println("Anzahl Datensaetze: "+anzahl);
      namen = new String[anzahl]; // erzeuge entsprechende Speicherplaetze
      pswd = new String[anzahl];
      while ((zeile = ein.readLine())!= null){
        teile = zeile.split("\t"); // zerteile Zeile
        namen[i]=teile[0];
        pswd[i]=teile[1];
        System.out.printf("%2d. %-10s %s %n",i+1,namen[i],pswd[i]);
        i++;
      }
      ein.close();
    } catch (IOException e) {
       System.out.println("IOException: " + e);   
    }
  }
}

 

ArrayList

Beliebig viele Strings ohne viel Aufwand anhängen.

Array-List:JavaInsel

Quellcode [Passwortprojekt/PasswortList.java]
import java.io.*;
import java.nio.file.*; 
import java.util.*;  // List
 
public class PasswortList{
  static List<String> namen = new ArrayList<>();
  static List<String> pswd = new ArrayList<>();
  static String dateiname = "accounts.txt";
 
  static void test(){ // Einfuegen zeigen
    namen.addAll(Arrays.asList("Anna","Bert","Kalle"));
    pswd.addAll(Arrays.asList("f1c4e","123gaga","babe"));
  }
  public static void leseDaten()  {
    String zeile;
    String[] teile;
    try {
      BufferedReader ein = Files.newBufferedReader(Paths.get(dateiname));
      while ((zeile = ein.readLine())!= null){
        teile = zeile.split("\t"); // zerteile Zeile
        namen.add(teile[0]);
        pswd.add(teile[1]);
      }
      ein.close();
    } catch (IOException e) {
       System.out.println("IOException: " + e);   
    }
  }
  static void ausgeben(){
    int i;
    for(i=0;i<namen.size();i++){
      System.out.printf("%2d. %-10s %s %n",i+1,namen.get(i),pswd.get(i));
    }
  }
}

Eine Liste mit Account-Objekten

Klassendiagramm

Zwei getrennte Listen für Namen und Passwörter sind nicht praktisch.

 

Die Datensätze werden jetzt in Account-Objekten gespeichert. Eine Klasse Account gibt die Datenstruktur vor.

In einer Klasse Passwortverwalter werden die Account-Objekte als Liste verwaltet.

 

Mit der Operation test() können Beispieldatensätze angelegt werden.

 

Praktische Info:

TG-Formelsammlung

Quellcode [PasswortOOP/PasswortVerwalterAufg.java]
import java.io.*;
import java.nio.file.*; 
import java.util.*;  // List
 
public class PasswortVerwalterAufg{
  static List<Account> accounts = new ArrayList<Account>();
  static String dateiname = "accounts.txt";
 
  static void test(){
    accounts.add(new Account("Anna","f1c4e"));
    accounts.add(new Account("Bert","123gaga"));
    accounts.add(new Account("Kalle","babe"));
  }
  static void ausgeben(){
    int i;
    for(i=0;i<accounts.size();i++){
      System.out.printf("%2d. %-10s %s %n",i+1,accounts.get(i).name,
                                               accounts.get(i).pswd);
    }
  }
  static void leseDaten()  {
    String zeile;
    String[] teile;
    try {
      BufferedReader ein = Files.newBufferedReader(Paths.get(dateiname));
      while ((zeile = ein.readLine())!= null){
        teile = zeile.split("\t"); // zerteile Zeile
        accounts.add(new Account(teile[0],teile[1]));
      }
      ein.close();
    } catch (IOException e) {
       System.out.println("IOException: " + e);   
    }
  }
  static boolean check(String n, String p){
    int i;
    // Aufgabe
    return false;
  }
  static boolean neuerAccount(String n,String p){
    int i;
    // Aufgabe
    return true;
  }
  static void ausgebenDatei(){
    int i;
    try {  // versuchen wir mal folgenden Code aus zu fuehren
      // Aufgabe
    } catch (IOException e) {  // falls ein IO-Ausnahmefehler auftritt
      System.out.println("IOException: " + e);  // gib den Fehler aus   
    }
  }
}
Quellcode [PasswortOOP/Account.java]
public class Account{
  String name;
  String pswd;
  Account(String n,String p){//Konstruktor
    name = n;
    pswd = p;
  }
}
Testdaten [PasswortOOP/accounts.txt]
Nino	38zHHei8*
Robert	hGe3Uiie9
Marie	nez7wwiI

Aufgaben

Eine Operation check(n:Text,p:Text):bool überprüft, ob bei existierenden Namen n das Passwort p stimmt.

Eine Operation neuerAccount(n:Text,p:Text):bool überprüft, ob der Name schon existiert, sonst wird ein neues Account-Objekt erzeugt und in die Liste hinzugefügt.

ausgebenDatei() gibt die Account-Liste in eine Datei aus.

Lösung anzeigen..

Ausblick, ToDo

  1. Passwörter verschlüsselt speichern (Hashwerte)
  2. Eingaben und Konsistenzen besser überprüfen
  3. Passwortregeln einführen und überprüfen
  4. Erweitern um Nickname, Ausgabe, Dateischreiben und Lesen in Account-Klasse verlagern
  5. Polymorphie demonstrieren
  6. Als XML-Format speichern