Probleme bei einer Hausarbeit

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Antworten
Benutzeravatar
monkey
Beiträge: 182
Registriert: 29.06.2006 09:00:51
Wohnort: Buxtehude

Probleme bei einer Hausarbeit

Beitrag von monkey » 25.12.2008 19:06:02

Hallo zusammen,
unser Prof. hat uns paar Aufgaben aufgegeben und zwar müssen wir zwei Programme schreiben. Die Programme sollen in eine Datei schreiben können oder aus einer Datei lesen können. Bei dem ersten Programm sollen diese Funktionen in der Main erledigt werden und bei dem zweiten sollen dieses Funktionen in Funktionen ausgelagert werden. Die beiden Programme habe ich hin bekommen und sie lassen sich ohne Probleme kompilieren. Nur mein Problem ist das wenn ich innerhalb der beiden Programme versuche die Eingabe zu machen, das manche Eingaben übersprungen werden. So kann ich nach dem ich meinen Namen eingegeben habe nicht den Status eingeben, weil der Punkt übersprungen wird. Vielleicht könnt ihr mir weiter helfen bin langsam mit meinem Latein am Ende.

http://nopaste.debianforum.de/17718

http://nopaste.debianforum.de/17717

Benutzeravatar
Mike1985
Beiträge: 148
Registriert: 01.05.2005 14:38:11
Wohnort: Lienz / Austria
Kontaktdaten:

Re: Probleme bei einer Hausarbeit

Beitrag von Mike1985 » 25.12.2008 19:45:02

Dieser Link [1] beschreibt genau dein Problem und liefert auch gleich einige Lösungsansätze.
mfg m

[1] http://openbook.galileocomputing.de/c_v ... 05_001.htm

Hans-Martin
Beiträge: 141
Registriert: 06.12.2007 18:03:03
Lizenz eigener Beiträge: MIT Lizenz
Wohnort: Kehl

Re: Probleme bei einer Hausarbeit

Beitrag von Hans-Martin » 28.12.2008 19:55:49

Hi monkey,

etwas verkürzt würde ich sagen: Das Problem liegt in der Funktion scanf().

In der bei mir vorhandenen Literatur, die bis in die späten 80er Jahre zurückreicht, habe ich dazu nie etwas Kritisches gefunden.

Wir leben bekanntlich in Europa. Wenn wir eine Zahl mit Dezimalstellen schreiben wollen, verwenden wir somit ein Komma. Versuche das doch einmal mit scanf(): Entweder verwendet der Anwender einen Punkt, oder ...

Die erste Überlegung sollte sein: Welche Arten von Daten sollen eingegeben werden. Wir unterscheiden

Alphanumerische Zeichenfolgen
ganze Zahlen (wieso hast Du im zweiten Programmbeispiel für das Geburtsdatum einen int-Wert definiert?)
Gleitkommazahlen.

Abhängig davon bieten sich unterschiedliche Verarbeitungsroutinen für die erfassten Zeichenfolgen an. Bei Texteingaben ist gets() einfach und sicher zu handhaben.

Es sollte aber immer geprüft werden, ob jemand eventuell mehr Zeichen eingegeben hat, als z.B. in der aufnehmenden char-Variable vorgesehen ist. Du prüfst z.B. die Eingabelänge des Namens nicht nach und beschränkst sie auch nicht. 20 Zeichen können reichen, müssen es aber nicht. Und dann: Fehler!

Eine Möglichkeit der Prüfung ist die Anlage eines längeren Strings, die Prüfung der Eingabelänge und dann das Kopieren von maximal 19 Zeichen in den Teil der struct, die den Namen aufnehmen muss (das 20. Byte enthält die beendende Null).

Da Du als Programmierer weißt, welche Art von Eingabe vom Benutzer erwartet wird, kannst Du z.B. einen String überprüfen und, wenn er formal die erwarteten Zeichen hat, in int oder double-Werte konvertieren. Im letzten Fall kann damit z.B. sowohl ein Punkt als auch ein Komma als Dezimalzeichen interpretiert werden, wobei das Komma durch eine Programmroutine vor der Konvertierung in einen Punkt gewandelt sein muss.

Ich selbst verwende ncurses. Dabei kann der Cursor positioniert werden, Du kannst unterschiedliche Fenster definieren, die parallel im Speicher liegen, und Farben einsetzen.

Über die Abfrage des Wertes von Eingaben der Pfeiltasten etc. kannst Du Erfassungsroutinen schreiben, bei denen nach Feststellung eines Schreibfehlers wieder in die vorausgegangene Abfrage eingestiegen werden kann (und mehr).

Ich weiß natürlich nicht, auf welchem Niveau Dein Professor die Aufgabe gelöst sehen möchte. Es sollte aber nie vergessen werden, dass das schwächste Glied in der Kette häufig der Mensch ist, der am Computer sitzt und das Programm benützt.

Gruß

Hans-Martin

Benutzeravatar
Meillo
Moderator
Beiträge: 9267
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: Probleme bei einer Hausarbeit

Beitrag von Meillo » 29.12.2008 09:39:10

Hans-Martin hat geschrieben:Bei Texteingaben ist gets() einfach und sicher zu handhaben.
Einfach vielleicht, aber keinesfalls sicher: Immer fgets() stattdessen verwenden!
Es sollte aber immer geprüft werden, ob jemand eventuell mehr Zeichen eingegeben hat, als z.B. in der aufnehmenden char-Variable vorgesehen ist. Du prüfst z.B. die Eingabelänge des Namens nicht nach und beschränkst sie auch nicht. 20 Zeichen können reichen, müssen es aber nicht. Und dann: Fehler!
Eben das ist bei fgets() möglich.
sowohl ein Punkt als auch ein Komma als Dezimalzeichen interpretiert werden, wobei das Komma durch eine Programmroutine vor der Konvertierung in einen Punkt gewandelt sein muss.
dazu kannst du ja einfach den Sting abschreiten und alle Kommas in Punkte umwandeln. Danach in eine Zahl umwandeln lassen.
Ich selbst verwende ncurses. Dabei kann der Cursor positioniert werden, Du kannst unterschiedliche Fenster definieren, die parallel im Speicher liegen, und Farben einsetzen.
Üblicherweise ist das deutlich zu kompliziert für Übungsaufgaben die anderen Zwecken dienen. Wenn man schon programmieren kann, dann ist es etwas anderes.
Ich weiß natürlich nicht, auf welchem Niveau Dein Professor die Aufgabe gelöst sehen möchte. Es sollte aber nie vergessen werden, dass das schwächste Glied in der Kette häufig der Mensch ist, der am Computer sitzt und das Programm benützt.
Ich denke dass man in den Einstiegsaufgaben davon ausgehen darf, dass korrekte Eingaben gemacht werden. So war es zumindest in meinem Studium. Aber das sollte der Prof. eigentlich gesagt haben.

Somit schlage ich vor: Schau dass das Programm funkioniert, wenn immer korrekte Eingaben gemacht werden. Danach, wenn du dann noch Zeit hast, kannst du dich um Robustheit und Sicherheit kümmern.
Use ed once in a while!

Benutzeravatar
monkey
Beiträge: 182
Registriert: 29.06.2006 09:00:51
Wohnort: Buxtehude

Re: Probleme bei einer Hausarbeit

Beitrag von monkey » 29.12.2008 11:23:17

Danke erst mal für die ganzen Beiträge. Ich hatte mir das schon gedacht das scanf() Probleme hat unter Linux den Puffer zu leeren. Da ich mir paar Übungen von meinen Kommilitonen angeschaut habe und die hatten es auch nicht anders gemacht. Nur die hatten es auf ihren Windows Laptops ausgeführt und da gab es anscheinend keine Probleme. Als ich unseren Betreuer gefragt hatte (nicht der Prof) meinte der, das wäre eigentlich auch richtig wie ich das gemacht hätte. Aber mir sagen konnte er auch nicht, weshalb manche Eingaben übersprungen werden.
@Hans-Martin grundsätzlich hast du recht man sollte immer überprüfen was der Benutzer eingegeben hat, das predigt unser Prof. auch ständig. Aber bei den Übungsaufgaben soll es eigentlich nur drum gehen das wir verstehen was ein Stream ist und mit Datei umgehen können. Ich denke mal wenn ich die Tage noch Zeit habe werde ich versuchen deine Vorschläge umzusetzen.

PS Könnt ihr mir ein Tipp geben wie ich in das Programm eine verkettete Liste integriere. Stehe da momentan noch etwas auf dem Schlauch. Bin für jeden Tipp dankbar.

mfg monkey

Hans-Martin
Beiträge: 141
Registriert: 06.12.2007 18:03:03
Lizenz eigener Beiträge: MIT Lizenz
Wohnort: Kehl

Re: Probleme bei einer Hausarbeit

Beitrag von Hans-Martin » 29.12.2008 15:00:43

Hi monkey,

hilft Dir auf Deine komplex zu beantwortende Frage ein Literaturhinweis?

Vielleicht kannst Du das im Verlag Markt + Technik erschienene Buch "C-Programmierung für Linux" auftreiben, Verfasser des englischen Originals sind Erik de Castro Lopo, Peter Aitken, Bradley L. Jones. Dort findet sich eine mehrseitige Erläuterung samt Programmbeispiel.

Extrem verkürzt, baust Du einen Zeiger auf eine struct Kartei in die von Dir definierte struct Kartei ein:

struct Kartei
{
char Name[20];
char Status;
int Semester;
int Geburtsdatum;
struct Kartei *zeiger_auf_das_naechste_element;
};

Was ncurses angeht: Manchmal ist das zunächst komplizierter Scheinende das Einfachere.

Schaue Dir z.B. das manual zu getch an (rein vorsorglich: Aufruf man getch - Voraussetzung Du hast die manuals komplett installiert). Mit den Rückgabewerten für Funktionstasten lassen sich ganz einfach Wünsche des Benutzers nach der Fortführung der Eingabe oder nach sonst irgend etwas abfragen (ganz genau genommen: in Bezug auf Eingaben des Standardfensters, aber das ist als default unproblematisch).

Was Meillo zu fgets() schreibt, ist zwar grundsätzlich richtig. Einen längeren String für die Eingabe zu verwenden, wie ich es vorschlage, hat den Vorteil, dass der Benutzer nicht plötzlich gestoppt wird, sondern Du als Programmierer prüfen kannst, wie viel Zeichen er mehr eingegeben hat als im char[] angelegt sind. Du kannst eine Routine zur Abkürzung schaffen. Was die Wandlung von Kommas in Punkte angeht, wie sie Meillo vorschlägt, ist es natürlich nötig, darauf zu achten, dass nur das als Dezimalstelle gedachte Komma zum Punkt wird und Leerstellen und evtl. versehentlich geschriebene andere Zeichen eliminiert werden.

Alle Kommas in Punkte zu wandeln führt zu einem Fehler.

Gruß

Hans-Martin

Benutzeravatar
Meillo
Moderator
Beiträge: 9267
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: Probleme bei einer Hausarbeit

Beitrag von Meillo » 29.12.2008 16:28:09

Hans-Martin hat geschrieben:Was Meillo zu fgets() schreibt, ist zwar grundsätzlich richtig. Einen längeren String für die Eingabe zu verwenden, wie ich es vorschlage, hat den Vorteil, dass der Benutzer nicht plötzlich gestoppt wird, sondern Du als Programmierer prüfen kannst, wie viel Zeichen er mehr eingegeben hat als im char[] angelegt sind. Du kannst eine Routine zur Abkürzung schaffen.
Was aber ist ein ``längerer Sting''? 128Byte? 1024Byte? Es wird immer einen User geben, der noch mehr Zeichen eingibt. Und dann hast du deinen Pufferüberlauf, und kannst nichts dagegen tun!

Deshalb nochmal: gets() ist _grundsätzlich_ zu vermeiden! fgets() kann gets() vollständig ersetzen und ist sicher.

Was die Wandlung von Kommas in Punkte angeht, wie sie Meillo vorschlägt, ist es natürlich nötig, darauf zu achten, dass nur das als Dezimalstelle gedachte Komma zum Punkt wird und Leerstellen und evtl. versehentlich geschriebene andere Zeichen eliminiert werden.

Alle Kommas in Punkte zu wandeln führt zu einem Fehler.
Das sehe ich anders. Deshalb habe ich aus bewusst geschrieben: alle Kommas in Punkte umzuwandeln.

Denn ein selektives Umwandeln benötigt eine Logik (Intelligenz) die weiß welches Komma umgewandelt werden soll und welches nicht. Und wenn du Leerzeichen und sonstige Störzeichen eliminieren willst, dann brauchst du noch mehr Logik. Wie handelst du bei ``1234 5.67'' um und wie bei ``123.4 567'' und wie bei ``1234.56 7'' oder gar bei ``12.3,4.5,67''? Soviel Logik die benötigt ist um derartige Fragen zufriedenstellend zu lösen, führt unweigerlich zu Komplexität und wird das Programm aufblähen. (Andere Stellen mit ähnlichen Problemstellungen werden folgen und weitere Komplexität hinzufügen.)

Ich würde einfach strtol() (eine sicherere Alternative zu atoi() und atol() ) die Arbeit tun lassen. Soll die Funktion doch entscheiden welche Zahlen sie findet. Und wenn sie keine findet, dann soll der User sie erneut und korrekt eingeben.

Das einzige was ich wollte, ist es auf simple Weise Zahlen mit Dezimal-Komma zu akzeptieren. Dafür benötigt es nur wenige Zeilen:

Code: Alles auswählen

int i;

/* convert ',' to '.' to support german number format */
for (i = 0; i < sizeof(buf); i++) {
  if (buf[i] == ',') {
    buf[i] = '.';
  }
}
(unerheblich ob dies die beste Implementierung ist; jeder weiß sofort was hier passiert)

Empfehlenswert dazu ist: ``The Practice of Programming'' von Kernighan und Pike, die zu Recht predigen: Simplicity, Clarity, Generality!


EDIT:
Vielleicht hilft auch schon alleinig das Umstellen der Locales auf deutsch. Könnte sein, dass strtol() dann das Komma als Dezimaltrenner erkennt ... aber keine Ahnung ob das wirklich geht, oder gar portabel ist.
Use ed once in a while!

Benutzeravatar
GoKi
Beiträge: 2068
Registriert: 04.07.2003 23:08:56
Lizenz eigener Beiträge: MIT Lizenz

Re: Probleme bei einer Hausarbeit

Beitrag von GoKi » 29.12.2008 16:54:07

Meillo hat geschrieben:EDIT:
Vielleicht hilft auch schon alleinig das Umstellen der Locales auf deutsch. Könnte sein, dass strtol() dann das Komma als Dezimaltrenner erkennt ... aber keine Ahnung ob das wirklich geht, oder gar portabel ist.

Code: Alles auswählen

#include <stdio.h>
#include <stdlib.h>
#include <locale.h>

int main()
{
  char* end;
  double d;

  char input[] = "1,2345 Beispiel";
  d = strtod(input, &end);
  printf("number %lf parsed up to %s\n", d, end);

  setlocale(LC_ALL, ""); // set default locale
  d = strtod(input, &end);
  printf("number %lf parsed up to %s\n", d, end);
}
Kompilieren und dann mal mit deutscher Umgebung ausführen.

Code: Alles auswählen

export LANG=de_DE.UTF-8
Aber es geht hier um eine Übungsaufgabe....

BTW: Auch scanf kann man mitteilen, dass es nur x Zeichen in den char* kopieren darf.
MfG GoKi
:wq

Benutzeravatar
Meillo
Moderator
Beiträge: 9267
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: Probleme bei einer Hausarbeit

Beitrag von Meillo » 29.12.2008 20:05:13

GoKi hat geschrieben:Kompilieren und dann mal mit deutscher Umgebung ausführen.
thx ... so habe ich auch wieder was gelernt (nämlich wie man locales in C-Programmen verwendet ;-) )

Und für alle die zu faul sind selbst zu kompilieren; hier die Ausgabe:

Code: Alles auswählen

$ LANG=de_DE.UTF-8 ./a.out 
number 1.000000 parsed up to ,2345 Beispiel
number 1,234500 parsed up to  Beispiel
Use ed once in a while!

Antworten