Bestimmte Zeichenkette aus char* löschen
-
Undertaker schrieb:
Shade Of Mine schrieb:
in manchen situationen einfach super praktisch
kennst du eine?
return strcmp(strtoupper(string_remove(s, 3, 6)), s2);
und ähnliches
-
Shade Of Mine schrieb:
Undertaker schrieb:
Shade Of Mine schrieb:
in manchen situationen einfach super praktisch
kennst du eine?
return strcmp(strtoupper(string_remove(s, 3, 6)), s2);
und ähnliches
oh ja, sowas ist besonders praktisch, wenn man fehler drin hat.
in welcher funktion ist er wohl abgestürzt? :p
-
Undertaker schrieb:
oh ja, sowas ist besonders praktisch, wenn man fehler drin hat.
in welcher funktion ist er wohl abgestürzt? :pist doch komplett egal ob ich
string_remove(s, 3, 6); strtoupper(s); return strcmp(s, s2); //oder strcmp(strtoupper(string_remove(s, 3, 6)), s2);
schreibe.
wo soll da der unterschied sein? ich bekomme ja keine fehlermeldung in zeile X ist ein fehler aufgetreten. ich bekomm nen backtrace.
und wenn ich denn mal debug ausgaben zwischen den einzelnen funktionsaufrufen machen muss (wozu man das in zeiten von debuggern machen will weiss ich allerdings nicht) kann ich die zeile immernoch aufsplitten).
man muss nicht auf teufel komm raus alles in eine zeile packen, aber wenn es sinn macht: warum sollte man solche konstrukte verbieten?
-
Shade Of Mine schrieb:
und wenn ich denn mal debug ausgaben zwischen den einzelnen funktionsaufrufen machen muss (wozu man das in zeiten von debuggern machen will weiss ich allerdings nicht) kann ich die zeile immernoch aufsplitten).
mir ist es tatsächlich mehr als einmal passiert, dass ich einen schönen einzeiler wieder auseinanderreissen musste, um vernünftig durchzusteppen.
ausserdem hat nicht jeder ms visual studio
-
Natürlich kann man ruhig Read-Only-Strings benutzen, wenn die Funktion bzw. das ganze Modul darauf abgestimmt, vorbereitet ist.
Natürlich terminiert auch strncpy, strcat richtig, wenn die Parameter überprüft werden.
( Und bei jeder halbwegs ernstzunehmenden Anwendung sollten die Parameter stets auf Herz und Nieren geprüft werden)
Ich stelle jetzt einfach mal meine Version vor, wie ich es machen würde.
Teilweise lasse ich die Namen der Variablen so, wie sie der Autor dieses Threads bezeichnet hat.
Die Anforderung an die Funktion ist ferner, das auch mitten aus der Zeichenkette heraus Buchstaben gelöscht werden dürfen und der übergebene Parameter ein Read-Only-String sein darf.
Here we go:// D.n.n.N.S ( Der noch nettere Nachbar Software ) // Programm Delete // Entfernt Buchstaben aus einer Zeichenkette // Version 0.81 beta #include <limits.h> // UINT_MAX #include <stdio.h> // printf #include <stdlib.h> // malloc #include <string.h> // strlen // Maximale Länge der Zeichenkette // Das dürfte für die meisten Anwendungen ausreichen. // Wenn nicht, gibt es noch größere Datentypen. #define ZK_MAXLEN UINT_MAX // Prüft, ob die Zeichenkette eine bestimmte Länge nicht überschreitet // und ob die Zeichenkette richtig terminiert ist. // ( Vermeidung von Programmabstürzen ); // Rückgabewerte: 0 == OK, 1 == Fehler int pruefe_zeichenkette( char* zk ) { unsigned int i; for ( i = 0; i < ZK_MAXLEN; i++ ) { if ( zk[i] == '\0' ) return 0; // Zeichenkette ist ok } return 1; // Zeichenkette zu lang oder nicht '\0' terminiert. } // Bei Erfolg wird der Zeiger auf die neue Zeichenkette übergeben. // Im Falle eines Fehlers wird NULL zurückgegeben und der Grund des Fehlers // in der Konsole angezeigt. // Der Aufrufer muss für die Freigabe des reservierten Speicherplatzes sorgen. // Werden alle Zeichen gelöscht, wird ein gültiger Zeiger auf '\0' // zurückgegeben. char* Delete( char* Zeichenkette, unsigned int start_position, unsigned int Anzahl ) { char* neue_zk = NULL; unsigned int neue_laenge = 0; // ******** Prüfung der Parameter ************************************************* if ( Zeichenkette == NULL ) return NULL; // Es gibt keine Zeichen zu löschen. if ( Anzahl == 0 ) return NULL; // dito // Wurde auch kein Blödsinn eingegeben ? if ( (int) Anzahl < 0 ) { // Ja. fprintf( stderr, "%s", "Anzahl < 0\n"); return NULL; // Dafür gibts auch nichts zurück } neue_laenge = strlen( Zeichenkette ) - Anzahl + 1; // Wurde auch kein Blödsinn eingegeben ? if ( (int) neue_laenge <= 0 ) { // Ja. fprintf( stderr, "%s", "neue_laenge <= 0\n"); return NULL; // Dafür gibts auch nichts zurück } // Wurde auch kein Blödsinn eingegeben ? if ( (int) start_position < 0 ) { // Ja. fprintf( stderr, "%s", "start_position < 0.\n"); return NULL; // tststs } // Wurde auch kein Blödsinn eingegeben ? if ( start_position > strlen( Zeichenkette ) - 1 ) { // Ja. fprintf( stderr, "%s", "start_position > strlen( Zeichenkette ) - 1\n"); return NULL; // pfui ! } // Prüfe die Zeichenkette, ohne dem läuft erst gar nix an ;-) if ( pruefe_zeichenkette( Zeichenkette ) ) { fprintf( stderr, "Die Zeichenkette ist zu lang, oder nicht korrekt Terminiert.\n"); return NULL; // Zeichenkettentest durchgefallen } // ***************** Es kann endlich los gehen :-) ******************************** // Arbeitsspeicher für den neuen String reservieren. if ( NULL == ( neue_zk = calloc( neue_laenge, sizeof( char ) ) ) ) { // Arbeitsspeicher konnte nicht reserviert werden. perror( "Delete: malloc failed\n" ); // perror liefert u.U. noch weitere // Informationen über die Fehler-Ursache. return NULL; } strncpy( neue_zk, Zeichenkette, start_position ); strcat ( neue_zk, Zeichenkette + start_position + Anzahl ); return neue_zk; } // Beispielprogramm für die Verwendung der Funktion Delete int main() { char* neu; char* alt = "Sie zu Ihm: Leck Mich am Schnürsenkel !\n"; neu = Delete( alt, 19, 15 ); printf("%s\n", neu ); // Wird die Zeichenkette nicht mehr gebraucht, // reservierten Speicherplatz wieder frei zu geben. free( neu ); puts("Problem: Er ist nicht schwul und moechte daher Mikel auch nicht lecken,\n" "schon gar nicht am Schnuersenkel( logisch ).\n" "Abhilfe: Parameter aendern.\n\n"); // Ist Er nicht schwul, will er auch den Mikel nicht lecken(LOL), darum besser: neu = Delete( alt, 21, 17 ); printf("%s ( oops )\n", neu ); // ahaa schon besser ! :-D free( neu ); return 0; }
-
perfektes beispiel wie man es nicht macht. dein Delete schreit foermlich danach speicher zu leaken.
und strncpy terminiert nicht mit 0 wenn n<=strlen(src) ist.
was genau soll der sinn von pruefe_zeichenkette sein?
die fehler abfragen in Delete sind auch ein horror. wozu erst nen unsigned verlangen wenn du dann erstrecht wieder auf int wechselst, etc?
lahm ist das ding auch noch.sorry, aber das ist eine sehr miese lösung. ich kann ihr nix gutes abgewinnen.
Lektion Nummer 1 beim Interface Design in C: NEVER EVER intern allokierten speicher nach aussen geben.
PS:
char* remove_buffer(char* dst, char* src, int start, int len) { assert(dst); assert(src); assert(start>0); assert(len>0); assert(start+len<=strlen(src)); memcpy(dst, src, start); strcpy(dst+start, src+start+len); return dst; }
sogar mit kompletter fehlerüberprüfung
-
Shade Of Mine schrieb:
sogar mit kompletter fehlerüberprüfung
dir ist schon klar, dass 'assert' nur in der debug-version wirksam ist?
-
Undertaker schrieb:
Shade Of Mine schrieb:
sogar mit kompletter fehlerüberprüfung
dir ist schon klar, dass 'assert' nur in der debug-version wirksam ist?
das ist sinn der sache. logik fehler in der debug version abfangen.
was anderes als sofort die anwendung beenden kann man eh nicht machen wenn ein logikfehler aufgetreten ist.
-
Shade Of Mine schrieb:
perfektes beispiel wie man es nicht macht. dein Delete schreit foermlich
danach speicher zu leaken.Wo sollte denn da was leaken.
Man braucht nur dafür zu sorgen, das der reservierte Speicherplatz für
jede Zeichenkette die zurückgegeben wird, mit free frei gegeben wird.Shade Of Mine schrieb:
und strncpy terminiert nicht mit 0 wenn n<=strlen(src) ist.
Das muss hier auch nicht, weil der Speicherplatz für die neue
Zeichenkette mit calloc reserviert wird. Dadurch hat man die
Terminierung im Falle n<=strlen(src) quasi 'frei Haus'.Shade Of Mine schrieb:
was genau soll der sinn von pruefe_zeichenkette sein?
Tja, wenn du den Kommentar über der Funktion gelesen hättest dann
wüßtest du es.
Aber möglicher weise hast du ihn gelesen und den Sinn nicht verstanden.Shade Of Mine schrieb:
die fehler abfragen in Delete sind auch ein horror. wozu erst nen
unsigned verlangen wenn du dann erstrecht wieder auf int wechselst, etc?Ganz einfach:
Unigned, weil start_position und Anzahl logischer weise nicht negativ
sein sollten.
if ( (int) Anzahl < 0 )
soll verhindern das der Benutzer einen negativen Wert benutzt und weil
die Variable Anzahl vom Typ unsigned int ist, würde
if ( Anzahl < 0 )
bei einem negativen Wert nicht zu dem gewünschten Ergebnis führen, etc.Shade Of Mine schrieb:
Lektion Nummer 1 beim Interface Design in C: NEVER EVER intern
allokierten speicher nach aussen geben.Learning Interface Design by Shade Of Mine ?
Der Sinn dieser Funktion ist doch gerade der, Zeichenketten zu erzeugen
und nach aussen zu geben.
Betrachte es als stand-alone-interface, für die Freigabe des Speicherplatzes ist der Benutzer verantwortlich.Shade Of Mine schrieb:
// ... ( Auf die Darstellung des Quellcodes wird hier aus didaktischen Gründen verzichtet. )
sogar mit kompletter fehlerüberprüfungDas glaubst du doch selbst nicht. Nicht einmal annähernd in der
Debug-Version.Die Funktion soll aber gerade im Einsatz, also als Release-Version möglichst viele Fehlerquellen selbständig abfangen. Da werden dir deine asserts nichts nützen.
Hier habe ich manch einen Fehler über stderr ausgegeben.
In der Praxis schreibe ich die Fehlerursachen in einen char-Puffer, dann
kann der Anwender entscheiden, ob und wie er die Fehlermeldung anzeigen
möchte. ( Konsole, MessageBox, etc. )
Und wie sein Programm darauf reagieren soll.Shade Of Mine schrieb:
lahm ist das ding auch noch.
Warum ? Welches Ding ?
Sollte sich die Funktion in der Praxis als zu langsam herausstellen, kann man über Optimierungen immer noch nachdenken. Hier steht aber die Programm-Stabilität im Vordergrund.Shade Of Mine schrieb:
sorry, aber das ist eine sehr miese lösung. ich kann ihr nix gutes abgewinnen.
Diese Meinung kannst du ruhig beibehalten.
Ich bin jemand, der gern dazulernt. Ich habe den Qeullcode so freizügig gepostet, damit er von den C-Profis ordentlich zerpflückt wird( insofern sie Zeit und Lust dazu haben ) und ich
dadurch dazu lernen kann.
Sorry, aber von deinen Argumenten konnte mich nicht ein einziges des Besseren belehren.MfG D.n.n.N. ( Der noch nettere Nachbar )
-
Shade Of Mine schrieb:
Undertaker schrieb:
Shade Of Mine schrieb:
sogar mit kompletter fehlerüberprüfung
dir ist schon klar, dass 'assert' nur in der debug-version wirksam ist?
das ist sinn der sache. logik fehler in der debug version abfangen.
was anderes als sofort die anwendung beenden kann man eh nicht machen wenn ein logikfehler aufgetreten ist.ihr geht beide von anderen voraussetzungen aus.
das programm von "Der noch nettere Nachbar" versucht unerwarteten, chaotischen input abzufangen, während du davon ausgehst, dass der code nur intern verwendet wird und mistige eingaben ausschliesslich auf programmierfehler zurückzuführen sind.
also wird das wohl ein thread, in dem alle aneinander vorbeireden.
-
hallo zusammen
also erstmal danke dass ihr hier alle so fleißig ward.
hab mir jetzt alles mal durchgelesen, aber jeder hatte an dem code jemand anderen etwas, was nicht so ok war.
was soll ich denn jetzt nehmen?
gibt es eine "endversion" *gg*
-
MSS-Software schrieb:
...aber jeder hatte an dem code jemand anderen etwas, was nicht so ok war.
was soll ich denn jetzt nehmen?
gibt es eine "endversion" *gg*Die Entscheidung bleibt letztlich bei dir. Wenn du dich darauf verlassen kannst, das stets gültige Parameter übergeben werden, wäre meine Version mindestens zu 85% redundant und man könnte die ganze Fehlerprüfung auch gleich weglassen.
Möchtest, bzw. musst du keine 'Read-Only-Strings' benutzen, kannst du auch die dynamische Reservierung von Arbeitsspeicher komplett weglassen und die Zeichenketten einfach in ihrem Puffer schrumpfen lassen. usw, usw, usw.
Kurzum: Es hängt vom Anwendungsfall ab.Ich habe jedenfalls noch etwas verbesserungswürdiges entdeckt.
Es macht durchaus Sinn
#define ZK_MAXLEN UINT_MAX
durch
#define ZK_MAXLEN INT_MAX
zu ersetzen, denn (int) neue_laenge <= 0
liefert für neue_laenge = INT_MAX + 1 einen wahren Wert.Naja, INT_MAX sollte ja wohl auch ausreichen.
Wer geübt im Tippen ist und 255 Anschläge pro Minute schafft, der ist mit dem Eintippen einer solchen Zeichenkette schon nach ca. 16 Jahren fertig.( 4 Byte Integer vorausgesetzt )
MfG
-
also ihr könnt mir ja sagen was ihr wollt, aber ich kriege es immer noch net hin
ich hab jetzt mal folgendes gemacht:
char* Delete(char* Zeichenkette, int start_position, int Anzahl) { char *rueckgabe_wert = (char*)malloc(strlen(Zeichenkette+1)); rueckgabe_wert = Zeichenkette; for ( int i = 1; i < Anzahl; i++ ) rueckgabe_wert[start_position] = 'x'; return rueckgabe_wert; }
ich weiß, ihr werdet mir jetzt alle ins gesicht springen weil ich den speicher noch nicht wieder freigebe
also: bei dem code hängt er sich auf, sobald er das zeichen x zuweisen will
lasse ich die zeile
rueckgabe_wert = Zeichenkette;
aber weg, schreibt er mir da problemlos rein.
wo isn da der unterschied?
ich möchte doch einfach nur ein zeichen durch ein anderes ersetzen?
-
MSS-Software schrieb:
ich weiß, ihr werdet mir jetzt alle ins gesicht springen weil ich den speicher noch nicht wieder freigebe
Nein, deswegen springt dir niemand ins Gesicht, aber: (a) brauchsst du den Rückgabewert von malloc nicht zu casten und (b) vergisst du mit der Zeile 'rueckgabewert=Zeichenkette' die Adresse des neureservierten Speicherbereichs und biegst deinen Zeiger um auf den übergebenen Parameter (das bedeutet, in der Schleife schreibst du nicht in dem Speicherbereich, den malloc() dir besorgt hat, sondern in dem, den du von außen bekommen hast).
Wenn du den Inhalt von 'Zeichenkette' kopieren willst, brauchst du strcpy().
-
wenn ich den aber nicht caste, kommt er mir damit
'Initialisierung': 'void *' kann nicht in 'char *' konvertiert werden
gut, strcpy hat geholfen
ich wollte mich dann nochmal bei euch entschuldigen.
hab bis jetzt hauptsächlich immer nur mit strings gearbeitet in der MFC
gibts denn irgendein tut oder so, was ich mir zu diesem thema mal durchlesen könnte?
-
MSS-Software schrieb:
wenn ich den aber nicht caste, kommt er mir damit
'Initialisierung': 'void *' kann nicht in 'char *' konvertiert werden
Wenn du C Programme schreibst, solltest du auch einen C-Compiler verwenden, um sie übersetzen zu lassen
(ein C++ Compiler ist an dieser Stelle etwas pingeliger mit der Typumwandlung)