Ich habe einen C-String, der mit irgendeinem Text befüllt ist.
In diesen will ich formatiert hineinschreiben (an eine beliebige
Stelle).
D.h. ich würde sprintf oder Konsorten dafür benutzen.
Leider wird mir ja von den printf Funktionen immer die '\0' angehängt.
D.h. mein ursprünglicher String wird beschnitten.
Einzige Möglichkeit die mir einfällt ist einen temporären Puffer
anzulegen, dort reinzuschreiben und dann mittels strncpy das Ergebnis
kopieren.
Speicher sowie CPU-Load sind noch nicht das Problem, könnt das also
schon so machen.
Wundere mich nur, ob es für diese Problemstellung keine elegantere
Lösung gibt.
Oder gibts da doch was ohne Umwege?
Grüße
Hallo Michl,
ganz verstehe ich Deine Frage nicht. Du willst mitten in einen String
einen anderen String kopieren, ohne daß die angehängte Null mitkopiert
wird und memcpy gefällt Dir nicht?
Oder willst Du noch irgendwie formatieren?
Oder willst Du einen String in einen anderen einfügen à la snprintf?
Dann wird die Null aus dem Ursprungsstring ohnehin nicht übernommen:
1
chara[]="Hallo";
2
char[100]b;
3
snprintf(b,100,"lalala%slalala",a);
wird die terminierende Null von a ja sowieso nicht übernommen.
Viele Grüße
W.T.
Michl schrieb:> Einzige Möglichkeit die mir einfällt ist einen temporären Puffer> anzulegen, dort reinzuschreiben und dann mittels strncpy das Ergebnis> kopieren.
Aus der strncpy-Doku:
"destination and source shall not overlap (see memmove for a safer
alternative when overlapping)."
Ohne zusätzlichen Buffer geht es nur, wenn du die Länge des
einzukopierenden Strings vorher kennst, dir das durch /0 überschriebene
Zeichen vor dem kopieren merkst, und dannach zurückschreibst.
Oliver
Oliver schrieb:> Ohne zusätzlichen Buffer geht es nur, wenn du die Länge des> einzukopierenden Strings vorher kennst, dir das durch /0 überschriebene> Zeichen vor dem kopieren merkst, und dannach zurückschreibst.
Danke dir. So hab ichs mir auch gedacht. Ist zwar auch a weng a
Gefrickel, aber sollte tun und braucht weder Puffer noch muss was
kopiert werden.
Michl schrieb:> Oliver schrieb:>> Ohne zusätzlichen Buffer geht es nur, wenn du die Länge des>> einzukopierenden Strings vorher kennst, dir das durch /0 überschriebene>> Zeichen vor dem kopieren merkst, und dannach zurückschreibst.>> Danke dir. So hab ichs mir auch gedacht. Ist zwar auch a weng a> Gefrickel, aber sollte tun und braucht weder Puffer noch muss was> kopiert werden.
Was macht man denn normalerweise mit einem String?
Man gibt ihn aus.
Und in dem Fall erhebt sich dann auch oft die Frage, ob man denn
überhaupt in einem Rutsch ausgeben muss. Ein
1
sprintf(buff,"Dies ist %s einfach so\n",str);
2
lcd_puts(buff);
ist zwar nett. Aber ein
1
lcd_puts("Dies ist ");
2
lcd_puts(str);
3
lcd_puts(" einfach so\n");
tut es ja auch. Ganz ohne Umkopiererei oder sonstigen Aufwand. Und kein
Benutzer wird je merken, dass der Text am LCD nicht 'in einem Zug'
geschrieben wurde.
Selbiges für Ausgaben über die UART, selbiges für Ausgaben auf ein File.
Das stimmt meistens.
Wenn doch nicht, und man mit GNU arbeitet: evtl. geht es, die Datei mit
fmemopen() (mit "b" im mode) zu öffnen.
Dnn werden nachfolgende fprintf() etc. direkt in den Speicher schreiben.
Ich bin nicht sicher, ob dann eine Nullterminierung geschrieben wird.
Falls doch: man kann sich mit fopencookie() so etwas ähnliches auch
selbst bauen und hat dann volle Kontrolle, was beim Schreiben geschieht.
Damit und mit vfprintf() könnnte man sich das gewünschte mprintf()
zusammenschrauben (wie sprintf, aber ohne Terminierung).
Könnte man ja vielleicht tatsächlich noch öfter brauchen ...
Klaus Wachtler schrieb:> Wenn doch nicht, und man mit GNU arbeitet: evtl. geht es, die Datei mit> fmemopen() (mit "b" im mode) zu öffnen.> Dann werden nachfolgende fprintf() etc. direkt in den Speicher schreiben.> Ich bin nicht sicher, ob dann eine Nullterminierung geschrieben wird.
Ich habe es mal probiert, und es scheint zu klappen:
Früher gab's mal die Funktion memcpy, ich gehe mal davon aus, dass
selbige nicht entfernt wurde.
Der einzige Nachteil ist, dass Du nun für ein eventuell überschriebenes
Stringende selber zuständig bist.
Wenn die Einfügeposition sowieso bekannt ist...
Karl Heinz schrieb:> Ganz ohne Umkopiererei oder sonstigen Aufwand. Und kein> Benutzer wird je merken, dass der Text am LCD nicht 'in einem Zug'> geschrieben wurde.
Grundsätzlich geb ich dir recht. Der Benutzer merkt nicht, dass die
Ausgabe auf mehrere Schritte verteilt stattfindet.
Sehr wohl aber der Implementeur.
Mal ehrlich, wieso sollte man auf die Komfortfunktionen von den
printf-Funktionen verzichten, wenn man sowohl Speicher als auch CPU-Zeit
übrig hat? Das grenzt manchmal ja schon an Selbstkasteiung.
Ich hab auch noch eine Stelle in meiner Software, wo ich mir auf dem LCD
Messwerte ausgeben lasse. Auf klassische Weise. Und mit klassisch mein
ich: die LCD-API hat eine Funktion zum Cursor setzen, eine FUnktion zum
Zeichen schreiben und eine Funktion zum String schreiben. Der "Ausgeber"
darf sich also seine Messwerte selbst umrechnen und formatieren, muss
dazu natürlich auch lokale temporäre Variablen halten. Von den zig
Aufrufen der LCD-Funktionen mal ganz zu schweigen.
Natürlich klappt das. Aber es macht keinen Spaß. Und wenn du mal was
änderst ist die Chance groß, dass die Ausgabe im ersten Schuss erstmal
nicht so aussieht wie erwartet (und sei es nur weil ein Messwert nur 2
statt 3 Dezimalstellen hat und die Ausgabe deswegen verschoben ist).
Meine UART-Routinen arbeiten schon lange nur noch mit formatierter
Ausgabe. Mein LCD demnächst auch.
Erst gestern hatte ich den Fall dass ich über I²C Messwerte (8 Stück)
bekam, die dann weiterverarbeitet werden. Das Ergebnis passte nicht.
Also haut man sich an mehreren Stellen im Code einen printf-Aufruf zum
Debuggen rein und schon sieht man wo was schief läuft.
Nein, auf den Komfort will ich nicht mehr verzichten. Sowas mach ich
nicht mehr "per Hand".
Naja, aber der Thread ist eh abgedriftet. Ich hab meine Antwort, Danke
euch dafür, und jetzt tobt euch aus :-)
Hm...also meinem Printf (weder dem auf LCD noch dem auf UART) macht es
aber nichts aus, ob es dreimal mit Teilstrings oder einmal mit dem
zusammengesetzten Teil aufgerufen wird. Es ändert auch nichts an der
Ausgabe. Für die Formatierung sind schließlich wie üblich
Escape-Sequenzen zuständig.
1
printf("Hallo\b\b");
2
printf("\t Du\n");
3
printf("Experte!");
sieht genauso aus wie
1
printf("Hallo\b\b\t Du\nExperte!");
da letztendlich der Aufruf der putchar-Routinen völlig gleich verläuft.
Michl schrieb:> Mal ehrlich, wieso sollte man auf die Komfortfunktionen von den> printf-Funktionen verzichten, wenn man sowohl Speicher als auch CPU-Zeit> übrig hat?
Sollst du ja gar nicht. Deinen Teilstring kannst du ja ruhig mit sprintf
erstellen. Was aber Blödsinn ist, ist der Ansatz, den Teilstring dann
mit irgend einer Stringfunktion in einen anderen hineinzukopieren.
Anfang und Ende des gesamten Strings müssen doch bekannt sein, denn
ansonsten kommt da nur unleserlicher Unsinn raus.
Alos gib den Anfargstring aus, dann denn Mittelstring, und dann den
Endstring.
Oliver
Michl schrieb:> Naja, aber der Thread ist eh abgedriftet.
Um ehrlich zu sein.
Ich denke, ich hab immer noch nicht verstanden, wo denn jetzt eigentlich
der Schuh drückt.
na ganz einfach: er hat einen langen String, und will dort irgendwo
mittendrin mit etwas wie sprintf() einen Teil überschreiben.
sprintf() geht aber für ihn nicht, weil es seine Ausgabe mit \0
terminiert und dadurch der alte lange String mittendrin terminiert ist.
1
vorher: alt_alt_alt_alt_alt_alt_alt_alt\0
2
Wunsch: alt_alt_neu_neu_alt_alt_alt_alt\0
3
mit sprintf(): alt_alt_neu_neu\0alt_alt_alt_alt\0
Deshalb will er etwas wie sprintf(), das aber seine Ausgabe nicht
terminiert.
WARUM er das will, ist doch egal.
Klaus Wachtler schrieb:> na ganz einfach: er hat einen langen String, und will dort irgendwo> mittendrin mit etwas wie sprintf() einen Teil überschreiben.
Ah! Jetzt hab ichs.
Er will sowas
1
chartext[]="Spg V, Strm A";
2
3
sreplace(text,4,"%3d",spg);
4
sreplace(text,15,"%3d",strm);
und das "spreplace" soll mit einem Format String arbeiten, aber das
Ergebnis an eine definierte Stelle in den String einbauen.
Ja, könnte man machen. Muss man aber selber programmieren. Elegant würde
das ganze, wenn man die Positionsangabe, dann auch noch mit in den
Format String reinkriegt, zb.
1
chartext[]="Spg V, Strm A";
2
3
sreplace(text,"&4%3d&15%3d",spg,strm);
Für mich persönlich sehe ich die Notwendigkeit noch nicht, weil ich auf
dem LCD Fixtext und variablen Text sowieso grundsätzlich trenne, und auf
der UART stellt sich das Problem so nicht wirklich, denn bei einem fixen
Layout wird wieder Fixtext und variabler Text getrennt, während bei
gescrollter Ausgabe einfach mit sprintf ausgegeben wird.
Ja, manchmal ist das ein bischen Gefummel, bis man die Stellen dort hat,
wo man sie haben will, aber so schlimm auch wieder nicht. Bezogen auf
ein Gesamtprojekt fällt das meist unter ferner liefen.
Aber ok. So mach ich das, das bedeutet nicht, dass alle es so machen
müssen.
Wobei man bei einem LCD ja auch direkt jeden Teilbereich überschreiben
kann.
Ich weiß nicht, wofür er es letztlich nehmen will: AVR oder was anderes?
LCD oder was anderes?
Ich könnte mir vorstellen, dass der TO ein virtuelles Display und ein
reales verwendet. Ersteres existiert als Buffer im RAM, eine (mehr oder
minder intelligente) Ausgaberoutine bildet dann diesen Buffer auf das
real existierende Display ab.
Genau in diesem Falle bräuchte man die Möglichkeit, formatiert in einen
Bereich eines Buffers schreiben zu können.
Diese Aufgabe könnte man - auf Terminals bezogen - mit der
Curses-Funktion printw() und deren Verwandten vergleichen.
Frank M. schrieb:> Ich könnte mir vorstellen, dass der TO ein virtuelles Display und ein> reales verwendet. Ersteres existiert als Buffer im RAM, eine (mehr oder> minder intelligente) Ausgaberoutine bildet dann diesen Buffer auf das> real existierende Display ab.
Das sind 100 Punkte, du hast recht :-)
Ich geb zeichenweise meinen Puffer auf dem LCD aus um das "aktive
Warten" bei längeren Texten zu vermeiden.
Nun will ich die LCD Routinen eben um formatierte Ausgabe erweitern.
PS: mir wird ja jetzt gleich wieder angekreidet, ich hätt nicht von
Anfang an gesagt was ich vorhab.
Problem dabei ist, ich hätt dann 100 Vorschläge gekriegt wie eure LCD
Routinen aussehen, aber keine Antwort auf die ursprüngliche Frage. Kennt
man ja leider.
Aber um das Thema abzuschließen: ich werd wohl in einen temporären
Puffer schreiben müssen und diesen dann umkopieren.
Problem ist, dass die Länge der formatierten Ausgabe ja erstmal
unbekannt ist, und damit auch die Position des Zeichens, das man
"retten" müsste wenn man direkt in den Displaypuffer schreibt.
Alternative wäre snprintf() zweilmal laufen zu lassen, bei ersten mal
mit NULL als pointer. Dann hätte ich die Länge. Allerdings kostet diese
Variante wohl deutlich mehr Overhead als ein strncpy oder memcpy.
Grüße
Karl Heinz schrieb:> Ja, könnte man machen. Muss man aber selber programmieren. Elegant würde> das ganze, wenn man die Positionsangabe, dann auch noch mit in den> Format String reinkriegt, zb.> char text[] = "Spg V, Strm A";>> sreplace( text, "&4%3d&15%3d", spg, strm );
So ähnlich schauts bei mir aus.
Allerdings lege ich die Positions- und Längeninfo nicht im Formatstring
ab. Dieser soll einfach wie von printf gewohnt funktionieren.
In meiner LCD-Konfiguration definiere ich mir "Textfelder", die z.B. so
aussehen:
1
typedefstruct
2
{
3
uint8_tx;
4
uint8_ty;
5
uint8_tsize;
6
}textfield_t;
7
8
textfield_ttextfields[]={{1,0,12},{1,1,8}};
9
10
#define LCD_FIELD_VTG 0
11
12
...
13
...
14
Lcd_Print(LCD_FIELD_VTG,"Vtg: %iV",voltage);
So zumindest die Idee. Ob der Textfeld-Mechanismus in der Praxis Sinn
macht oder zu umständlich ist wird sich zeigen, ist noch nicht ganz
fertig.
Aber gerade die Größenbegrenzung verhindert dass man sich wieder mal
verzählt hat und Bereiche des Displays überschreibt.
Unabhängig davon ist mir die formatierte Ausgabe aber recht wichtig.
Grüße
Michl schrieb:> Das sind 100 Punkte, du hast recht :-)> Ich geb zeichenweise meinen Puffer auf dem LCD aus um das "aktive> Warten" bei längeren Texten zu vermeiden.> Nun will ich die LCD Routinen eben um formatierte Ausgabe erweitern.
OK.
Wovon reden wir?
Von Gcc, genauer AVR-gcc?
Denn da gäbe es noch eine Möglichkeit.
Du kannst ja dem I/O System eine putchar Routine unterjubeln, die von
printf bzw. fprintf zur Ausgabe benutzt wird. Leitest du mittels putchar
die Ausgabe in deinen LCD-Buffer um, dann müsste das meinem Verständnis
nach ziemlich genau das sein, was du suchst.
avr-gcc, allgemeiner eben gcc.
Meine Standardausgabe ist bereits umgeleitet, auf eine Instanz vom UART.
Könnte natürlich mir nochmal einen Stream anlegen und ein
fprintf-Derivat benutzen.
Aber mal schauen, irgendwann wirds halt doch Overkill. Die Streamausgabe
bringt eben mit sich dass die zum Stream gehörende putc() unter
Umständen sehr oft aufgerufen wird.
Momentan gefällt mir die Lösung so wie ich sie in meinem letzten Beitrag
beschrieben habe am Besten, also der Weg über die "Textfelder". Mit dem
Umkopieren aus dem temporären Puffer kann ich ja leben, wie im
Eröffnungspost geschildert. Die Frage war ja, ob es denn was anderes
gäbe.
Karl Heinz schrieb:> Michl schrieb:>>> Das sind 100 Punkte, du hast recht :-)>> Ich geb zeichenweise meinen Puffer auf dem LCD aus um das "aktive>> Warten" bei längeren Texten zu vermeiden.>> Nun will ich die LCD Routinen eben um formatierte Ausgabe erweitern.>> OK.>> Wovon reden wir?> Von Gcc, genauer AVR-gcc?>> Denn da gäbe es noch eine Möglichkeit.> Du kannst ja dem I/O System eine putchar Routine unterjubeln, die von> printf bzw. fprintf zur Ausgabe benutzt wird. Leitest du mittels putchar> die Ausgabe in deinen LCD-Buffer um, dann müsste das meinem Verständnis> nach ziemlich genau das sein, was du suchst.
Das sähe dann (minus Tippfehler), circa so aus
Deinen LCD-Rausschaufelmechanismus musst du natürlich noch auf
lcd_screen ansetzen und ein paar zusätzliche Hilfsfunktionen zum
Cursorpositionieren bzw. Screen löschen wirst du noch brauchen, aber so
würde der Basismechanismus aussehen, mit dem man printf überredet, in
einen Speicherbereich zu schreiben. Ganz ohne Umweg über zusätzliche
Strings.
(Ob du die Newline Behanldung überhaupt brauchst, bzw. was beim Zeilen
Overflow passieren soll, musst du natürlich selbst entscheiden. Ich hab
halt mal einfach mal Wrap-Arounds angenommen. Kann man auch alles
weglassen)