String before and after delimiter

Permalänk

String before and after delimiter

Skriver på två funktioner och får en känsla av det finns ett enklare sätt. Då menar inte jag att skriva mer kompakt.
Funktionerna ska alltså returnera en pekare till sträng som jag kan använda mig av i ex. printf() eller i strcpy() funktioner.
Originalsträngen får ej modifieras.

Jag tycker det är omåttligt fult alt ha den där malloc() där men jag måste ju få dit '\0' och behöver därför en kopia eftersom originalsträngen ej får moddas.

Förslag?

edit: Jag ser nu att *temp ska vara static.

/*****************************************************************/ char* after_delimiter(char *str, char del) { return strchr(str,del)+1; } //********************************************************************** char* before_delimiter(char *str, char del) { char *temp, *p; temp=malloc(strlen(str)+1); strcpy(temp, str); p=strchr(temp,del); *p='\0'; return temp; }

Permalänk
Medlem

Om du vet maxlängden på strängen kan du använda en stackallokerad buffer, men i grund och botten tror jag inte att det blir så mycket enklare än vad du gör.

Det är för övrigt missar som denna — null-terminerade strängar — i C som gör att Rust kan prestera bättre i flera situationer, om man inte typ skriver sin egen alternativa sträng-implementation och sånt bös.

Visa signatur

Arbets- / Spelstation: Arch Linux - Ryzen 5 3600 - RX 7900 XT - 32G DDR4
Server: Arch Linux - Core i5-10400F - 16G DDR4

Permalänk
Skrivet av Bryal:

Om du vet maxlängden på strängen kan du använda en stackallokerad buffer, men i grund och botten tror jag inte att det blir så mycket enklare än vad du gör.

Det är för övrigt missar som denna — null-terminerade strängar — i C som gör att Rust kan prestera bättre i flera situationer, om man inte typ skriver sin egen alternativa sträng-implementation och sånt bös.

Ok. Funktionerna är kanske minimala nog som de är. Måste läsa på om Rust hör jag....

Permalänk
Medlem
Skrivet av Sweedland:

Ok. Funktionerna är kanske minimala nog som de är.

Såg förresten en liten förbättring till din kod nu när jag kom hem till datorn. Du allokerar onödigt mycket minne för resultat-strängen i before_delimiter. Du kan söka upp delimitern först, räkna hur lång prefix-strängen är, och bara allokera och kopiera över så många tecken som behövs:

char* before_delimiter(const char* str, char del) { const char* p = strchr(str, del); const size_t i = (size_t)(p - str); char* new = malloc(i + 1); strncpy(new, str, i); new[i] = 0; return new; }

Skrivet av Sweedland:

Måste läsa på om Rust hör jag....

Jag tycker Rust är ett väldigt trevligt språk. Det trevligaste jämfört med C tycker jag är det avancerade typsystemet med bl.a. generics/polymorfism. Speciellt polymorfism gör det så mycket enklare att återanvända datastrukturer för olika typer, istället för att ad-hoc behöva återuppfinna hjulet varje gång man vill, säg, lägga sin egna datatyp i ett hash-träd utan att behöva lägga allting bakom massa indirektion med void-pekare. C går nog alltid att göra snabbare än Rust om man är villig att lägga ned jobbet, men i min erfarenhet känns det som att man med Rust når 90% av prestandan av optimal C, med 30% av koden. Och detta är ju då jämfört med just optimal C, vilket man sällan orkar ta fram.

Visa signatur

Arbets- / Spelstation: Arch Linux - Ryzen 5 3600 - RX 7900 XT - 32G DDR4
Server: Arch Linux - Core i5-10400F - 16G DDR4

Permalänk

Tror tyvärr att det behövs mer kod än mindre kod. Felsituationer kan inträffa. Det kan t ex vara så att avgränsaren del inte finns i strängen str och då fås en NULL-pekare som man inte ska använda.

Ett gissel med dynamisk minnesallokering är att man kan få minnesläckage om man inte frigör det minne man allokerat. I mitt kodexempel med realloc() nedan blir det också ett minnesläckage eftersom det inte anropar free() men läckaget är begränsat till längden på den längsta strängen.

Båda funktionerna returnerar NULL om något gått fel.

/*****************************************************************/ char* after_delimiter(char *str, char del) { char *after; if ((after = strchr(str, del))) ++after; return after; } //********************************************************************** char* before_delimiter(char *str, char del) { static char *before; char *tmp; if ((before = (char *) realloc(before, strlen(str)+1))) { strcpy(before, str); if ((tmp = strchr(before, del))) *tmp = '\0'; else before = NULL; } return before; }

Visa signatur

// Lars Lindehaven

Permalänk
Skrivet av Bryal:

Såg förresten en liten förbättring till din kod nu när jag kom hem till datorn. Du allokerar onödigt mycket minne för resultat-strängen i before_delimiter. Du kan söka upp delimitern först, räkna hur lång prefix-strängen är, och bara allokera och kopiera över så många tecken som behövs:

char* before_delimiter(const char* str, char del) { const char* p = strchr(str, del); const size_t i = (size_t)(p - str); char* new = malloc(i + 1); strncpy(new, str, i); new[i] = 0; return new; }

Jag tycker Rust är ett väldigt trevligt språk. Det trevligaste jämfört med C tycker jag är det avancerade typsystemet med bl.a. generics/polymorfism. Speciellt polymorfism gör det så mycket enklare att återanvända datastrukturer för olika typer, istället för att ad-hoc behöva återuppfinna hjulet varje gång man vill, säg, lägga sin egna datatyp i ett hash-träd utan att behöva lägga allting bakom massa indirektion med void-pekare. C går nog alltid att göra snabbare än Rust om man är villig att lägga ned jobbet, men i min erfarenhet känns det som att man med Rust når 90% av prestandan av optimal C, med 30% av koden. Och detta är ju då jämfört med just optimal C, vilket man sällan orkar ta fram.

Den förbättringen är en klar förbättring. Sen hade jag ju gärna velat ha en begränsning i hur mkt minne malloc använder. Typ procent av total mängd minne. Men mig veterligen finns inget "std-C" funktion som returnerar datorns totala mängd minne. Skulle gärna velat ha typ att malloc kan max allokera 0.5% av tillgängligt minne.

Jag håller med om att C inte är speciellt kraftfullt språk. Det krävs en hel del plock med kod och återanvändning. Dock arbetar jag mest med microcontrollers och ibland Linux enkortsdatorer. I dessa använder jag delar av ett funktionsbibliotek. Jag vet inte till vilka miljöer RUst finns tillgängligt.

Permalänk
Skrivet av Lindehaven:

Tror tyvärr att det behövs mer kod än mindre kod. Felsituationer kan inträffa. Det kan t ex vara så att avgränsaren del inte finns i strängen str och då fås en NULL-pekare som man inte ska använda.

Ett gissel med dynamisk minnesallokering är att man kan få minnesläckage om man inte frigör det minne man allokerat. I mitt kodexempel med realloc() nedan blir det också ett minnesläckage eftersom det inte anropar free() men läckaget är begränsat till längden på den längsta strängen.

Båda funktionerna returnerar NULL om något gått fel.

/*****************************************************************/ char* after_delimiter(char *str, char del) { char *after; if ((after = strchr(str, del))) ++after; return after; } //********************************************************************** char* before_delimiter(char *str, char del) { static char *before; char *tmp; if ((before = (char *) realloc(before, strlen(str)+1))) { strcpy(before, str); if ((tmp = strchr(before, del))) *tmp = '\0'; else before = NULL; } return before; }

Ja. Det behövs ofta mer kod än vad man önskar. Mkt felhantering. NULL ska ju tas om hand av anropande funktion. En hel del plock med det där.
Varför använda realloc()? (inte jobbar så mkt med dessa minnesallokerare)

Permalänk

realloc() förändrar storleken på det minne som redan allokerats. malloc() allokerar minne på nytt.

Visa signatur

// Lars Lindehaven

Permalänk
Skrivet av Lindehaven:

realloc() förändrar storleken på det minne som redan allokerats. malloc() allokerar minne på nytt.

Jo. Realloc fattar jag. Men du allokerar en area som inte finns eller? Pekaren *before är väl bara en variabel.
I detta fall bör realloc uppföra sig som malloc.

Permalänk

@Sweedland: Ja, om minnet inte allokerats innan så beter sig realloc() som malloc().

Visa signatur

// Lars Lindehaven

Permalänk

Egentligen borde anropande funktion skicka med en pekare till ett utrymme som denne har gjort iordning för resultatsträngen. Det är mer tydligt.