[C/C++] Alternative zu strtok()

jbJOGI

Cadet 4th Year
Registriert
März 2004
Beiträge
105
Hi,

ich hab ein Problem, das mich bald zur Verzweiflung treibt.

Und zwar programmiere ich gerade ein Programm, welches ich daheim im privaten LAN zur Steuerung meines Servers verwenden möchte, starten, runterfahren, etc.

Um nun Benutzereingaben, wie zb. eine IP (xxx.xxx.xxx.xxx), zu prüfen, zerlege ich diese in der Socket-Klasse, die die Verbindung aufbaut, in Teilstrings, trenne also am Punkt. Diese Teilstrings werden dann auf Gültigkeit geprüft und am Ende wieder zusammen gesetzt um den Connect zu machen.

Auf jeden Fall hatte ich Probleme, wenn ich diese Klasse in mehreren Threads verwendet habe. Wie ich mittlerweile rausgefunden habe, liegt das gerade an strtok(), da diese Funktion intern einen Zeiger benutzt um sich die Position im String zu merken, für weitere Aufrufe mit NULL als erstem Parameter (sie ist also nicht re-entrant, geschweige denn thread-save).

Also brauche ich eine alternative Funktion um meine Strings auch in verschiedenen Threads prüfen zu können. Im Netz habe ich dazu nun schon ein paar mal von strtok_r gelesen, was genau diese Funktionalität bietet. Angeblich sollte diese Funktion auch in string.h enthalten sein, was bei mir allerdings nicht der Fall ist. So wie es aussieht gibt es das nur für Unix/Linux, aber nicht für Windows.

Wie bekomme ich die Funktion auf meinen Rechner, damit ich sie endlich benutzen kann? Nur die string.h austauschen, wird wohl nicht helfen, da ja dann trotzdem noch die dlls nicht stimmen. Hat jemand eine Ahnung, wie ich dieses Problem beheben kann? Es kann doch nicht sein, dass ich der erste bin, der darüber stolpert. :(

Entwicklungsumgebung:
Windows XP Prof. SP2
Visual Studio .NET 2003
 
Das musst du dir doch garnicht antun. Benutz einfach gleich:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/getaddrinfo_2.asp

Parameters

nodename
[in] Pointer to a null-terminated string containing a host (node) name or a numeric host address string. The numeric host address string is a dotted-decimal IPv4 address or an IPv6 hex address.

Falls da keine vernünftige IP-Adresse drinstand, kommt eine entsprechende Fehlermeldung zurück, die du nur auswerten musst.


Zusätzlich steht da auch noch das hier:
When the AI_NUMERICHOST bit is set, the nodename parameter must contain a non-NULL numeric host address string, otherwise the EAI_NONAME error is returned. This prevents a name resolution service from being called; all information returned by the getaddrinfo function is dynamically allocated.

Also falls du ausschließlich IP-Adressen zulassen willst, kannst du das damit auch erreichen.
 
Zuletzt bearbeitet:
hi,

danke für die antwort.

Ich glaube ich habe mich nicht ganz klar ausgedrückt. Es geht mir nicht nur um IP-Adressen, sondern um das "Zerlegen" von Strings im Allgemeinen.
In PHP gibts da explode(), welches nen String und ein Trennzeichen als Eingabe bekommt und nen Array mit teilstrings zurück gibt. Sowas in der Art möchte ich für PHP haben und hab mir folgende Funktion geschrieben, die mir auch einen Array von Zeigern auf den String zurück gibt:

Code:
char ** strExplode (char * szInput, char * szDivider, int * iOutNumValues)
{
	char		* token,						// Zeiger auf aktuelles Token
			* bufferPointer = NULL,
			** index = NULL;				// Zeiger auf Array von Zeiger auf Stellen in szInput
	int		iNumValues = 0,
			iMaxValues = 0;
	
	token = strtok (szInput, szDivider);
	while (token != NULL)
	{
		if (iNumValues >= iMaxValues)
		{
			index = (char **) realloc (index, (iMaxValues+=5)*sizeof(char*));	// Speicher vergrößern
			if (index == NULL)
				break;
		}
		
		index[iNumValues++] = token;							// aktuelles Token einfügen
		
		token = strtok (NULL, szDivider);						// nächstes Token suchen
	}
	
	// NULL-Zeiger einfügen um Ende zu markieren
	if (index)
	{
		index = (char **) realloc (index, (iNumValues +1) * sizeof (char*));
		if (index != NULL) 
			index[iNumValues] = NULL;
   	 }

	* iOutNumValues = iNumValues;
	return index;
}

Ich möchte diese Funktion nun so umbauen, dass ich sie auch in mehreren Threads benutzen kann. Damit sollen alle möglichen Eingaben und Strings geprüft werden, zB. auch die Startparameter, oder die Gültigkeit eine MAC-Adresse, etc.

Ich hoffe es ist nun klarer geworden, was ich suche und es weis mir dennoch jemand Rat. :)
 
Da im Titel auch C++ steht, hier mal die C++ Variante:
Code:
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>

void explode( const std::string& text, const char* tokens, std::vector<std::string>& result)
{
	std::vector<std::string> res2;
	std::string::size_type last = 0;
	std::string::size_type temp = text.find_first_of( tokens, last);
	while( temp != std::string::npos) {
		res2.push_back( text.substr( last, temp - last));
		last = temp + 1;
		temp = text.find_first_of( tokens, last);
	}
	res2.push_back( text.substr( last, std::string::npos));
	result.swap( res2);
}

int main(int argc, char* argv[])
{
	std::string s = "192.168.100.1";
	std::vector<std::string> test;
	explode( s, ".", test);
	std::copy( test.begin(), test.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
	std::cin.get();
	return 0;
}

Übrigens, fast jede Linux Manpage sagt zu strtok :
BUGS: Never use this function.

Und strtok_r gehört nicht zum C/C++-Sprachstandard. Sie wird in POSIX.1c definiert. Von daher sollten halbwegs aktuelle Linux/UNIX-Systeme die Funktion zur Verfügung stellen, Windows-Rechner üblicherweise aber nicht.
 
Zuletzt bearbeitet:
aha, das sieht ja fast schon aus wie Java. :)

Diese Vector-Klasse kenn ich, aber mit den Strings habe ich noch nicht gespielt. Muss ich mir mal anschauen. Sieht auf jeden Fall gut aus :)

Bei strtok steht in den man-pages, dass man es nicht verwenden soll, weil die Funktion nicht re-entrant ist, kann also nur von einer Stelle aus verwendet werden und nicht abwechselnd auf unterscheidliche Zeichenketten, da sie ihre Position intern mit nem statischen Zeiger speichert. Deshalb wollte ich ja dieses strtok_r benutzen, aber du hast meinen Verdacht bestätigt, dass es das für Windows so nicht gibt. Und wenn ich diese POSIX-Libraries verwende, muss ich die auch auf jedem Rechner, auf dem ich das Programm laufen lassen möchte, installieren.

Ich werde mich also mal mit deiner Lösung befassen. Vielen Dank für die Unterstützung. :)
 
jbJOGI schrieb:
Bei strtok steht in den man-pages, dass man es nicht verwenden soll, weil die Funktion nicht re-entrant ist

Dazu kommt:
Die eingehende Zeichenkette wird verändert.
strtok kann nicht auf konstante Strings und Literale angewendet werden. (Aufgrund des erlaubten Standard-casts von const char* nach char* ist es aber möglich, sie trotzdem aufzurufen, was dann in undefiniertes Verhalten oder Abstürze resultiert)
Die Identität der Trennzeichen geht verloren.


Das finde ich schon ziemlich heikel. Außerdem mag ich persönlich das Gefrickel mit den char-Pointern nicht. Die String-Klasse solltest du dir auf jeden Fall mal genauer anschauen (es sei denn, du hast vor, Unicode zu benutzen).
 
Hallo jbJOGI,

Du kannst Deine Klasse auch mit einem Mutex schützen. Dann kannst Du in Deiner Klasse beliebige Funktionen verwenden und die Klasse ist trotzdem threadsicher.

MfG

Arnd
 
hi,

das mit den CRITICAL_SECTIONs (so heißen die Datentypen für Mutexe/Semaphoren bei Windows) habe ich bereits versucht, aber dann hätte ich die Funktion in ne Klasse stecken müssen und diese global deklarieren. Ein anderer Weg wäre gewesen, die Funktion und CRITICAL_SECTION in eine Klasse zu stecken, beide static zu machen und dann eben immer Tools::strExplode (..) aufzurufen. Problem hierbei war aber, dass die Klasse nirgends instanziiert wird und somit die CS nicht initialisiert werden kann, ohne Initialisierung kann man sie aber nicht verwenden.


Ich habe mich nun ein bisschen über Strings und Vektoren schlau gemacht und ich bereue es echt, dass ich das erst jetzt mache... so bequem!!
Ich hab deine Funktion ein wenig angepasst, da sie Probleme hatte, wenn mehrere Trennzeichen hintereinander im Eingabestring vorkamen. Aber sonst funktioniert die wunderbar. Bin grade dabei mein Programm umzubauen. :)

Code:
void explode (	const std::string& text,
		const char* tokens,
		std::vector<std::string>& result)
{
	std::string					strSub;
	std::vector<std::string>	res2;
	std::string::size_type		last = 0;
	std::string::size_type		temp = text.find_first_of (tokens, last);

	while (temp != std::string::npos)
	{
		strSub = text.substr (last, temp - last);
		if (!strSub.empty ())
			res2.push_back (strSub);
		last = temp + 1;
		temp = text.find_first_of (tokens, last);
	}
	res2.push_back (text.substr (last, std::string::npos));	
	result.swap (res2);
}

*überglücklich, dass er endlich das Problem hinter sich hat* :)

Vielen Dank nochmal für die Mühen! :)

Gruß,
jbJOGI
 
Zurück
Oben