Say I have a const char* string like this:
../products/product_code1233213/image.jpg
I want to retrieve the second last part of this path string, which is the parent folder name of the jpg file, how would I do that?
You can use strtok.
#include <string.h>
#include <stdio.h>
int main()
{
char str[] = "/products/product_code1233213/image.jpg";
char s[2] = "/";
char *token;
/* get the first token */
token = strtok(str, s);
/* walk through other tokens */
while( token != NULL )
{
printf( " %s\n", token );
token = strtok(NULL, s);
}
return(0);
}
Output:
products
product_code1233213
image.jpg
This version works with a const char *:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
const char *s = "/products/product_code1233213/image.jpg";
const char *p = s, *begin = s, *end = s;
char *result;
size_t len;
while (p) {
p = strchr(p, '/');
if (p) {
begin = end;
end = ++p;
}
}
if (begin != end) {
len = end - begin - 1;
result = malloc(len + 1);
memcpy(result, begin, len);
result[len] = '\0';
printf("%s\n", result);
free(result);
}
return 0;
}
Using only strchr() and no backtracking. Fast and const-safe.
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SEPARATOR '/'
const char *path = "../products/product_code1233213/image.jpg";
int main(void) {
const char *beg, *end, *tmp;
beg = path;
if ((end = strchr(beg, SEPARATOR)) == NULL) {
exit(1); /* no separators */
}
while ((tmp = strchr(end + 1, SEPARATOR)) != NULL) {
beg = end + 1;
end = tmp;
}
(void) printf("%.*s\n", (int) (end - beg), beg);
return 0;
}
Related
I am trying to enhance the string splitter by splits on : char. Original version can be found at string splitter - how is it working
I do not want to use MAX_TOKEN_SIZE, I want the buffer to be just enough to hold each token. I added malloc and realloc as follows but I am getting free(): double free detected in tcache 2 error which I do not understand. How am I double freeing ? Thanks for all your help.
PS: Based on Gerhardh's comments, I modified the code as follows, but now I am getting segfault.
PS: Based on user3121023's comments, I added parenthesis around *token in 2 places and it works now.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
# define GROWBY 32
const char* splitter(const char *str, char delimiter, char **token) {
size_t i = 0;
size_t buflen = 32;
while (*str) {
if ( i == buflen) {
buflen += GROWBY;
printf("gowing buffer\n");
char *new_token = realloc(*token, buflen * sizeof **token);
if (new_token == NULL){
fprintf(stderr, "Out of Memory");
abort();
}
*token = new_token; //set the new pointer to old pointer
}
char c = *(str++);
if (c == delimiter)
break;
(*token)[i++] = c; //user3121023
}
(*token)[i] = '\0'; /* set the null terminator, user3121023 */
return str;
}
int main(){
const char *env =
"/bin/svsgerertegdfyufdujhdcjxbcn:/sbin:::/usr/bin/46526vw67vxshgshnxxcxhcgbxhcbxn";
while (*env){
char *token = malloc(GROWBY * sizeof(char));
env = splitter(env, ':', &token);
if (token[0] == '\0') {
strcpy(token, "./");
}
printf("%s\n", token) ;
free(token);
}
return 0;
}
Try using strcspn to advance to the next delimiter.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
const char* splitter(const char *str, char *delimiter, char **token) {
size_t buflen = 0;
size_t extra = 0;
buflen = strcspn ( str, delimiter); // characters to next delimiter
extra = 1;
if ( ! buflen) {
extra = 3; // need space for "./" copy in main
}
char *new_token = realloc(*token, ( buflen + extra) * sizeof **token);
if (new_token == NULL){
fprintf(stderr, "Out of Memory");
abort();
}
*token = new_token; //set the new pointer to old pointer
strncpy ( *token, str, buflen);
(*token)[buflen] = 0;
str += buflen;
if ( *str) {
str += 1;
}
return str;
}
int main(void){
const char *env =
"/bin/svsgerertegdfyufdujhdcjxbcn:/sbin:::/usr/bin/46526vw67vxshgshnxxcxhcgbxhcbxn";
while (*env){
char *token = NULL;
env = splitter(env, ":", &token); // note " instead of '
if (token[0] == '\0') {
strcpy(token, "./");
}
printf("%s\n", token) ;
free(token);
}
return 0;
}
Here two prgrams present. Program 1 and program 2.
Input to both programs: {(0,0),(25,25)}
Output is : (0,0),(25,25)
The program -1 is working fine, giving out put as expected. Program 2 is not giving out put as expected bot no errors.
Suspecting problem at *str2 = *token in program -2.But what the problem is not identified exactly. can any one explain me
Program --1
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <malloc.h>
char* fun_call(char [], int );
int main()
{
char str[] = "{(0,0),(25,25)}";
int base_str_leng;
char *retun_ptr;
base_str_leng = sizeof(str)-1;
printf("base_str_lemg:%d\n",base_str_leng);
retun_ptr = fun_call(str, base_str_leng);
printf("str2 in main loop:::%s\n",retun_ptr);
return 0;
}
char* fun_call(char str1[], int len)
{
printf("len::%d\n", len);
len = len-2;
printf("len=%d\n",len);
char *token = str1;
char *str2 = (char*) malloc( len * sizeof(char) );
printf("sizeof(str2) = %zd\n",malloc_usable_size(str2));
printf("token:%s\n", token);
printf("****While loop started****\n");
int i=0,j=0;
while(token[i] != '\0')
{
if(token[i] != '{' && token[i] != '}' )
{
printf("Each token:%c\n", token[i]);
str2[j] = token[i];
i++;
j++;
printf("str2_Token::%c\n", str2[j]);
}
else
{
i++;
}
}
printf("Function str2::%s\n", str2);
return (str2);
}
Program: 2
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <malloc.h>
char* fun_call(char [], int );
int main()
{
char str[] = "{(0,0),(25,25)}";
int base_str_leng;
char *retun_ptr;
base_str_leng = sizeof(str)-1;
printf("base_str_lemg:%d\n",base_str_leng);
retun_ptr = fun_call(str, base_str_leng);
printf("str2 in main loop:::%s\n",retun_ptr);
return 0;
}
char* fun_call(char str1[], int len)
{
printf("len::%d\n", len);
len = len-2;
printf("len=%d\n",len);
char *token = str1;
char *str2 = (char*) malloc( len * sizeof(char) );
printf("sizeof(str2) = %zd\n",malloc_usable_size(str2));
printf("token:%s\n", token);
printf("****While loop started****\n");
while(*token != '\0')
{
if(*token != '{' && *token != '}' )
{
// printf("Each token:%c\n", token[i]);
*str2 = *token;
str2++;
token++;
// printf("str2_Token::%c\n",*str2);
}
else
{
token++;
}
}
printf("Function str2::%s\n", str2);
return (str2);
}
In your program 2 you do not end the string with \0, your while loop ends when token[i] == '\0'
You could use calloc instead to initialize the memory to 0
char *str2 = calloc( len, sizeof(char) );
In the code below, in "parse" function I am trying to get substring from the string "line". I am successfully printing the "method" variable, but "requesttarget" and "httpversion" variables are empty for some reason.
(ps all these printf's are also inside my parse function)
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
//prototypes
bool parse(const char* line, char* abs_path, char* query);
int strindex(char** pos, const char* str);
void substr(int start, int end, char* holder, const char* line);
int main(void)
{
const char* line = "GET /hello.php?name=Alice HTTP/1.1";
char* abs_path = NULL;
char* query = NULL;
if(parse(line, abs_path, query))
{
printf("It works!\n");
}
}
bool parse(const char* line, char* abs_path, char* query)
{
char* space;
int firstspace;
int secondspace;
char* method = malloc(50 * sizeof(char));
char* requesttarget = malloc(50 * sizeof(char));
char* httpversion = malloc(50 * sizeof(char));
space = strchr(line, ' ');
printf("%p\n", space);
//checks if strchr returns
if(space == NULL)
{
return false;
}
//index in INT of the character
firstspace = strindex(&space, line);
printf("%i\n", firstspace);
//stores the method
substr(0, firstspace, method, line);
space = strrchr(line, ' ');
printf("%p\n", space);
//index in INT of the character
secondspace = strindex(&space, line);
printf("%i\n", secondspace);
//checks if strchr returns
if(space == NULL)
{
return false;
}
//firstspace should come before secondspace
if(firstspace > secondspace)
{
return false;
}
//stores request - target
substr(firstspace + 1, secondspace, requesttarget, line);
//stores http-version
substr(secondspace + 1, strlen(line), httpversion, line);
printf("method: %s\n", method);
printf("requesttarget: %s\n", requesttarget);
printf("httpversion: %s\n", httpversion);
return true;
}
int strindex(char** pos, const char* str)
{
for(int i = 0, n = strlen(str); i < n; i++)
{
if((str + i) == *pos)
{
return i;
}
}
return -1;
}
void substr(int start, int end, char* holder, const char* line)
{
//char* holder = malloc(50 * sizeof(char));
int i = start;
for(; i < end; i++)
{
holder[i] = line[i];
}
holder[i] = '\0';
//return holder;
}
void substr(int start, int end, char* holder, const char* line)
{
//char* holder = malloc(50 * sizeof(char));
int i = start, j=0;
for(; i < end; i++)
{
holder[j++] = line[i];
}
holder[j] = '\0';
//return holder;
}
you were not storing data in holder from 2nd iteration properly.
from 2nd iteration start = 3 and end = 25. While storing in holder your index starts from 3, which is correct for line but not for holder.
Add one more variable to start the index for holder from 0.
A smaller version: (untested)
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
const char* line = "GET /hello.php?name=Alice HTTP/1.1";
char method[32], request[1024], version[32], *src, *dest, *end;
for(src=line, end=(dest=method)+sizeof(method)-1 ; *src!='\0' && *src!=' ' && dest<end; src++, dest++) *dest=*src;
*dest='\0';
while(*src==' ') src++;
for(end=(dest=request)+sizeof(request)-1 ; *src!='\0' && *src!=' ' && dest<end; src++, dest++) *dest=*src;
*dest='\0';
while(*src==' ') src++;
for(end=(dest=version)+sizeof(version)-1 ; *src!='\0' && *src!=' ' && dest<end; src++, dest++) *dest=*src;
*dest='\0';
printf("method: %s\n", method);
printf("requesttarget: %s\n", request);
printf("httpversion: %s\n", version);
}
I have a string in my program where in which it need to be altered with another string value before a "/".
Source String : qos-tree/output_rate
Target String : qos-tree-2/output_rate
#include <stdio.h>
#include <string.h>
void append(char* s, char c)
{
int len = strlen(s);
s[len] = c;
s[len+1] = '\0';
}
int main(void)
{
char str[256] = "qos-tree/output_rate";
char c = "a";
append(str, c);
printf("%s\n", str);
return 0;
}
This is what i have done so far,I think the logic is wrong here.Can anyone guide me to correct it?
Once the execution is completed the source string should have a "-2" before the "/"
void insert_before_ch(char *s, const char *ins, char c){
char *p = strchr(s, c);
if(p){
size_t len = strlen(ins);
memmove(p + len, p, strlen(p)+1);
memcpy(p, ins, len);
}
}
int main(void){
char str[256] = "qos-tree/output_rate";
insert_before_ch(str, "-2", '/');
printf("%s\n", str);
return 0;
}
In your attempt, you don't look for a slash and I do not see any "-2" anywhere.
Try this instead:
#include <stdio.h>
#include <string.h>
void append(char* s, char del, char* substring) {
char origin[256];
strcpy(origin, s);
int i = 0, j = 0, z = 0;
for(; origin[i]; ++i) {
if(origin[i] != del) {
s[j++] = origin[i];
} else {
for(; substring[z]; ++z) {
s[j++] = substring[z];
}
s[j++] = origin[i];
}
}
s[j] = '\0';
}
int main(void) {
char str[256] = "qos-tree/output_rate";
char del = '/';
char* substring = "-2";
append(str, del, substring);
printf("%s\n", str);
return 0;
}
The logic is that inside the function we use origin array to remember the actual contents of the array and then we copy from origin to s (which is the actual array of main()). If we find our delimiter del, then we copy the substring in that position and continuing with copying.
Note that the length of the array should be enough to store the resulted string. In this case, it is.
I think you should make your function work with dynamic allocation, because inserting characters into the string will make the resulting string larger, so this is my suggestion
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void insert(char **str, char chr, unsigned int position)
{
int length;
char *ptr;
if (str == NULL)
return;
length = strlen(*str);
if (position >= length)
return;
ptr = realloc(*str, 2 + length);
if (ptr == NULL)
return;
*str = ptr;
memmove(ptr + position + 1, ptr + position, length - position + 1);
ptr[position] = chr;
}
int main(void)
{
const char *source = "qos-tree/output_rate";
size_t length = strlen(source);
char *str = malloc(1 + length);
if (str == NULL)
return -1;
strcpy(str, source);
insert(&str, '-', 8);
insert(&str, '2', 9);
printf("%s\n", str);
free(str);
return 0;
}
first of all thist char c = "a" should be replace with this char c = 'a'; because c is a character not a string
as for your problem I didn't realy see the relation between what your code is doing with what you said you wanted to do , but here a piece of code to achieve what , I think , you want to do :
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
void append(char* str , char c)
{
char firststr[60];
char therest[30];
strcpy(firststr , strtok(str , "/"));
strcpy(therest , strtok(NULL , "/"));
strcat(firststr , &c);
strcat(firststr , "/");
strcat(firststr , therest);
strcpy(str , firststr);
}
int main(void)
{
char str[60] = "qos-tree/output_rate";
char c = '2';
append(str , c);
printf("%s\n" , str);
}
there you go I think this is what you wanted to do you can modify the array sizes to fit your needs
Is there a better of parsing the below string instead of doing a strtok() to get each field.
"subject=what&cc=bose#yahoo.com&server=smtp.yahoo.com:8000"
Basically I want to retrieve the value for each field into another char buf's.
Here is my code. Just wanted to know if there is any other better way of doing it (any better string parsing algos)
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#define SUBJECT "subject="
#define CC_LIST "cc="
#define SERVER "server="
static void
get_value (const char *tok, char **rval_buf, size_t field_len)
{
size_t val_size = 0;
if (!tok || !rval_buf)
return;
val_size = strlen(tok + field_len) + 1;
*rval_buf = calloc(1, val_size);
if (*rval_buf) {
strlcpy(*rval_buf, tok + field_len, val_size);
}
}
int
main (int argc, char **argv)
{
/* hard coded buf for testing */
char buf[] = "subject=what&cc=bose#yahoo.com&server=smtp.yahoo.com:8000";
char *subject_text = NULL;
char *cc_list = NULL;
char *server_addr = NULL;
char *tok = NULL;
int field_len = 0;
int val_len = 0;
tok = strtok(buf, "&");
while(tok) {
/*
* Handle the token
*/
/* check if it is subject */
if (strstr(tok, SUBJECT)) {
get_value(tok, &subject_text, strlen(SUBJECT));
} else if (strstr(tok, CC_LIST)) { /* check if it is CC */
get_value(tok, &cc_list, strlen(CC_LIST));
} else if (strstr(tok, SERVER)) { /* check if it is server */
get_value(tok, &server_addr, strlen(SERVER));
}
tok = strtok(NULL, "&");
}
/* dump data */
fprintf(stdout, "\nSUBJECT: \"%s\"\nCC_LIST: \"%s\"\nSERVER: \"%s\" \n\n",
subject_text, cc_list, server_addr);
return EXIT_SUCCESS;
}
strstr searches for one string ("the needle") inside another ("the haystack"), but you really only want to know whether the needle is the beginning of the haystack.
Here's a small suggestion: (requires #include <stdbool> or change the booleans to ints. I like bools.)
static bool
getval(const char* haystack, const char** res, const char* needle, size_t len) {
if (haystack && 0 == strncmp(haystack, needle, len)) {
*res = strdup(haystack + len);
return true;
}
return false;
}
and later:
for (tok = strtok(buf, "&"); tok; tok = strtok(NULL, "&")) {
getval(tok, &subject_text, SUBJECT, strlen(SUBJECT)) ||
getval(tok, &cc_list, CC_LIST, strlen(CC_LIST)) ||
getval(tok, &server_addr, SERVER, strlen(SERVER));
}
You can actually get away with doing the strlen inside of getval, which cuts down a lot on the noise, because most modern compilers are clever enough to inline getval and constant-fold the length of a constant string.
Use strtok()
char *strtok(char *str, const char *delim)
You can put '&' as a delimeter
I wrote a quick-n-dirty splitter for you:
int split(char* input, char delim, char*** parts)
{
int count = 1;
char** result;
char* t = input;
while(*t != '\0')
{
if (*t++ == delim)
{
count++;
}
}
result = (char**)malloc(count * sizeof(char*));
t = input;
int i = 0;
result[i] = input;
while(*t != '\0')
{
if (*t == delim)
{
*t = '\0';
result[++i] = ++t;
}
else
{
t++;
}
}
*parts = result;
return count;
}
int main()
{
char raw[] = "subject=\"some text\"&cc=abcd&server=acd.com";
char* str = _strdup(raw);
char** parts;
char** keyval;
int cnt = split(str, '&', &parts);
for(int i=0; i<cnt; ++i)
{
split(parts[i], '=', &keyval);
printf("[%d]: %s <--> %s\n", i, keyval[0], keyval[1]);
free(keyval);
}
free(parts);
getchar();
return 0;
}
Output
[0]: subject <--> "some text"
[1]: cc <--> abcd
[2]: server <--> acd.com