1

I am using and understanding strtok for a while but this time it is giving unexpected error. I can't figure out what's wrong. Please help. I am using Visual Studio on Windows 10.

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char* argv[])
{
    char* filepath = "C:\\Users\\RAKESH\\source\\repos\\TESTING\\log.c";

    char* filename = strtok(filepath, "\\");

    while (filename != NULL)
    {
        filename = strtok(NULL, "\\");
    }

    printf("%s\n", filename);

    return 0;
}

1 Answers1

0

Before I posted this question, I found the error intuitively. Actually strtok needs to manipulate the char array, so it needs read-write memory space in the char array to do so, so it can't work with string literals which are typically stored in read-only memory. So the solution is:

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define FILENAME_MAX 260

int main(int argc, char* argv[])
{
    char* filepath = (char*)malloc(FILENAME_MAX * sizeof(char));
    
    strcpy(filepath, "C:\\Users\\RAKESH\\source\\repos\\TESTING\\log.c");

    char* filename = strtok(filepath, "\\");

    while (filename != NULL)
    {
        filename = strtok(NULL, "\\");
    }

    printf("%s\n", filename);
    free(filepath);
    return 0;
}

Note: This code does not give the filename but returns NULL as the filename, so to get the actual filename, the following code changes in the while loop are required:

while (filename != NULL)
{
    if (strstr(filename, ".") == NULL)
        filename = strtok(NULL, "\\");
    else
        break;
}
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 2
    Another option would be to make `filepath` an initialized array of `char` instead of a pointer to `char`: `char filepath[] = "C:\\Users\\RAKESH\\source\\repos\\TESTING\\log.c";`. Now the string literal is only being used to initialize the array `filepath` whose size is being determined automatically from the initializer. – Ian Abbott May 09 '23 at 16:25
  • 1
    *so it can't work with literal which have no memory of its own.* - It has memory of its own, but modifying it is invoking undefined behavior. Think of it as read-only. – Eugene Sh. May 09 '23 at 16:27
  • @Eugene Sh, so its a read only memory, thanks for your correction. I will update in my answer. – Rakesh Solanki May 09 '23 at 16:34
  • @Ian Abbott, yes that would make it more memory efficient. – Rakesh Solanki May 09 '23 at 16:35
  • 1
    Consider the merits of `strrchr(filepath, '\\')` to find the rightmost backslash. Beware of trailing backslashes and the root directory on a given drive. – Jonathan Leffler May 09 '23 at 16:45
  • 1
    @Rakesh Solanki, Alternative:: `strcspn()`: `size_t filename_offset(const char *path) { const char *path_original = path; size_t offset; while (path[(offset = strcspn(path, "/\\"))] != '\0') { path += offset + 1; } return (size_t) (path - path_original); } int main() { char* filepath = "C:\\Users\\RAKESH\\source\\repos\\TESTING\\log.c"; int offset = (int) filename_offset(filepath); printf("'%s'\n", filepath + offset); filepath = "just_slash\\"; offset = (int) filename_offset(filepath); printf("'%s'\n", filepath + offset); } ` – chux - Reinstate Monica May 09 '23 at 16:48
  • `if (strstr(filename, ".") == NULL)` everytime if strstr is allocating memory internally do I need to consider it for free later? – Rakesh Solanki May 09 '23 at 16:49
  • 1
    @Rakesh Solanki, A _string literal_ is not certainly in read-only memory. You first code might have worked. Yet writing into a string literal memory is UB - do not do that. – chux - Reinstate Monica May 09 '23 at 16:52
  • 1
    I am not quite fimiliar with strrchr and strcspn but now I will check out. Thanks. – Rakesh Solanki May 09 '23 at 16:52
  • 1
    @RakeshSolanki A paired use of `strcspn()` and `strspn()` mimics `strtok()`, but does to modify the string. – chux - Reinstate Monica May 09 '23 at 16:54
  • @chux-ReinstateMonica "string literal is not certainly read-only memory". Writing to literal gives undefined behavior. Also I checked `strcspn() strspn()` and looks quite useful I guess is a building block of `strtok()` – Rakesh Solanki May 09 '23 at 17:04