Clever Informationen aus String einlesen
-
Hallo Forum,
zur Zeit suche ich möglichst effektive Arten und Weise, aus einem gegebenen char-Array Informationen auszulesen. In besagtem Array stehen die RFC.822 Header Informationen zu einer Mail, aus welchen ich Dinge wie Absender, Datum etc. auslesen möchte. Derzeit sieht meine vorgehensweise wie folgt aus:
char *from; char buffer[BUF]; buffer = infos_einlesen(); from = strstr(buffer, "From: "); from += strlen("From: "); from = strsep(from, "\r"); // bis zum Zeilenende lesen
Diese Methode funktioniert bisher wie sie soll, scheint mir aber aufgrund der benötigten Variablenmenge etwas uneffektiv. Abgesehen von eher unschöner Syntax and whatnot.
Lösungen wie sscanf scheinen bei diesem Problem von vornherein ausgeschlossen. Daher würde ich gerne wissen, wie eine optimiertere, einfache oder wie auch immer verbesserte Lösung aussehen könnte.
Gruß!
EDIT: ich meinte strsep anstatt strtok. Nun im Beispiel angepasst.
-
Erst einmal fällt auf, dass du selber RFC822 falsch umsetzt. Ein "field" geht nicht bis zum Zeilenende und ein Zeilenende ist auch kein einfaches '\r'. Die genaue Definition ist:
RFC822 schrieb:
field = field-name ":" [ field-body ] CRLF field-name = 1*<any CHAR, excluding CTLs, SPACE, and ":"> field-body = field-body-contents [CRLF LWSP-char field-body] field-body-contents = <the ASCII characters making up the field-body, as defined in the following sections, and consisting of combinations of atom, quoted-string, and specials tokens, or else consisting of texts>
Zu einem gegebenen Feldnamen musst du also den nächsten Doppelpunkt (oder Ende des Headers) finden. Die Zeile mit dem nächsten Doppelpunkt zählt nicht mehr zum Feldinhalt. Prinzipiell kann ein Feld mehrmals vorkommen, typisches Beispiel die "Received"-Felder. Wir benötigen noch eine Definiton des Zeichensatzes:
RFC-822, gekürzt schrieb:
; ( Octal, Decimal.) CHAR = <any ASCII character> ; ( 0-177, 0.-127.) CTL = <any ASCII control ; ( 0- 37, 0.- 31.) character and DEL> ; ( 177, 127.) CR = <ASCII CR, carriage return> ; ( 15, 13.) LF = <ASCII LF, linefeed> ; ( 12, 10.) SPACE = <ASCII SP, space> ; ( 40, 32.) HTAB = <ASCII HT, horizontal-tab> ; ( 11, 9.) CRLF = CR LF LWSP-char = SPACE / HTAB ; semantics = SPACE
Du siehst, die Regeln sind nicht ganz so einfach. Wenn du auch noch kontrollieren möchtest, ob die Regeln eingehalten wurden, dann wird es ganz schön kompliziert. Wesentlich komplizierter als deine jetzige Lösung. Da gibt es auch keinen Trick, manchmal muss man da eben einfach durch. Wie ich vorgehen würde (Pseudocode):
Gegeben: kompletter Header Gegeben: Gesuchtes Feld "feld" Suche in Header nach "feld:" Falls gefunden: Suche nach nach nächstem ':' Falls gefunden: Suche nach vorhergehendem CRLF Falls gefunden und hinter "feld:": Alles zwischen "feld:" und CRLF ist Feldinhalt Fertig Andernfalls: Header ist ungültig. Andernfalls: Alles zwischen "feld:" und Ende ist Feldinhalt Fertig Andernfalls: Feld existiert nicht Fertig
Kannst du ja mal umsetzen. Ist sicherlich nicht sooo schwierig, wie es auf den ersten Blick aussieht. Aber schön elegant wird es nicht.
Oder: Du suchst mal im Netz. Du bist schließlich nicht der erste, der so etwas machen möchte. Du wirst sehen, es gibt da schon fertige Bibliotheken dafür. Und nicht bloß für einfache Header in einfachen Emails, sondern gleich für den kompletten MIME-Standard. RFC-822 ist nämlich nicht mehr wirklich aktuell.
-
Vielen Dank für den Hinweis zum Protokollverständnis. Das ist mein erstes Mal, dass ich mich mit der konkreten Implementierung eines Protokolls auseinandersetze und da habe ich an solche Dinge erst einmal nicht im Geringsten gedacht. Ich schätze jedoch, dass es für mein derzeitiges Ausprobieren eventuell zu weit führt. Die Gründlichkeit sollte aber dennoch in Erinnerung gehalten werden.
Mittlerweile merke ich, dass ich ohnehin großzügig mit den Header Informationen verfahren muss. Daher werde ich für's erste bei meiner nicht ganz Protokoll konsistenten Lösung bleiben. Sollten andere Lösungen (konkret bezogen auf die syntaktische Fragestellung) dennoch auftauchen, freue ich mich über jeden Hinweis!
-
per schrieb:
Sollten andere Lösungen (konkret bezogen auf die syntaktische Fragestellung) dennoch auftauchen, freue ich mich über jeden Hinweis!
Dir ist klar, dass strsep (und üblicherweise auch strtok) den Ausgangsstring verändern? Falls das nicht gewünscht ist, nimm sscanf, um von deiner Startposition an bis zum nächsten '\r' (oder Ende) zu lesen. Eine volle Protokollimplementierung mit sscanf ist wohl wirklich unmöglich, aber wenn du bloß die Zeile bis zum Ende lesen möchtest, ist es ein sehr gutes Mittel.
-
Ja, das war mir bewusst. Da ich annahm, dass jeder Mailserver eine einheitliche Reihenfolge der einzelnen Felder bedient, wäre dies auch nicht weiter schlimm gewesen. Doch dem ist leider nicht so, weshalb ich am ehesten auf die sscanf-Methode (Art und Weise, that is) zurückgreifen würde. Doch leider bekomme ich bei folgendem Vorgang immer nur einen NULL Pointer zurückgeliefert.
struct mail { char from[100]; }; //Beispielstruct char header[BUF]; char *from; struct mail *m = (struct mail *) malloc (sizeof(struct mail)); lies_info_ein(header, BUF); if (from = strstr(header, "From:")) sscanf(from, "From: %s\r\n", m->from);
Nun habe ich noch nicht sehr viel Routine im Umgang mit diesen Dingen und da werde ich sicher die ein oder andere Sache übersehen haben, aber im Grunde dürfte dem angesichts der Signatur von sscanf nichts entgegen stehen ... (?) Ansonsten bitte ich um Zurechtweisung!
Gruß, Per
-
per schrieb:
header = lies_info_ein();
so bekommst du die daten nicht in dein array.
eher soint lies_info_ein ( char header[], size_t header_size ) { // hier maximal header_size bytes im char-array header speichern }
-
Da hätte ich präziser sein sollen und die angesprochene Stelle als Pseudocode kennzeichnen müssen. Ich werde dies korrigieren.
-
hast du geprüft ob in deinem array auch "From:" enthalten ist?
-
Fehlerprüfung wird gemacht, bzw. Abfrage auf Rückgabewert von strstr(). Entschuldigt die ungenügende Ausdrucksweise. Werde das Beispiel anpassen.
-
das kann immer noch krachen, wenn
- malloc NULL liefert
- per sscanf >= 100 zeichen konvertiert werden hat.
-
Nutze Funktionen für deine Aufgabenstellung, das erhöht die Übersichtlichkeit und das Debugging, beugt dem Codekopieren vor, usw.
const char *lies(const char *s,const char *t,char *b) { char *p=strstr(s,t); if(p) sscanf(p,"%*s%999s",b),p=b; return p; } int main() { char *p,b[1000]; if( p=lies("...From: bla ... To: fasel ...","From:",b) ) puts(p); if( p=lies("...From: bla ... To: fasel ...","To:",b) ) puts(p); if( p=lies("...From: bla ... To: fasel ...","Sender:",b) ) puts(p); return 0; }