Montag, 6. Juni 2011
Listings - Source Code drucken
Es sollte m.E. doch möglich sein, Listings in der folgenden - lesbaren - Form zu erzeugen:
Die Schrift ist leserlich, die Zeilen richtig eingerückt, Dateiname und Autor etc. sind ersichtlich (Ich gebe zu, dieses Bild ist schlecht...). Verwendet wurde recode und a2ps unter Linux.
Aber mit Office kann man auch sehr schöne Listings erzeugen:
Man muss nur Kopf- und Fußzeilen einrichten, die Schrift auf Courier New (9 oder 10pt) oder eine andere mit fixer Breite und dann zwei Seiten auf eine drucken.
Stattdessen bekommt man so etwas.
Der Sourcecode ist unmöglich zu lesen, weil die Einrückung bald die dargestellte Zeilenlänge erreicht. Die Schriftgröße ist aber zumindest OK.
Schlimm ist aber das folgende Listing, da würden 4 Code-Seiten auf einem Blatt Platz haben. Trotzdem sind es aber nur 2 Seiten pro Blatt. Die Schrift ist unleserlich klein.
Das ist eine Zumutung!
Labels: allgemeines, POS1-1, POS1-2, PR2, PR3, PR5
Mittwoch, 20. April 2011
Locking- und Updateprobleme mit RMI (PR: 5A, 5B)
Da bei normalen "Remote-Objekten" immer ganze Objekte übertragen werden, hat man echtes Call-By-Value. Man arbeitet also mit Kopien von Objekten. Das gilt sowohl für Objekte, die an eine Methode eines Remote-Objekts übergeben werden als auch für Objekte als Returnwerte.
Normalerweise werden innerhalb einer JVM Objekte immer als Referenz übergeben bzw. zurückgegeben. Über RMI werden die Objekte immer serialisiert und komplett übertragen.
Das wirkt sich bei unserem "Data-Beispiel" so aus, dass Updates erst "sichtbar" werden, wenn die Daten auch wieder von der Datei geladen werden. Sollte das Locking direkt in der Record-Implementierung erfolgen, so muss man bedenken, dass auch immer ein
Record-Objekt übertragen wird und der Client mit einer Kopie arbeitet. Damit bekommt er immer den Lock!
Abhilfe?
Man darf dem Client
nur Referenzen zur Verfügung stellen. Nur der Server arbeitet mit den "echten" Objekten. - Klar, und wie geht das?
Das herauszufinden war nicht einfach, die Java-Dokumentation von Sun (Oracle) schweigt sch diesbezüglich aus, zumindest was direkte Lösungsvorschläge und Beispielcode angeht.
Grundsätzliche Erklärung und Beispiele zur Funktionsweise von RMI hilft etwas:
jGuru: Remote Method Invocation (RMI) aber die letzte zündende Idee bekommt man durch
How to return object and return object reference in Java RMI
Der Unterschied liegt in der Klassendefinition. Wenn die Klasse eine Unterklasse von
UnicastRemoteObject ist, so ist sie damit RMI-fähig (kann RMI-Service anbieten). In diesem Fall wird also eine Referenz zurückgegeben werden. Wenn die Klasse nur
Serializable implementiert, so ist die Klasse nicht für RMI-Aufrufe geeignet (kein RMI-Service) und es wird das Objekt zurückgegeben werden.
Ein Beispiel wird in Kürze geliefert.
Labels: Java, PR5
Freitag, 15. April 2011
Serialisierungsprobleme mit RMI (PR: 5A, 5B)
Remote
Method
Invocation funktioniert nur für Objekte, welche serialisiert werden können (die also das Interface
Serializable implementieren). Wird innerhalb eines Objekts, welches über RMI gebunden werden soll, eine Referenz auf ein nicht serialisierbares Objekt benötigt, so bekommt man immer eine Exception der Art (z.B. für
RandomAccessFile):
sun.rmi.registry.RegistryImpl_Stub.rebind(Unknown Source)
java.rmi.Naming.rebind(Naming.java:177)
rmi.Server.run(Server.java:43)
rmi.Server.main(Server.java:68)
error marshalling arguments; nested exception is:
java.io.NotSerializableException: java.io.RandomAccessFile
RandomAccessFile ist nicht serialisierbar, weil der innere Zustand so eines Objekts von der Laufzeitumgebung (Betriebssystem, Dateisystem, etc.) abhängt. Eine Referenz auf ein
RandomAccessFile-Objekt muss als
transient deklariert werden. Wenn nötig, dann muss der Zustand selbst gespeichert bzw. wiederhergestellt werden, z.B. in Form des Dateinamens und der Position innerhalb der Datei.
Dazu kann man die Methoden des Interfaces
Serializable implementieren:
private void writeObject(java.io.ObjectOutputStream out)
throws IOException
private void readObject(java.io.ObjectInputStream in)
throws IOException, ClassNotFoundException;
private void readObjectNoData()
throws ObjectStreamException;
Für die Zwecke der
Data-Klasse genügen die ersten beiden Methoden. Folgender Ausschnitt aus der
Data-Klasse soll dies verdeutlichen:
public class Data implements DB, Header, Serializable {
private static final long serialVersionUID = -8958941842720498616L;
private transient RandomAccessFile database;
private String filename;
private transient Logger log = Logger.getLogger(LOGURL);
private DBHeader header;
private TreeMap data;
private int lastDeleted = -1; // zuletzt gelöschter Datensatz
private void writeObject(ObjectOutputStream stream) throws IOException {
stream.defaultWriteObject();
stream.writeLong(database.getFilePointer());
}
private void readObject(ObjectInputStream stream) throws IOException {
try {
stream.defaultReadObject();
} catch (ClassNotFoundException e) {
String msg = "Unable to find class";
if (e.getMessage() != null)
msg += ": " + e.getMessage();
throw new IOException(msg);
}
database = new RandomAccessFile(filename, "rw");
database.seek(stream.readLong());
log = Logger.getLogger(LOGURL);
}
//... weiterer Code entfernt
}
Hier ist auch der Logger transient, da auch der Logger vom Dateisystem abhängt und nicht serialisiert werden kann.
Labels: Java, PR5
Mittwoch, 13. April 2011
Package für Data, Header, Record (PR: 5A, 5B)
Verwenden Sie unbedingt das
Package data für die Klassen und Interfaces
DB,
Data,
Header und
Record. Nur so kann der Datenteil automatisch getestet werden.
Labels: Aufgabe, Java, PR5
Dienstag, 12. April 2011
Refactoring Data - Interface Record (PR: 5A, 5B)
Um einfacher automatisch testen zu können, soll das Interface
Record (anstatt einer Klasse
Record) bei der Datenklasse
Data verwendet werden.
package data;
/**
* RuD-Übung 2011
*
* data.Record.java
* @author (c) 2011, Harald R. Haberstroh
* 1.3.2011
*/
import java.rmi.Remote;
import java.rmi.RemoteException;
/**
* @author Harald R. Haberstroh (hh)
*
*/
public interface Record extends Remote {
/**
* @return Anzahl der Felder dieses Datensatzes z.B. 8
*/
public int getCntFields() throws RemoteException;
/**
* @return liefert alle Felder verkettet in einen String mit ';' getrennt
* z.B.
* "12;49.82012,17.260019;47.81497,16.260409;523;201102131510;67;2345121269;O"
* Strings sind unter Hochkomma zu setzen. z.B.
* "\"wlr1\";\"Werk Laderoboter 1\";\" 50\";\" 50\""
*/
public String getCsvString() throws RemoteException;
/**
* Setzt die Felder auf die gegebenen Strings. Gegebenenfalls sind die
* Strings abzuschneiden. Die Anzahl der Strings stimmt beim Aufruf von
* setFields() auf alle Fälle mit getCntFields() überein (Prüfung braucht
* nicht zu erfolgen).
*
* @param fields
* Array mit den Feldwerten.
* @throws RemoteException
*/
public void setFields(String[] fields) throws RemoteException;
}
Damit kann per JUnit-Test ganz einfach der Inhalt eines Datensatzes und die Anzahl der Felder geprüft werden.
Überdies wird hier
Refactoring geübt, da das Programm (die Klassen) entsprechend angepasst werden müssen (Ihre
Record-Klasse muss umbenannt werden und eben das gegebene Interface implementieren).
Labels: Aufgabe, Java, PR5
Mittwoch, 6. April 2011
GUI für RMI-Client (PR: 5A, 5B)
Erstellen Sie für den Client unserer RMI-Client/Server-Anwendung eine grafische Oberfläche, welche den Inhalt der Datenbank (evtl als Ausschnitt) in einer JTable darstellt. Es soll möglich sein, einzelne Werte zu ändern, Zeilen zu löschen, neue Zeilen einzufügen und zu Suchen (eigener Dialog). Bei der Suche sind nur die gefundenen Datensätze anzuzeigen.
Das GUI könnte (minimal, die Funktionen zum Löschen, Einfügen und Suchen sind in "Edit" versteckt) etwa so aussehen:
Labels: Aufgabe, Java, PR5
Freitag, 18. März 2011
Experimente mit Queues (POS1-2: 2A, 2C, PR: 5A, 5B)
Implementieren Sie im Package
ringbuffer eine Klasse
Queue für Integer-Werte. Implementieren Sie die
Queue in Form eines
Ringpuffers. Es soll die Exception
QueueFullException geworfen werden, wenn der Ringpuffer voll ist und die Exception
QueueEmptyException, wenn der Ringpuffer leer ist.
Als Testklasse verwenden Sie bitte die folgende Klasse
QueueTest, welche zwei Threads erzeugt. Einen Sender, der zufällige "Nachrichten" (Integerwerte) in die Warteschlange steckt und einen Empfänger, der Werte aus der Warteschlange entnimmt. Beide geben jeweils die gesendete bzw. gelesene Nachricht aus. Beide Threads haben unterschiedliche zufällige Wartezeiten.
Experimentieren Sie mit unterschiedlichen Wartezeiten, sodass die Queue (Ringpuffer) voll bzw. leer werden kann. Was passiert?
Wenn die Queue nicht synchronisiert wird, was kann passieren? Können Sie das Verhalten in Experimenten nachweisen?
Bauen Sie entsprechendes
synchronized in Ihre Queue ein (5. Klasse). Was muss synchronisiert werden?
Source Code
/**
* java-queue: ringbuffer.QueueTest.java
*
* 17.03.2011, Harald R. Haberstroh
*/
package ringbuffer;
import java.util.Random;
/**
* Testen der Queue mit 2 Threads.
*
* @author Harald R. Haberstroh (hp)
*
*/
public class QueueTest extends Thread {
private final static int MAXRUNS = 20;
private Queue messages;
private Random rnd = new Random();
public QueueTest(String name, Queue messages) {
super(name);
this.messages = messages;
}
public void run() {
if (getName().equalsIgnoreCase("Sender")) {
for (int i = 0; i < MAXRUNS; i++) {
int msg = rnd.nextInt(100);
long sleepTime = (long) rnd.nextInt(500) + 500; // 500-1000ms
try {
System.out.println(getName() + " sending " + msg);
messages.put(msg);
} catch (QueueFullException e) {
System.err.println(getName() + ":Oops - Queue full");
}
try {
sleep(sleepTime);
} catch (InterruptedException e) {
System.err.println(getName() + ":Oops - interrupted");
}
}
} else { // Receiver
setName(" " + getName()); // Einrückung
for (int i = 0; i < MAXRUNS || !messages.isEmpty(); i++) {
long sleepTime = (long) rnd.nextInt(900) + 600; // 900-1500ms
try {
if (!messages.isEmpty()) {
System.out.println(getName() + " read " + messages.get());
}
} catch (QueueEmptyException e) {
System.err.println(getName() + ": Oops - Queue empty");
}
try {
sleep(sleepTime);
} catch (InterruptedException e) {
System.err.println(getName() + ":Oops - interrupted");
}
}
}
System.out.println(getName() + " finished");
}
public String toString() {
return getName();
}
/**
* @param args
*/
public static void main(String[] args) {
Queue messages = new Queue();
QueueTest sender = new QueueTest("Sender", messages);
QueueTest receiver = new QueueTest("Receiver", messages);
sender.start();
receiver.start();
try {
sender.join();
receiver.join();
} catch (InterruptedException e) {
System.err.println(e.getLocalizedMessage());
}
System.out.println(".... all finished ....");
}
}
Beispielausgabe bei Standardeinstellung
Sender sending 73
Sender sending 46
Receiver read 73
Sender sending 63
Receiver read 46
Sender sending 11
Receiver read 63
Sender sending 89
Sender sending 21
Receiver read 11
Sender sending 99
Receiver read 89
Sender sending 79
Sender sending 41
Receiver read 21
Sender sending 17
Receiver read 99
Sender sending 92
Receiver read 79
Sender sending 89
Receiver read 41
Sender sending 52
Receiver read 17
Sender sending 60
Receiver read 92
Sender sending 25
Receiver read 89
Sender sending 82
Sender sending 60
Receiver read 52
Sender sending 13
Sender sending 34
Receiver read 60
Sender sending 10
Receiver read 25
Sender finished
Receiver read 82
Receiver read 60
Receiver read 13
Receiver read 34
Receiver read 10
Receiver finished
.... all finished ....
Labels: Aufgabe, Java, POS1-2, PR5
Mittwoch, 16. März 2011
RMI-Client/Server mit Java (PR: 5A, 5B)
Schreiben Sie eine Klasse
Server, welche über
RMI folgende Objekte bereitstellt:
rmi://localhost/Data (implementiert Interface DB)
rmi://localhost/Header (implementiert Interface Header)
localhost kann natürlich (theoretisch) jede beliebige IP-Adresse bzw. jeder beliebige Hostname sein, wenn unter dieser Adresse ein RMI-Server erreichbar ist.
Die zu öffnende Datei soll als Parameter der Kommandozeile angegeben werden. Aufruf:
java Server dateiname
Also z.B.:
java Server machines.db
Schreiben Sie weiters eine Klasse
Client, welche über
RMI die vom
Server bereitgestellten Objekte verwendet, um einfache "Datenbank"-Zugriffe zu machen. Beim Aufruf soll über einen optionalen Kommandozeilenparameter die Adresse des Servers angegeben werden. Wird kein Parameter angegeben, so ist
localhost zu verwenden. Aufruf:
java Client [hostaddress]
Also z.B.:
java Client
oder
java Client 127.0.0.1
Client soll einfach den Inhalt der Datenbank ausgeben (nur nicht gelöschte Datensätze). Die Spaltenüberschriften sind entsprechend der Informationen aus der Datei (über
Header) ausgeben. Zeilen müssen gemäß der Feldlängen formatiert sein.
In weiterer Folge soll der Client
eine Minute lang zufällige Datensätze auswählen und ausgeben (falls diese existieren). Zwischen zwei Zugriffen soll eine zufällige Zeit zwischen 1 und 5 Sekunden liegen (d.h. es werden mindestens 12 und höchstens 60 weitere Zeilen ausgegeben).
Labels: Aufgabe, Java, PR5
Binärdateien bearbeiten mit Java - Zusatz für Client (PR: 5A, 5B)
Wie unter "
Binärdateien bearbeiten mit Java (PR: 5A, 5B)" beschrieben, ist in der Binärdatei auch die Feldbeschreibung enthalten. An Server bzw. in der Klasse
Data wird aber nur die Typ bzw. Längeninformation benötigt. Implementieren Sie das Interface
Header (entweder eigene Klasse oder Klasse
Data), um die nötigen Informationen für den Client, der ja die Daten darstellen soll, bereitzustellen.
package data;
/**
* RuD-Übung 2011
*
* data.Header.java
* @author (c) 2011, Harald R. Haberstroh
* 16.3.2011
*/
import java.rmi.Remote;
import java.rmi.RemoteException;
/**
* @author Harald R. Haberstroh (hh)
*
*/
public interface Header extends Remote {
/**
* Liefert die Anzahl der Felder.
*
* @return Anzahl der Felder
* @throws RemoteException
*/
public int getNumFields() throws RemoteException;
/**
* Liefert den Feldnamen zur gegebenen Feldnummer
*
* @param fieldNo
* Nummer Feldes.
* @return Feldname
* @throws RemoteException
*/
public String getFieldName(int fieldNo) throws RemoteException;
/**
* Liefert die Beschreibung des Feldes zur gegebenen Feldnummer
*
* @param fieldNo
* Nummer Feldes.
* @return Feldbeschreibung
* @throws RemoteException
*/
public String getFieldDesc(int fieldNo) throws RemoteException;
/**
* Liefert den Typ des Feldes zur gegebenen Feldnummer.
* 'F'...fix, 'V'...Variabel, 'C'...Zeichen
*
* @param fieldNo
* Nummer Feldes.
* @return Typ des Feldes ('F', 'V' oder 'C')
* @throws RemoteException
*/
public char getFieldType(int fieldNo) throws RemoteException;
/**
* Liefert die (maximale) Länge des Feldes zur gegebenen Feldnummer.
*
* @param fieldNo
* Nummer Feldes.
* @return (maximale) Feldlänge
* @throws RemoteException
*/
public int getFieldLen(int fieldNo) throws RemoteException;
}
Labels: Aufgabe, Java, PR5
Dienstag, 1. März 2011
Binärdateien bearbeiten mit Java (PR: 5A, 5B)
Entwickeln Sie sie eine Klasse
Data, die das Interface
DB implementiert. Die Klasse
Data soll die Methoden zum Bearbeiten (Lesen, Schreiben) binärer Dateien nach dem unten beschriebenen Datenformat bereitstellen.
package data;
/**
* RuD-Übung 2011
*
* data.DB.java
* @author (c) 2011, Harald R. Haberstroh
* 1.3.2011
*/
import java.rmi.Remote;
import java.rmi.RemoteException;
/**
* @author Harald R. Haberstroh (hh)
*
*/
public interface DB extends Remote {
/**
* Sperre den gegebenen Datensatz. Ist der Datensatz bereits gesperrt, dann
* wait().
*
* @param recNo
* Nummer des zu sperrenden Datensatzes.
* @return Sperr-Identifikation.
* @throws RemoteException
* @throws RecordNotFoundException
* ungültige Datensatznummer
*/
public long lock(int recNo) throws RemoteException, RecordNotFoundException;
/**
* Hebe Sperre wieder auf. notify().
*
* @param recNo
* Nummer des gesperrten Datensatzes
* @param lockCookie
* Sperr-Identfikation.
* @throws RemoteException
* @throws RecordNotFoundException
* ungültige Datensatznummer
* @throws SecurityException
* ungültiges lockCookie oder nicht gelockt
*/
public void unlock(int recNo, long lockCookie) throws RemoteException,
RecordNotFoundException, SecurityException;
/**
* lege neuen Datensatz an.
*
* @param record
* Neuer Datensatz
* @return Nummer des neuen Datensatzes
* @throws RemoteException
*/
public int create(Record record) throws RemoteException;
/**
* lösche Datensatz. Sperre wird aufgehoben, notify();
*
* @param recNo
* Nummer des Datensatzes.
* @param lockCookie
* Sperr-Identfikation.
* @throws RemoteException
* @throws RecordNotFoundException
* ungültige Datensatznummer
* @throws SecurityException
* ungültiges lockCookie oder nicht gelockt
*/
public void delete(int recNo, long lockCookie) throws RemoteException,
RecordNotFoundException, SecurityException;
/**
* ändere Datensatz (überschreibe mit dem gegebenen Datensatz).
*
* @param record
* neuer Datensatz
* @param recNo
* Nummer des gesperrten Datensatzes
* @param lockCookie
* Sperr-Identfikation.
* @throws RemoteException
* @throws RecordNotFoundException
* ungültige Datensatznummer
* @throws SecurityException
* ungültiges lockCookie oder nicht gelockt
*/
public void update(Record record, int recNo, long lockCookie)
throws RemoteException, RecordNotFoundException, SecurityException;
/**
* suche Datensätze.
*
* @param criteria
* String-Array mit Kriterien, null matcht alles, sonst
* Stringrepräsentation des Suchkriteriums (entspricht den Daten in
* Record).
* @return Array von Recordnummern
* @throws RemoteException
*/
public int[] find(String[] criteria) throws RemoteException;
/**
* lese Datensatz.
*
* @param recNo
* Nummer des zu lesenden Datensatzes.
* @return Record
* @throws RemoteException
* @throws RecordNotFoundException
* Datensatz existiert nicht.
*/
public Record read(int recNo) throws RemoteException, RecordNotFoundException;
}
Die Klasse Data muss zusätzlich einen Konstruktor
Data(String dateiname) bereitstellen, der die Datei dateiname (z.B.
"machines.db") öffnet und zur Bearbeitung mit den im Interface
DB definierten Methoden bereitstellt. Der Konstruktor muss die nötigen Exceptions werfen, wenn die Datei nicht geöffnet werden kann.
Der Parameter
String[] criteria der Methode
find() enthält das Suchkriterium, wobei jedem Feld ein String zugeordnet ist. Soll also nur nach einem Namen (siehe unten) gesucht werden, so muss
criteria[1] den gesuchten Namen (bzw. einen Teil davon) enthalten. Alle anderen Elemente des Arrays müssen dann
null sein. Soll nach mehreren Kriterien selektiert werden, so sind die entsprechenden Felder mit Werten zu belegen.
find() liefert dann ein Array mit den Indizes der passenden Datensätze.
Die Datensatznummer
recNo ist immer der Index innerhalb der Datei!
Gelöschte Datensätze werden einfach als gelöscht markiert (s.u.) und müssen beim Anlegen neuer Datensätze zuerst verwendet werden (erst wenn kein gelöschter Datensatz existiert, wird die Datei vergrößert).
RecordNotFoundException wird immer dann geworfen, wenn
recNo auf einen gelöschten Datensatz weist oder, wenn der Datensatz gar nicht existiert. Lösen Sie das Problem, dass nur mit der einfachen
RecordNotFoundException das Dateiende nicht ermittelt werden kann, in geeigneter Weise.
Record muss in geeigneter Weise implementiert werden.
Datensätze sollen gesperrt werden können (die Klasse
Data kann von mehreren parallelen Threads verwendet werden). Dazu soll die Methode
lock() eine (beliebige) dem Datensatz zuordenbare Zahl (Lockcookie bzw. Sperr-Identifikation) liefern und den Satz sperren. Die Methoden, die eine Sperre benötigen müssen dann anhand dieses Lockcookies überprüfen, ob der Datensatz gesperrt ist und die entsprechende Operation ausführen. Ist der Datensatz nicht gesperrt oder das Lockcookie ungültig, so ist eine
SecurityException zu werfen. Die Methode
unlock() hebt die Sperre wieder auf. Nach einem
delete() braucht man (kann man) kein
unlock() machen.
Datenformat
Die Strings sind kodiert mit dem Encoding
ISO-8859-1, d.h. jedes Zeichen ist einem Byte kodiert und es sind nur ASCII-Zeichen sowie die westeuropäischen Erweiterungen erlaubt. Es gibt Strings fixer Länge (werden mit Leerzeichen aufgefüllt) und Strings variabler Länge (werden wie C-Strings mit
'\0' abgeschlossen) mit einer Obergrenze.
Die Datei mit den (Test-)Daten hat folgendes Format:
Die Datei beginnt mit einem
Header:
4 Bytes Magic Cookie, Wert 4242
4 Offset in Bytes zu den eigentlichen Daten
2 Bytes Anzahl Felder pro Datensatz
Pro Feld gibt es folgenden Eintrag:
2 Bytes Länge Feldname
n Bytes Feldname
2 Bytes Länge Feldbeschreibung
n Bytes Feldbeschreibung für die Tabellenansicht
1 Byte Datentyp: 'F' ... fix, 'V' ... variabel, 'C' ... ein Zeichen
2 Bytes Feldlänge
Nach den Feldbeschreibungen, d.h. ab dem Offset, finden sich die eigentlichen Datensätze gemäß der Beschreibungen im Header:
2 Bytes Flag Gelöscht (== 0 ... gelöscht, != 0 ... aktiv)
n Bytes Daten gemäß der Datensatzbeschreibung
Testdatei machines.db
Diese Datei enthält nur die Geräte (und deren grafische Koordinaten):
| Feldname | Feldbeschreibung | (maximale) Länge | Typ | Bemerkung |
| id | Geräte-Id | 10 | String '\0' terminiert | z.B. "z23" |
| name | Gerätename | 100 | String '\0' terminiert | Gerätebeschreibung, z.B. "Laderoboter Halle B" |
| x | x | 5 | String fixe Länge | z.B. " 50" (vorne mit Leerzeichen aufgefüllt) |
| y | y | 5 | String fixe Länge | z.B. " 200" |
Testen der Klasse Data
Schreiben Sie passende Tests um die Klasse
Data zu testen. Der erste Test soll einfach die Datei
machines.db in lesbarer Form auf der Konsole ausgeben.
Die weiteren Tests sollen natürlich die gesamte Funktionalität und das Locking testen.
Downloads
Abgabe
Nennen Sie das Projekt
klasse-name-data also z.B.
5ad-meier-data und checken Sie es am CVS ein. Termin wird noch bekanntgegeben.
Wie geht's weiter
Die Datenklasse soll dann später in einem Client-/Server-System zum Einsatz kommen, wo mehrere grafische Clients eine Binärdatei bearbeiten sollen. Die Angabe dazu folgt...
Labels: Aufgabe, Java, PR5
Mittwoch, 23. Februar 2011
Schwerpunkt Programmieren (PR: 5A, 5B)
Der letztmögliche Abgabetermin für die Ausarbeitung ist der 8. April 2011, damit noch genügend Zeit bleibt, die Ausarbeitung zu besprechen.
- JavaFX
- eclipse plugins schreiben
- Funktionale Programmierung
- Logikbasierte Programmierung
- Objective C
- Algorithmen für Textsuche
- Closures - Definition, Verwendung, Beispiele (Programmiersprachen)
- Garbage Collector - Algorithmen und Implementierung
Zu allen Themen gibt es Informationen im Internet. Die Links zur Wikipedia sind nur allererste Einstiege, da dort selbst oft die Qualität in Frage gestellt wird.
Ein wichtiger Aspekt beim Schwerpunktthema ist natürlich die eigene Recherche und das Erarbeiten in ein völlig neues Gebiet.
Ausarbeitung in etwa 10 bis 20 Seiten plus Sourcecode mit Beispielen.
Weitere Literatur:
Links:
Weitere Informationen, sobald ich welche habe, gibt es dann hier.
Labels: PR5
Mittwoch, 15. Dezember 2010
Dynamische Speicherverwaltung in C (PR: 5A, 5B)
Implementieren Sie eine einfache dynamische Speicherverwaltung, die nach dem in der folgenden Skizze gezeigten Prinzip funktioniert.
Diese Speicherverwaltung soll es ermöglichen, Speicher in beliebiger Reihenfolge zu allozieren und wieder freizugeben. Siehe auch
Dynamische Speicherverwaltung
Schreiben Sie ein Modul
heap für die Speicherverwaltung selbst, welches folgende Funktionen enthält (exportiert):
void *halloc(size_t size); zum Anfordern von Speicher der Größe size
void hfree(void *p); zum Freigeben des Speichers, auf den der Zeigern p zeigt.
Erstellen Sie eine weitere Version des Moduls
heap, welches bei jedem Aufruf von
halloc() bzw.
hfree() den
gesamten Speicher in lesbarer Form auf
stderr ausgibt, damit man das Allozieren der Speicherblöcke sehen kann (die Größe des Heaps sollte dabei eher
klein gewählt werden!).
Schreiben Sie weiters ein Modul, welches Ihre Speicherverwaltung testet durch Anfordern von Speicher, belegen und lesen des allozierten Speichers und wieder Freigeben des Speichers in verschiedenen Varianten.
Erstellen Sie dazu eine Liste von Testfällen und beschreiben Sie das Ergebnis der Tests (die Speicherauszüge bekommen Sie mit obiger Variante des Moduls).
Beispiele von Testfällen:
n. Test: allozieren eines Strings der Länge
HEAPSIZE - sizeof(heap_t), füllen des Strings mit lauter
'A' und ausgeben des Strings.
OK
n+1. Test: freigeben des Speichers aus Test
n.
OK
Zu jedem Test ist anzugeben, ob der Test erfolgreich war. Falls ein Test nicht erfolgreich war, so ist festzustellen, warum und ggf. der Fehler zu beheben. Wird am Programm etwas verändert, so müssen
alle Tests noch einmal gemacht werden!
Schreiben Sie ein passendes
Makefile, mit dem man alternativ die normale Variante von
heap und die Testvariante von
heap erzeugen kann!
Organisieren Sie sich in
Kleingruppen und versuchen Sie die einzelnen Funktionen gemeinsam zu entwerfen und zu programmieren. Welche Hilfsfunktionen braucht z.B. das
halloc()?
Erweitern Sie die Speicherverwaltung um eine "Garbage Collection", die freie Speicherbereiche, die hintereinanderliegen zusammenfasst.
Erstellen Sie eine geeignete Verzeichnisstruktur und verwenden Sie
CVS.
Tauschen Sie die Module mit anderen Gruppen aus (das müßte ganz einfach sein, da die Schnittstellen immer gleich sind (s.o.).
Abgabe:
Nennen Sie das Projekt
5ad-name-heap bzw.
5bd-name-heap und checken Sie es ein.
Beantworten Sie weiters folgende Fragen (die Antworten müssen in der Datei
heap.txt gespeichert werden):
- Was passiert, wenn man einen String der Länge n alloziert und einen String der Länge n speichert?
- Was passiert, wenn man einen String der Länge n alloziert und einen String der Länge n + 1 speichert?
- Sie allozieren einen String der Länge 11 und speichern
"0123456789" und geben den String wieder frei. Danach allozieren Sie einen String der Länge 20 und geben diesen String aus. Was passiert? (die Speicheranforderungen bzw. die Freigabe muss unmittelbar hintereinander erfolgen).
- Welche Speicheranforderungen benötigen mehr Speicher - 10 Strings der Länge 100 oder 100 Strings der Länge 10? Begründen Sie die Antwort.
- Wie könnten Sie im Modul
heap sicherstellen, dass bei einem Aufruf von hfree(p); der Parameter p tatsächlich auf einen mit halloc() allozierten Speicherbereich zeigt?
heap.txt muss auch die Liste der Testfälle mit ihren Ergebnissen enthalten.
Geben Sie einen Ausdruck der ausgearbeiteten Fragen ab!
Labels: Aufgabe, C, PR5
Donnerstag, 2. Dezember 2010
Datum in C selbst gebastelt
Datumsberechnungen sind immer wieder eine Quelle großer langwieriger, fehleranfälliger Programmteile. Vor allem in Sprachen, die solche Datumsoperationen nicht unterstützen. Sie sind auch immer wieder geeignet, Programmieren zu üben. Hier die Musterlösung zu einem einfachen Kalenderprogramm in C, bei dem ein Großteil der Datumsfunktionen selbstgestrickt sind. Viel Spaß damit!
Verwendung:
Ohne Parameter gibt es einfach das aktuelle Monat mit Markierung
< 2> für den heutigen Tag und
: 8: als Markierung für österreichische Feiertage. Danach wird die Liste der Feiertage ausgegeben.
$ ./cal
Dezember 2010
Montag 6 13 20 27
Dienstag 7 14 21 28
Mittwoch 1 : 8: 15 22 29
Donnerstag < 2> 9 16 23 30
Freitag 3 10 17 :24::31:
Samstag 4 11 18 :25:
Sonntag : 5::12::19::26:
5.12. 2. Advent
8.12. Maria Empfängnis
12.12. 3. Advent
19.12. 4. Advent
24.12. Heiligabend
25.12. Christtag
26.12. Stefanitag
31.12. Silvester :-)
Here comes the Source...
/*
* File: cal.c
*
* Zweck: Kalenderprogramm
* ...für ein gegebenes Monat.
* mit/ohne argc/argv
*
* Autor: Harald Haberstroh
*
* Algorithmus:
*
* History: 2002-09-29
* 2003-10-15, Feiertage + Argumente von main()
* 2004-11-05, Liste der Feiertage
*/
/*--------------------------- includes ------------------------------*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <time.h>
/*--------------------------- defines -------------------------------*/
#define WOCHENTAG 3 /* Wochentag des 1.1.1582 */
#define MONTAG 0
#define DIENSTAG 1
#define MITTWOCH 2
#define DONNERSTAG 3
#define FREITAG 4
#define SAMSTAG 5
#define SONNTAG 6
/*--------------------------- typedefs ------------------------------*/
/*--------------------------- globals -------------------------------*/
enum {
KEIN = 0,
NEUJAHR,
HL3KOENIGE,
ASCHERMITTWOCH,
PALMSONNTAG,
KARFREITAG,
OSTERSONNTAG,
OSTERMONTAG,
STAATSFEIERTAG,
MUTTERTAG,
PFINGSTSONNTAG,
PFINGSTMONTAG,
DREIFALTIGKEITSSONNTAG,
FRONLEICHNAM,
MARIAHIMMELFAHRT,
NATIONALFEIERTAG,
REFORMATIONSTAG,
ALLERHEILIGEN,
ALLERSEELEN,
MARIAEMPFAENGNIS,
ADVENT1,
ADVENT2,
ADVENT3,
ADVENT4,
HEILIGABEND,
CHRISTIANI,
STEFANI,
SILVESTER
};
/*-------------------------- prototypes -----------------------------*/
void printFeiertage(int jahr, int monat);
int tagemonat( int jahr, int monat );
int sonntag( int jahr, int monat, int tag );
void tag2datum( int tagzahl, int *jahr, int *monat, int *tag );
void ostern( int jahr, int *monat, int *tag );
int feiertag(int jahr, int monat, int tag);
int anzProMonat(int jahr, int monat);
void printcal(int start, int anz, int heute, int monat, int jahr);
int schaltjahr( int Jahr );
int Anzahl_Tage( int Jahr, int Monat, int Tag );
/*----------------------------- main --------------------------------*/
int main( int argc, char *argv[] )
{
int tage, tag, monat, jahr, start;
struct tm *zeit;
time_t sec;
if (argc == 2 && strcmp(argv[1], "-i") == 0)
{ /* interaktiv */
printf("Jahr> ");scanf("%d", &jahr);
printf("Monat> ");scanf("%d", &monat);
printf("Tag (0 für kein bestimmter)> ");scanf("%d", &tag);
if (tag == 0)
{
tag = 1;
if (1 <= monat && monat <= 12 &&
1582 < jahr && jahr < 3000)
{
tage = Anzahl_Tage( jahr, monat, 1);
start = (tage + WOCHENTAG) % 7;
printcal(start, anzProMonat(jahr, monat), 0, monat, jahr);
return 0;
}
else
{
fprintf(stderr, "Monat zwischen 1 und 12 und Jahr zwischen "
"1583 und 2999\n");
return 2;
}
}
if (1 <= monat && monat <= 12 &&
1582 < jahr && jahr < 3000 &&
1 <= tag && tag <= anzProMonat(jahr,monat))
{
tage = Anzahl_Tage( jahr, monat, 1);
start = (tage + WOCHENTAG) % 7;
printcal(start, anzProMonat(jahr, monat), tag, monat, jahr);
return 0;
}
else
{
fprintf(stderr, "Monat zwischen 1 und 12 und Jahr zwischen "
"1583 und 2999\n"
"[Tag zwischen 1 und Anzahl für dieses Monat]\n");
return 2;
}
}
else if (argc == 3) /* Monat, Jahr */
{
monat = atoi(argv[1]);
jahr = atoi(argv[2]);
tag = 1;
if (1 <= monat && monat <= 12 &&
1582 < jahr && jahr < 3000)
{
tage = Anzahl_Tage( jahr, monat, 1);
start = (tage + WOCHENTAG) % 7;
printcal(start, anzProMonat(jahr, monat), 0, monat, jahr);
return 0;
}
else
{
fprintf(stderr, "Monat zwischen 1 und 12 und Jahr zwischen "
"1583 und 2999\n");
return 2;
}
}
else if (argc == 4) /* ganzes Datum */
{
monat = atoi(argv[2]);
jahr = atoi(argv[3]);
tag = atoi(argv[1]);
if (1 <= monat && monat <= 12 &&
1582 < jahr && jahr < 3000 &&
1 <= tag && tag <= anzProMonat(jahr,monat))
{
tage = Anzahl_Tage( jahr, monat, 1);
start = (tage + WOCHENTAG) % 7;
printcal(start, anzProMonat(jahr, monat), tag, monat, jahr);
return 0;
}
else
{
fprintf(stderr, "Monat zwischen 1 und 12 und Jahr zwischen "
"1583 und 2999\n"
"[Tag zwischen 1 und Anzahl für dieses Monat]\n");
return 2;
}
}
else if (argc == 1) /* aktuelles Monat */
{
time(&sec);
zeit = localtime(&sec);
tag = zeit->tm_mday;
monat = zeit->tm_mon + 1;
jahr = zeit->tm_year + 1900;
tage = Anzahl_Tage( jahr, monat, 1);
start = (tage + WOCHENTAG) % 7;
printcal(start, anzProMonat(jahr, monat), tag, monat, jahr);
return 0;
}
else
{
fprintf(stderr, "Aufruf:\n%s [[tag] monat jahr]\n", argv[0]);
return 1;
}
}
/*-------------------------- functions ------------------------------*/
/** Kalender ausgeben
* start ... Wochentag des 1. dieses Monats
* anz ... Anzahl der Tage dieses Monats
* heute ... Datum des heutigen Tages, 0, falls in einem anderen Monat
* monat ... Monat (1..12)
* jahr
*/
void printcal(int start, int anz, int heute, int monat, int jahr)
{
char *tage[] = { "Montag", "Dienstag", "Mittwoch", "Donnerstag",
"Freitag", "Samstag", "Sonntag" };
char *monate[] = { "Jänner", "Februar", "März", "April",
"Mai", "Juni", "Juli", "August",
"September", "Oktober", "November", "Dezember" };
int i;
int tag;
printf("%s %d\n\n", monate[monat-1], jahr);
for (i = 0; i < 7; i++)
{
printf("%10s ", tage[i]);
for (tag = i + 1 - start; tag < anz + 7 + start; tag += 7)
if (tag == heute && feiertag(jahr, monat, tag)
&& tag >= 1 && tag <= anz)
printf("[%2d]", tag);
else if (tag == heute && tag >= 1 && tag <= anz)
printf("<%2d>", tag);
else if (feiertag(jahr, monat, tag) && tag >= 1 && tag <= anz)
printf(":%2d:", tag);
else if (tag >= 1 && tag <= anz)
printf("%3d ", tag);
else
printf(" ");
printf("\n");
}
printf("\n");
printFeiertage(jahr, monat);
}
/*
* Function schaltjahr
*
* Zweck: Liefert 1, wenn angegebenes Jahr ein Schaltjahr ist
*
* Algorithmus:
* ab 1582 gilt folgende Regel für Schaltjahre:
* Ist die Jahreszahl durch 400 teilbar oder
* ist die Jahreszahl durch 4 teilbar aber nicht durch 100
* dann ist das Jahr ein Schaltjahr
*
* Parameter:
* IN: Jahr
* OUT:
*
* Return-Wert: 1 ... Schaltjahr
* 0 ... sonst
*/
int schaltjahr( int Jahr )
{
if ( Jahr % 400 == 0 )
return 1;
else if ( Jahr % 100 == 0 )
return 0;
else if ( Jahr % 4 == 0 )
return 1;
else
return 0;
}
int anzProMonat(int jahr, int monat)
{
switch(monat)
{
case 2:
return 28 + schaltjahr(jahr);
break;
case 4:
case 6:
case 9:
case 11:
return 30;
break;
default:
return 31;
}
}
/*
* Function Anzahl_Tage
*
* Zweck: Bestimmt die Anzahl der Tage ab 1.1.1582
*
* Algorithmus:
* Tag + Anzahl Tage für Monate + Anzahl Tage für Jahre
*
* Parameter:
* IN: Jahr, Monat, Tag
* OUT:
*
* Return-Wert: Anzahl der Tage ab 1.1.1582
*/
int Anzahl_Tage( int Jahr, int Monat, int Tag )
{
int j;
int m;
int tage = Tag; /* Monatstag */
/*
* Tage für Jahre berechnen
*/
for ( j = 1582; j < Jahr; j++ )
tage += 365 + schaltjahr( j );
/*
* Tage für Monate berechnen
*/
for ( m = 1; m < Monat; m++ )
switch( m )
{
case 2:
tage += 28 + schaltjahr( Jahr );
break;
case 4:
case 6:
case 9:
case 11:
tage += 30;
break;
default:
tage += 31;
}
return( tage );
}
/** Bestimmt Feiertag.
* @param jahr
* @param monat
* @param tag
* @return Nummer des Feiertags, 0 sonst
*/
int feiertag(int jahr, int monat, int tag)
{
int tagzahl, j, m, t;
ostern(jahr, &m, &t);
tagzahl = Anzahl_Tage(jahr, m, t);
if (monat == m && tag == t) /* Ostern */
return OSTERSONNTAG;
tag2datum(tagzahl + 1, &j, &m, &t);
if (monat == m && tag == t) /* Ostern */
return OSTERMONTAG;
tag2datum(tagzahl - 46, &j, &m, &t);
if (monat == m && tag == t) /* Aschermittwoch */
return ASCHERMITTWOCH;
tag2datum(tagzahl - 7, &j, &m, &t);
if (monat == m && tag == t) /* Palmsonntag */
return PALMSONNTAG;
tag2datum(tagzahl - 2, &j, &m, &t);
if (monat == m && tag == t) /* Karfreitag */
return KARFREITAG;
tag2datum(tagzahl + 49, &j, &m, &t);
if (monat == m && tag == t) /* Pfingstsonntag */
return PFINGSTSONNTAG;
tag2datum(tagzahl + 50, &j, &m, &t);
if (monat == m && tag == t) /* Pfingstmontag */
return PFINGSTMONTAG;
tag2datum(tagzahl + 56, &j, &m, &t);
if (monat == m && tag == t) /* Dreifaltigkeitssonntag */
return DREIFALTIGKEITSSONNTAG;
tag2datum(tagzahl + 60, &j, &m, &t);
if (monat == m && tag == t) /* Fronleichnam */
return FRONLEICHNAM;
if (monat == 1 && tag == 1) /* Neujahr */
return NEUJAHR;
if (monat == 1 && tag == 6) /* Heilige 3 Könige */
return HL3KOENIGE;
if (monat == 5 && tag == 1) /* Staatsfeiertag */
return STAATSFEIERTAG;
tag2datum(sonntag(jahr, 5, 13), &j, &m, &t);
if (monat == 5 && tag == t) /* Muttertag FIXME */
return MUTTERTAG;
if (monat == 8 && tag == 15) /* Mariahimmelfahrt */
return MARIAHIMMELFAHRT;
if (monat == 10 && tag == 26) /* Nationalfeiertag */
return NATIONALFEIERTAG;
if (monat == 10 && tag == 31) /* Reformationstag */
return REFORMATIONSTAG;
if (monat == 11 && tag == 1) /* Allerheiligen */
return ALLERHEILIGEN;
if (monat == 11 && tag == 2) /* Allersselen */
return ALLERSEELEN;
if (monat == 11)
{
if (sonntag(jahr, 12, 24) == Anzahl_Tage(jahr, 12, 24))
tagzahl = sonntag(jahr, 12, 24) - 21;
else
tagzahl = sonntag(jahr, 12, 24) - 28;
tag2datum(tagzahl, &j, &m, &t);
if (tag == t && m == 11) /* 1. Advent */
return ADVENT1;
}
if (monat == 12)
{
if (tag == 8) /* Maria Emfängnis */
return MARIAEMPFAENGNIS;
if (sonntag(jahr, 12, 24) == Anzahl_Tage(jahr, 12, 24))
tagzahl = sonntag(jahr, 12, 24) - 21;
else
tagzahl = sonntag(jahr, 12, 24) - 28;
tag2datum(tagzahl, &j, &m, &t);
if (tag == t && m == 12) /* 1. Advent */
return ADVENT1;
tag2datum(tagzahl+7, &j, &m, &t);
if (tag == t) /* 2. Advent */
return ADVENT2;
tag2datum(tagzahl+14, &j, &m, &t);
if (tag == t) /* 3. Advent */
return ADVENT3;
tag2datum(tagzahl+21, &j, &m, &t);
if (tag == t) /* 4. Advent */
return ADVENT4;
if (tag == 24) /* Hl. Abend */
return HEILIGABEND;
if (tag == 25) /* Christiani */
return CHRISTIANI;
if (tag == 26) /* Stefanitag */
return STEFANI;
if (tag == 31) /* Silvester :-) */
return SILVESTER;
}
return KEIN; /* Kein Feiertag */
}
void ostern( int jahr, int *monat, int *tag )
{
int a = jahr % 19;
int b = jahr / 100;
int c = jahr % 100;
int d = b / 4;
int e = b % 4;
int f = ( b + 8 ) / 25;
int g = ( b - f + 1 ) / 3;
int h = ( 19 * a + b - d - g + 15 ) % 30;
int i = c / 4;
int k = c % 4;
int l = ( 32 + 2 * e + 2 * i -h -k ) % 7;
int m = ( a + 11 * h + 22 * l ) / 451;
int p = ( h + l - 7 * m + 114 ) % 31;
*monat = ( h + l - 7 * m + 114 ) / 31;
*tag = p + 1;
}
void tag2datum( int tagzahl, int *jahr, int *monat, int *tag )
{
/*
* Jahr berechnen
*/
*jahr = 1582;
while ( tagzahl > 365 + schaltjahr( *jahr ) )
{
tagzahl -= 365 + schaltjahr( *jahr );
(*jahr)++;
}
/*
* Monat
*/
for ( *monat = 1; tagzahl > tagemonat( *jahr, *monat ); (*monat)++ )
tagzahl -= tagemonat( *jahr, *monat );
/*
* Tag
*/
*tag = tagzahl;
}
int sonntag( int jahr, int monat, int tag )
{
int tagzahl = Anzahl_Tage( jahr, monat, tag );
int wt = ( DONNERSTAG + tagzahl ) % 7;
if ( wt == SONNTAG ) /* bereits Sonntag */
return tagzahl;
else
return tagzahl + ( SONNTAG - wt );
}
int tagemonat( int jahr, int monat )
{
switch( monat )
{
case 2:
return 28 + schaltjahr( jahr );
break;
case 4:
case 6:
case 9:
case 11:
return 30;
break;
default:
return 31;
}
}
/** gib eine Liste der Feiertage aus.
* @param jahr IN
* @param monat OUT
*/
void printFeiertage(int jahr, int monat)
{
int i;
char *bez = "";
for (i = 1; i <= tagemonat(jahr, monat); i++)
{
bez = "";
switch (feiertag(jahr, monat, i))
{
case NEUJAHR:
bez = "Neujahr";
break;
case HL3KOENIGE:
bez = "Heilige Dreikönige";
break;
case ASCHERMITTWOCH:
bez = "Aschermittwoch";
break;
case PALMSONNTAG:
bez = "Palmsonntag";
break;
case KARFREITAG:
bez = "Karfreitag";
break;
case OSTERSONNTAG:
bez = "Ostersonntag";
break;
case OSTERMONTAG:
bez = "Ostermontag";
break;
case STAATSFEIERTAG:
bez = "Staatsfeiertag";
break;
case MUTTERTAG:
bez = "Muttertag";
break;
case PFINGSTSONNTAG:
bez = "Pfingstsonntag";
break;
case PFINGSTMONTAG:
bez = "Pfingstmontag";
break;
case DREIFALTIGKEITSSONNTAG:
bez = "Dreifaltigkeitssonntag";
break;
case FRONLEICHNAM:
bez = "Fronleichnam";
break;
case MARIAHIMMELFAHRT:
bez = "Maria Himmelfahrt";
break;
case NATIONALFEIERTAG:
bez = "Nationalfeiertag";
break;
case REFORMATIONSTAG:
bez = "Reformationstag";
break;
case ALLERHEILIGEN:
bez = "Allerheiligen";
break;
case ALLERSEELEN:
bez = "Allerseelen";
break;
case MARIAEMPFAENGNIS:
bez = "Maria Empfängnis";
break;
case ADVENT1:
bez = "1. Advent";
break;
case ADVENT2:
bez = "2. Advent";
break;
case ADVENT3:
bez = "3. Advent";
break;
case ADVENT4:
bez = "4. Advent";
break;
case HEILIGABEND:
bez = "Heiligabend";
break;
case CHRISTIANI:
bez = "Christtag";
break;
case STEFANI:
bez = "Stefanitag";
break;
case SILVESTER:
bez = "Silvester :-)";
break;
}
if (strlen(bez) > 0)
printf("%2d.%d. %s\n", i, monat, bez);
}
}
Labels: C, Lösung, PR5
Donnerstag, 11. November 2010
Code-Kata
Beim
Karate (z.B.
Karatedo Doshinkan) gibt es
Kata (dt. "Form", siehe auch
Kata auf Wikipedia), eine spezielle Übungsform, bei der man als Training gegen einen oder mehrere (vorgestellte) Gegner kämpft. Dabei ist der Bewegungsablauf aber stilisiert und fix vorgegeben. Das oftmalige Wiederholen der Bewegungsabläufe in Form von Kata ist unerlässlich für den persönlichen Fortschritt. Denn nur Übung macht den Meister.
Auch Programmieren beherrscht man nur durch ständiges Training. Nur durch Üben wird man auch hier zum Meister. Aber nicht nur durch den Kampf (Programmieren für das Geschäft) sondern auch durch Katas. Diese Idee für's Programmieren hat
Dave Thomas von den
Pragmatischen Programmieren (meines Wissens) erstmalig aufgegriffen und publiziert:
Code Kata
Sehr empfehlenswert für alle, die Programmieren meistern wollen.
Die Idee der Code Kata fasziniert und berührt mich besonders, da ich selber jahrelang

trainiert habe.
Labels: allgemeines, C, C#, C++, Java, POS1-1, POS1-2, PR5
Donnerstag, 28. Oktober 2010
C lernen (PR5: 5AD, 5BD)
Lernen Sie
C anhand der Vorgangsweise
15 Exercises for Learning a new Programming Language.
Machen Sie ein Projekt
5ad-name-learn-c bzw.
5bd-name-learn-c, in dem Sie die Lösungen der 15 Aufgaben dokumentieren (in erster Linie getesteter und kommentierter Source-Code).
Achtung: die erste Aufgabe ist ziemlich schwierig, da das Prüfen, ob eine Taste gedrückt wurde,
systemabhängig ist. Lösen Sie diese Aufgabe sowohl für
Windows als auch für
Linux.
Informationen:
Die Links zu jobsnake funktionieren nicht mehr. Am besten sucht man nach
Labels: Aufgabe, C, PR5
Dienstag, 26. Oktober 2010
ruby-loganalyse (PR: 5AD, 5BD)
Diverse Server (z.B. Webserver) tragen verschiedene Informationen in ein Logfile ein. Webserver loggen normalerweise jeden Zugriff auf die Website: IP-Adresse, Seite, Browser, Betriebssystem.
Bei Webservern ist es oft interessant, welche Browser oder Betriebssysteme auf der Clientseite verwendet werden.
Schreiben Sie ein ruby-Programm
loganalyse.rb, welches ein (Apache-)Logfile einliest und folgende Information ermittelt:
- Liste der Browser und deren Häufigkeiten, absteigend sortiert nach Häufigkeit, prüfen Sie dabei folgende Browser(strings) ohne Berücksichtigung der Groß-/Kleinschreibung:
"Firefox", "Opera", "Mozilla", "Netscape", "MSIE"
- Liste der Betriebssysteme und deren Häufigkeiten, absteigend sortiert nach Häufigkeit, prüfen Sie dabei folgende Betriebsysteme(strings) ohne Berücksichtigung der Groß-/Kleinschreibung:
"Linux", "Windows", "Mac"
- Liste der Subnetze (
xx.xx.xx.*) mit deren Häufigkeiten, absteigend sortiert nach Häufigkeit (Verwenden Sie zur Unterscheidung der Netze einfach die ersten drei Teile der IP-Adresse am Zeilenanfang).
- Liste der IP-Adressen mit deren Häufigkeiten, absteigend sortiert nach Häufigkeit.
- Zugriffe pro Tag mit den Häufigkeiten, sortiert nach Datum (sortieren Sie einfach die Datumstrings – z.B.
27/May/2007).
Die nötigen Informationen sind im Klartext im Logfile enthalten. Am Anfang jeder Zeile steht die IP-Adresse (z.B.
66.249.73.115). Die weiteren Informationen bekommt man durch (Sub-)Stringvergleich bzw. reguläre Ausdrücke.
Ausschnitt aus einem Logfile (
↵ bedeutet Zeilenumbruch):
66.249.73.115 - - [27/May/2007:06:50:10 +0200] "GET /r.simon/html/Unterlagen/mysql_de/mysql-2.html HTTP/1.1" 200 3358 "-" "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"↵
66.249.73.115 - - [27/May/2007:06:50:15 +0200] "GET /d03011/Art_of_War_Forum/board.php?boardid=17&sid=c8422d9b444a6ca37c37194516d35532 HTTP/1.1" 200 6297 "-" "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"↵
81.52.143.15 - - [27/May/2007:06:51:11 +0200] "GET /robots.txt HTTP/1.1" 404 379 "-" "Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.8.1) VoilaBot BETA 1.2 (http://www.voila.com/)"↵
81.52.143.15 - - [27/May/2007:06:51:12 +0200] "GET /d00050/http:/213.133.115.133/p/www.alpennic.com/zp_at.html HTTP/1.1" 403 550 "-" "Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.8.1) VoilaBot BETA 1.2 (http://www.voila.com/)"↵
66.249.73.115 - - [27/May/2007:06:52:02 +0200] "GET /d03011/Art_of_War_Forum/thread.php?postid=227 HTTP/1.1" 200 20545 "-" "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"↵
193.170.149.156 - - [27/May/2007:06:52:24 +0200] "GET / HTTP/1.0" 200 209 "-" "check_http/1.99 (nagios-plugins 1.4.6)"↵
Laden Sie sich eine Beispieldatei von
http://edvowww.htlwrn.ac.at/HP/blosxom/C/beispiele/access.log herunter (Achtung 14MB).
Schreiben Sie eine Klasse
LogAnalyse mit einem
Konstruktor, dem der Dateiname übergeben wird. In der Klasse muss eine Methode implementiert werden, welche obige Aufgabenstellung löst.
Im Hauptprogramm muss ein
Objekt der Klasse angelegt werden und diese Methode aufgerufen werden. Der
Dateiname kommt aus der
Kommandozeile.
Hinweise:
Verwenden Sie einen Hash zum Speichern der Zählinformation.
Verwenden Sie die Sortiermethode
sort von Arrays, um die entsprechende Liste zu erzeugen.
Abgabe am Ende der zweiten Übungsstunde:
Nennen Sie das Projekt
5xd-name-ruby-logfile-i
Dabei gilt:
- x ... a für 5A, b für 5B
- name ... Ihr Familienname
- i ... Information 1 bis 5, wobei jede Gruppe nur eine Information nach folgender Regel bearbeiten muss:
- 5BD Gruppe 1
- 5BD Gruppe 2
- 5AD Gruppe 1
- 5AD Gruppe 2
- alle, die beim Übungstermin gefehlt haben
Geben Sie auch das
Ergebnis als Textdatei innerhalb des Projekts ab:
ergebnis.txt. Das Projekt enthält also 2 Dateien:
loganalyse.rb
ergebnis.txt
Labels: Aufgabe, PR5, ruby
Freitag, 15. Oktober 2010
Listings (Source-Code drucken)
Sollten Sie Source Code in gedruckter Form abliefern (müssen), dann sollte ein Listing etwa so aussehen:
Das Listing sollte folgende Eigenschaften aufweisen:
- Autor und Dateiname ist sofort ersichtlich
- Seitennummern werden gedruckt
- Syntax-Hilighting: Schlüsselworte, Kommentare usw. sollten unterschiedlich formatiert sein
- Querformat mit zwei Spalten, um Papier zu sparen (Zwei Seiten auf eine drucken)
- Es muss ein "fixed font" (z.B. Courier) verwendet werden, d.h. jedes Zeichen hat die selbe Breite (ausgenommen String-Konstante)
- Optional sollen Zeilennummern gedruckt werden (siehe Beispiele unten).
Beispiel mit Zeilennummern:
Beispiel mit Zeilennummern, nur jede 5. Zeile bekommt eine Nummer:
Unter Linux können Sie den Befehl a2ps (ASCII to PostScript, 2 = two = to, daher a2ps) verwenden.
Beispiele:
Die Datei dreieck.py wird auf dem Drucker hpg1 ohne Zeilennummern gedruckt.
a2ps --line-numbers=1 -Phpg2 dreieck.py newprog.py
Die Dateien dreieck.py und newprog.py werden am Drucker hpg2 mit Zeilennummern gedruckt.
a2ps --line-numbers=5 -Phpg3 dreieck.py newprog.py
Die Dateien dreieck.py und newprog.py werden am Drucker hpg3 mit Zeilennummern gedruckt, wobei nur jede fünfte Zeilennummer gedruckt wird.
Weitere Optionen zu a2ps finden Sie in der man-Page (man a2ps).
Die meisten IDEs (z.B. eclipse oder netbeans) und viele Editoren bieten ähnliche Möglichkeiten, jedoch passt bei a2ps der Ausdruck praktisch immer (abgesehen davon, dass a2ps nur ASCII-versteht und damit Probleme mit Umlauten hat, aber damit kann man leben).
Labels: allgemeines, POS1-1, POS1-2, PR5
Montag, 4. Oktober 2010
Ruby lernen (PR: 5AD, 5BD)
Lernen Sie
Ruby anhand der Vorgangsweise
15 Exercises for Learning a new Programming Language.
Machen Sie ein Projekt
5ad-name-learn-ruby bzw.
5bd-name-learn-ruby, in dem Sie die Lösungen der 15 Aufgaben dokumentieren (in erster Linie getesteter und kommentierter Source-Code).
Achtung: die erste Aufgabe ist ziemlich schwierig, da das Prüfen, ob eine Taste gedrückt wurde,
systemabhängig ist. Lösen Sie diese Aufgabe sowohl für
Windows als auch für
Linux.
Informationen:
Labels: Aufgabe, PR5, ruby
Donnerstag, 9. September 2010
Entwicklungsumgebung für veschiedene Programmiersprachen einrichten (PR: 5AD, 5BD)
Richten Sie sich eine Entwicklungsumgebung ein, mit der Sie Java, C, C++, Ruby, Groovy und Python programmieren können. Weiters sollte es möglich sein, Projekte in einem CVS-Repository abzulegen.
Der Schwerpunkt - auch im Hinblick auf die Reife- und Diplomprüfung - liegt auf Java, aber im Laufe des Jahres werden die anderen Sprachen verwenden.
Für Ihre Projekte (Programmbeispiele) sollten Sie sowieso eine Versionsverwaltung einsetzen. In der Schule haben wir einen CVS-Server eingerichtet. Auf diesem System müssen Sie außerdem Ihre Beispiele abgeben.
Ich empfehle
eclipse oder
netbeans als Entwicklungsumgebung (wobei ich mir nicht sicher bin, ob netbeans auch ein Groovy-Plugin hat).
Falls Sie noch keinen Zugang zum CVS-Server haben, melden Sie sich bei mir.
"Abgabe": erstellen Sie folgende Projekte und checken Sie diese auf dem CVS-Server ein.
- 5ad-name-java-helloworld
- 5ad-name-c-helloworld
- 5ad-name-cpp-helloworld
- 5ad-name-ruby-helloworld
- 5ad-name-groovy-helloworld
- 5ad-name-python-helloworld
name ersetzen Sie bitte durch Ihren Familiennamen. "5ad" ist für die 5B natürlich durch "5bd" zu ersetzen. Die Projekte sollen jeweils "Hello World"-Programme in der gegebenen Programmiersprache enthalten.
Termin: Jeweils Beginn der 2. Übungseinheit.
Labels: Aufgabe, PR5, Programmiersprachen
Donnerstag, 3. Juni 2010
IDEs und Projektverzeichnisse und wie Maturanten damit umgehen
Ich versuche gerade, die Programmierarbeiten der Projektwoche der Reife- und Diplomprüfung zu korrigieren. Die Maturanten haben mich da vor ziemlich großes Problem gestellt, denn ich muss von 17 Abgegebenen Projekten
- bei einem einen Java-Decompiler verwenden, damit ich überhaupt praktisch mit dem Programm arbeiten und mir Fehler genauer betrachten kann, denn dieser Kandidat hat nur die
*.class-Dateien ins Abgabe-Archiv gepackt (Eclipse-Projekt).
- bei 6 (sechs) ich die
*.java-Dateien mühsam wieder in die richtige Paket-Struktur bringen, teilweise nur durch händisches Anlegen der Klassen und dann Kopieren der entsprechenden Textstellen aus der einen Textdatei, in der alle Klassen zusammengefasst waren (alles Netbeans-Projekte).
- bei den restlichen 10 konnte ich das Projektverzeichnis unmittelbar verwenden (4 Eclipse-, 5 Netbeans-Projekte bzw. ganz ohne Meta-Information der IDE).
Zur Erläuterung habe ich ein paar Screenshots gemacht. Das erste zeigt die Paket-Struktur eines richtig abgegebenen Netbeans-Projekt:

Das nächste Bild zeigt ein falsch abgegebenes Projekt. Es gibt in den Paket-Verzeichnissen keine *.java-Dateien!
Die
*.java-Dateien waren im übergeordneten Verzeichnis zu finden. Die Verzeichnisse mit den Sourcen beginnen alle mit einem Leerzeichen und müssen erst wieder umbenannt werden:
Das Verzeichnis, das mit "
-brz" endet, enthält das Projekt, aber eben ohne die Sourcen.
Ich denke, man muss schon ziemlich Hand anlegen, um aus den Projektverzeichnissen diese kaputten Projekte zu erzeugen.
Ich frage mich, was so kompliziert ist, wenn es heißt: "
Das gesamte Projekt-Verzeichnis (Name-brz) in eine Jar-Datei (Name-brz.jar) packen und ins Abgabeverzeichnis kopieren (Name-brz.jar enthält also u.a. die „ausführbare“ Datei brz.jar)."
Die Angabe für das Projekt war so:
"
Verwenden Sie als Standard-Encoding UTF-8 (Unicode)!
Nennen Sie das Projekt Name-brz, wobei Name Ihr Familienname ist.
Erstellen Sie eine geeignete Paket-Struktur.
Erstellen Sie die Verzeichnisse build und doc. Im Verzeichnis build muss das fertige Programm brz.jar abgelegt werden. In doc muss die generierte JavaDoc abgelegt werden. Legen Sie im Projektverzeichnis eine Datei readme.txt an, die Hinweise zum Erzeugen von brz.jar enthält.
brz.jar soll alle nötigen Informationen zum Betrieb mit Ausnahme der Datenbankdateien enthalten, d.h. brz.jar soll in jedem beliebigen Verzeichnis aufgerufen werden können.
Programmaufruf: über ein Argument der Kommandozeile soll festgelegt werden, ob das Programm als Server (Argument server), Client (Argument client) oder AdminClient (Argument admin) läuft.
Erstellen Sie ein Ant-Script, mit dem die Applikation und die Dokumentation (neu) erstellt werden kann."
Es durfte frei zwischen Eclipse und Netbeans gewählt werden.
Die Leute haben 5 oder mehr Jahre Programmieren hinter sich. Und immer wieder Tests in dieser Arbeitsumgebung (Java unter Linux). Ich verstehe das nicht!
Diese Umstände kosteten bis jetzt ein paar Stunden Arbeit (überhaupt, wenn man diesen Blog-Eintrag mitrechnet). Wirklich korrigiert habe ich noch keine der Arbeiten.
Labels: allgemeines, eclipse, Fehler, netbeans, PR5

Abonnieren Posts [Atom]