Python Master 47 hat geschrieben:Mr_Snede hat geschrieben:
OT:
wäre lustig sowas auch für Python - Ruby bzw Python - Perl zu haben.
Ich fände sowas für Pythn-C/C++ sehr gut!
Ein Python-C Spickzettel ist nicht so einfach, weil C nicht über so "fortgeschrittene" Datentypen verfügt, wie Python oder sogar PHP.
C Felder kennen zum Beispiel ihre Länge nicht, die muss man sich entweder gesondert merken, oder eine Endmarkierung benutzen, die nicht regulär im Feld vorkommen darf. Bei Zeichenketten, die eigentlich nur Felder vom Typ `char` sind, ist das zum Beispiel das Nullbyte.
Während ein Feld anlegen noch einfach ist...
...muss man für einen Test auf "enthalten sein" schon eine komplette Funktion schreiben, die das Feld, seine Grösse, das was man sucht und eine Vergleichsfunktion als Argumente entgegen nimmt, um den ``in`` Operator von Python nachzubilden:
Code: Alles auswählen
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TRUE 1
#define FALSE 0
typedef int bool;
typedef int (*cmp_func)(const void *a, const void *b);
bool in_array(const void **array,
const unsigned int array_size,
const void *needle,
const cmp_func cmp)
{
unsigned int i;
for (i=0; i < array_size; ++i) {
if (cmp(needle, array[i]) == 0) {
return TRUE;
}
}
return FALSE;
}
Benutzen kann man das dann so:
Code: Alles auswählen
if (in_array(array, 3, "bar", strcmp)) {
puts("Ist drin");
}
Das `count()` (PHP) bzw. `len()` (Python) Beispiel geht mit C Feldern so nicht, weil die wie schon gesagt, nicht "wissen" wie lang sie sind. Deshalb musste ich die Feldlänge ``3`` oben auch explizit angeben.
`join()` ist dann wegen der Speicherverwaltung, um die man sich bei C selbst kümmern muss, noch umfangreicher. Für das Ergebnis muss man soviel Speicher anfordern, das alle Zeichenketten plus den Seperatoren hineinpassen, also muss man in einem ersten Schritt ersteinmal die Längen der einzelnen Zeichenketten herausfinden. Dann kann man den Speicher anfordern und die einzelnen Zeichenketten und Seperatoren dort hin kopieren.
Code: Alles auswählen
char* str_join(const char *separator,
const char **strings,
const unsigned int array_size)
{
unsigned int i, separator_length, *lengths;
unsigned int new_size;
char *result, *ptr;
separator_length = strlen(separator);
new_size = 1 + (array_size - 1) * separator_length;
lengths = malloc(array_size * sizeof(unsigned int));
for (i=0; i < array_size; ++i) {
lengths[i] = strlen(strings[i]);
new_size += lengths[i];
}
result = malloc(new_size * sizeof(char));
ptr = result;
for (i=0; i < array_size; ++i) {
memcpy(ptr, strings[i], lengths[i]);
ptr += lengths[i];
if (i != array_size - 1) {
memcpy(ptr, separator, separator_length);
ptr += separator_length;
}
}
*ptr = '\0';
free(lengths);
return result;
}
Und man muss natürlich auf die ganzen Grenzfälle achten. Der Quelltext da oben funktioniert nicht für `array_size == 0`. Das liesse sich durch den Einsatz des ternären Operators in einer Zeile beheben, aber das überlasse ich dem Leser als Übung.
Anwendung der Funktion:
Code: Alles auswählen
char *str;
str = str_join("-", array, 3);
puts(str);
free(str);
Noch eine Implementierung für `split()` und dann höre ich auch auf, versprochen.
Code: Alles auswählen
char** str_split(const char *string,
const char *separator,
unsigned int *result_size)
{
unsigned int i, str_length, separator_length;
char **result, *str, *start, *end;
str = strdup(string);
str_length = strlen(str);
separator_length = strlen(separator);
i = 0;
result = malloc((str_length / 2) * sizeof(char**));
start = str;
while ((end = strstr(start, separator))) {
*end = '\0';
result[i++] = strdup(start);
start = end + separator_length;
}
result[i++] = strdup(start);
free(str);
result = realloc(result, i * sizeof(char**));
*result_size = i;
return result;
}
Um Grenzfälle habe ich mir jetzt auf die Schnelle keine Gedanken gemacht und benutzt wird es so:
Code: Alles auswählen
char **strings;
unsigned int i, size;
strings = str_split("foo<->bar<->frobnitz", "<->", &size);
for (i=0; i < size; ++i) {
puts(strings[i]);
/* free(strings[i]); */
}
/* free(strings); */
Man sieht, das ist nicht wirklich etwas für einen Spickzettel sondern eher für ein "Cookbook".