[c] String Funktionen

  • Themenstarter Themenstarter Bgag
  • Beginndatum Beginndatum
B

Bgag

Hallo zusammen!

Ich arbeite gerade an einem kleinen Projekt bei dem ich einige kleine Funktionen brauche. Leider bin ich noch ziemlicher Anfänger in C und brauche daher noch etwas Hilfe.

Momentan sitze ich an einer Funktion, die einen String, einen zweiten String und einen optionalen offset-Wert erhält bzw. erhalten kann. zurückgeben soll diese Funktion die erste Position ab der Stelle offset, an der der zweite String im ersten String vorkommt. Eine Grundform der Funktion könnte also so aussehen:

int strpos(char *s, char *needle [, int offset = 0 ] );

Ich habe leider garkeine Idee, wie ich dieses problem anfassen soll. Wäre nett, wenn mir jemand helfen könnte.

MfG, Andy
 
Hi.

Du möchtest also die Standard-Funktion strstr nachprogrammieren?

Das mit dem Offset ist relativ leicht, du mußt nur den Zeiger weiterschieben.

Soll dein Prototyp mit dem optionalen Parameter bedeuten, dass du C++ verwendest? Warum nimmst du dann nicht einfach einen std::string und die std::string::find Methode?

Gruß
 
Nein ich möchte die Position zurückgeben. strstr() gibt, wenn ich mich nicht ganz taeusche einen Pointer auf die Position zurueck. oder kann ich daraus vielleicht einfach die Position berechnen.
MfG, Andy
 
Guten Abend!

Danke erstmal für deine Hilfe. Habe mal eben versucht deinen Vorschlag umzusetzen, bin mir aber noch nicht so ganz sicher, ob das überhaupt so klappt. Entspricht mein Vorgehen beim Arbeiten mit variablen Argumentenlisten denn dem Standard? Funktioniert das auch wirklich so. Habe im Netz nur Beispiele für eine nicht genau bestimmte Menge von Argumenten gefunden (z.B. printf() oder add()), bei denen die Anzahl der Argumente irgendwie deklariert wurden (z.B. Formatierungs-String bei printf()).

Ich habe zudem eine zweite Funktion geschrieben, die es mir ermöglichen soll anhand eines Strings, eines Startpunktes und einem optionalen Längenparameter, einen Teilstring zurückgeben kann. Die beiden Funktionsdefinitionen sehen lso wie folgt aus:

C:
int strpos(const char *s, const char *needle, ...);
char *substr(const char *s, int offset, ...);

Es wäre einerseits nett, wenn mir jemand sagen könnte, ob mein Umgang mit den Funktionen der Standard-Bibliothek für variable Argumentenlisten zulässig ist und wenn mir jemand sagen könnte, ob meine Berechnung der Position der Zeichenkette im String mittels

C:
(int) s - (int) strstr(tmp, needle)

so korrekt ist. zudem würde ich mich natürlich auch über weitere Anmerkungen zu meinen beiden Funktionen freuen, die ihr im übrigen am Ende dieses Posts finden werdet.

Vielen Dank für euer Verständnis und eure Hilfe.

MfG, Andy

C:
/* include needed packages */
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>

char *substr(const char *s, int offset, ...)
{
	/* init return val */
	char *ret;

	/* init helper and length var */
	int i, length;

	/* init arg pointer */
	va_list ap;

	/* set pointer to first variable argument */
	va_start(ap, offset);

	/* get first variable argument */
	length = va_arg(ap, int);

	/* free var list */
	va_end(ap);

	/* check offset */
	if( (length == 0) || (length > (strlen(s) - start)) )
	{
		length = strlen(s);
	}

	/* init return val */
	ret[length];

	/* copy return val from string */
	for(i = offset; i <= (offset+length); i++)
	{
		ret[i] = s[i];
	}

	return ret;
}

int strpos(const char *s, const char *needle, ...)
{
	/* init helper vars */
	char *tmp;

	/* init return and offset val */
	int ret, offset;

	/* init arg pointer */
	va_list ap;

	/* set pointer to first variable argument */
	va_start(ap, needle);

	/* get first variable argument */
	offset = va_arg(ap, int);

	/* free var list */
	va_end(ap);

	/* check offset */
	if( offset != 0 )
	{
		/* remove first elements of string */
		tmp = substr(s, offset);

		/* return first appearance of needle */
		return (int) s - (int) strstr(tmp, needle);
	}

	return 0;
}
 
Hi.
Entspricht mein Vorgehen beim Arbeiten mit variablen Argumentenlisten denn dem Standard?
Nein. Du weißt doch gar nicht wieviel Argumente deiner Funktion übergeben wurden. Dann greifst du einfach auf ein Argument zu - das heißt du mußt immer mind. 3 Argumente übergeben.

Außerdem ist der "optionale" Parameter auch unnötig:
C:
strpos(s, sub, offset);

// ist äquivalent zu

strpos(s + offset, sub);
C:
/* init return val */
    char *ret;
...
/* init return val */
    ret[length];
Du dereferenzierst hier einen nicht initialisierten Zeiger. Das kann so nichts werden. Wenn du dort den String kopieren willst, mußt du Speicher allozieren und dann später natürlich auch wieder freigeben.
wenn mir jemand sagen könnte, ob meine Berechnung der Position der Zeichenkette im String mittels

C:
(int) s - (int) strstr(tmp, needle)
Also erstmal kann dort auch NULL zurückgegeben werden. Das solltest du erstmal abfangen. Dann brauchst du dort nicht wild umher zu casten. Mit Zeigern darf man ganz normal Arithmetik durchführen:
C:
const char* s = strstr(haystack, needle);
if (s != NULL) {
  return (s - haystack);
} else {
  return -1; // not found
}
Gruß
 
Abend!

Danke für deine Hilfe. Ich habe die Funktion strpos() mal nach deinen Anregungen abgeändert und bin nun zu folgendem Ergebnis gelangt:

C:
int strpos(const char *s, const char *needle)
{
	/* init helper vars */
	char *haystack;

	/* get poiter at first appearance of needle */
	haystack = strstr(s, needle);

	/* check if string equals NULL pointer */
	if( haystack != NULL )
	{
		return (haystack - s + 1);
	}

	/* search failed */
	return -1;
}

Funktioniert wirklich gut. Danke für den Tipp.

Leider weiß ich noch nicht, wie ich die zweite Funktion (substr()) nun abändern kann, sodass ich korrekt auf die optionalen Argumente der Funktion zugreife. Für diese Funktion gibt es ja leider keine so schöne Möglichkeit auszuweichen. Ich habe aber bereits die Allocierung des Speichers mit Hilfe von malloc eingefügt. Meine aktuelle Version der Funktion ist am Ende meines posts zu finden.

Vielen Dank aber nochmals für deine Hilfe.

MfG, Andy

C:
char *substr(const char *s, int offset, ...)
{
	/* init return val */
	char *ret;

	/* init helper and length var */
	int i, length;

	/* init arg pointer */
	va_list ap;

	/* set pointer to first variable argument */
	va_start(ap, offset);

	/* get first variable argument */
	length = va_arg(ap, int);

	/* free var list */
	va_end(ap);

	/* check offset */
	if( (length == 0) || (length > (strlen(s) - offset)) )
	{
		length = strlen(s);
	}

	/* init return val */
	ret = (char *) malloc(length * sizeof(char));

	/* copy return val from string */
	for(i = offset; i <= (offset+length); i++)
	{
		ret[i] = s[i];
	}

	return ret;
}
 
Hi.
Danke für deine Hilfe. Ich habe die Funktion strpos() mal nach deinen Anregungen abgeändert und bin nun zu folgendem Ergebnis gelangt:

C:
int strpos(const char *s, const char *needle)
{
...
		return (haystack - s + 1);
Warum +1 ? Eigentlich behandelt man in C Arrays und Strings 0-basiert.

Und die Benennung der Variablen ist nicht so glücklich. Eigentlich ist s ja der Heuhaufen in dem die Nadel gefunden werden soll und nicht andersrum.
Leider weiß ich noch nicht, wie ich die zweite Funktion (substr()) nun abändern kann, sodass ich korrekt auf die optionalen Argumente der Funktion zugreife.
Du bräuchtest einen zusätzlichen nicht-optionalen Parameter, der anzeigt ob bzw. wieviel optionale Parameter übergeben wurden. Das macht aber wenig Sinn, denn dann kannst du ja gleich immer die Länge in einem nicht-optionalen Parameter übergeben.

Dann sind ein paar Fehler in deiner substr Funktion:
  • den Rückgabewert von malloc sollte man in C nicht casten. (wenn dort eine Warnung vom Compiler kommt, hast du vergessen stdlib.h einzubinden)
  • um einen String der Länge n zu speichern muss man n + 1 Zeichen reservieren
  • dein Substring ist nicht 0-terminiert.

Warum verwendest du nicht die strncpy Funktion?

Gruß
 
Danke für deine Hilfe.

Du hast Recht die benennung der Variablen ist tatsächlich etwas verwirrend. Habe das mal geändert.

Wieso sollte man den Rückgabewert von malloc() nicht casten? Geht doch garnicht anders, da malloc() einen void Pointer zurückliefert. Wurde eigentlich auch extra von Ritchie und Kernighan so vorgesehen.

Zudem hast du Recht ich kann auch einfach strcpy und strncp verwenden und für das setzen eines Startwertes den gleichen Trick, wie bei der strpos Funktion verwenden.

Leider habe ich nun ein neues Problem. Ich möchte einige Funktionen schreiben, die es mir erlauben Zeichenketten in anderen Zeichenketten zu ersetzen und Zeichenketten anhand von Regexen zu suchen. Dabei habe ich dieses mal darauf geachtet optionale Parameter bei Funktionen zu umgehen. Dabei habe ich mich an strcpy() und strncpy() orientiert. Die Prototypen der Funktionen sehen nun wie folgt aus:

C:
void sreplace(char *search  , char *replace  ,char *subject);
void snreplace(char *search  , char *replace  ,char *subject, int n);
void regreplace(regex_t *pattern, char *replace, char *subject);
char **match(regex_t *pattern, char *subject);

Leider habe ich bisher eigentlich garnichts zu diesem Thema gefunden. Und die Dokumentation des Regex-Headerfiles hat mich leider auch nicht schlauer gemacht. Ich habe leider wirklich keine Ahnung, was die beiden Funktionen

C:
int regcomp(regex_t *, const char *, int);
int regexec(const regex_t *, const char *, size_t, regmatch_t[], int);

eigentlich genau machen bzw. wie ich sie zur Lösung meines Problems einsetzen könnte.

Es wäre nett, wenn mir auch bei diesem Problem jemand helfen könnte. Vielen Dank auch nochmals wegen der bisherigen Hilfe.

MfG, Andy
 
Wieso sollte man den Rückgabewert von malloc() nicht casten?
Siehe z.B. http://www.it.usyd.edu.au/~dasymond/mirror/c-faq/malloc/mallocnocast.html
Geht doch garnicht anders, da malloc() einen void Pointer zurückliefert.
Wie kommst du denn darauf? Ein void* kann implizit in jeden anderen Zeigertyp konvertiert werden.
Leider habe ich nun ein neues Problem. Ich möchte einige Funktionen schreiben, die es mir erlauben Zeichenketten in anderen Zeichenketten zu ersetzen und Zeichenketten anhand von Regexen zu suchen. Dabei habe ich dieses mal darauf geachtet optionale Parameter bei Funktionen zu umgehen. Dabei habe ich mich an strcpy() und strncpy() orientiert. Die Prototypen der Funktionen sehen nun wie folgt aus:

C:
void sreplace(char *search  , char *replace  ,char *subject);
void snreplace(char *search  , char *replace  ,char *subject, int n);
void regreplace(regex_t *pattern, char *replace, char *subject);
char **match(regex_t *pattern, char *subject);

Leider habe ich bisher eigentlich garnichts zu diesem Thema gefunden.
Willst du das in situ machen? Das wird aber problematisch wenn der Ersetzungstext größer ist als der Suchtext und somit der Resultatstext nicht mehr in den Eingabetext passt.... Da solltest du dann besser immer eine Kopie zurückgeben (oder zumindest einen Fehlercode).
Und die Dokumentation des Regex-Headerfiles hat mich leider auch nicht schlauer gemacht. Ich habe leider wirklich keine Ahnung, was die beiden Funktionen

C:
int regcomp(regex_t *, const char *, int);
int regexec(const regex_t *, const char *, size_t, regmatch_t[], int);

eigentlich genau machen bzw. wie ich sie zur Lösung meines Problems einsetzen könnte.
Siehe z.B. http://www.opengroup.org/onlinepubs/000095399/functions/regcomp.html (oder "man regcomp" unter Linux).

Gruß
 
Zurück