views:

658

answers:

5

I'm not very good at C, and I always get stuck on simple string manipulation tasks (that's why I love Perl!).

I have a string that contains a file path like "/Volumes/Media/Music/Arcade Fire/Black Mirror.aac". I need to extract the drive name ("Media" or preferably "/Volumes/Media") from that path.

Any help would be greatly appreciated, just as I try to return the favor on the Perl questions!

  • Jim
+1  A: 

I think that you need to be a little more exact in the specification of your problem.

When you say that you want to extract "Media", do you mean everything between the second and third '/' character, or is there a more complex heuristic at work?

Also, is the string in a buffer that's suitable to be modified?

Typically the way to do this would be to use strchr or strstr one or more times to find a pointer to where you want to extract the substring from (say p), and a pointer to the character after the last character that you need to extract (say q), if the buffer is a temporary buffer that you don't mind destroying then you can just do *q = 0 and p will be a pointer to the required string. Otherwise you need to have a buffer of at least q - p + 1 chars ( +1 is to include space for the null terminator as well as the q - p interesting characters. e.g. char *buffer = malloc(q - p + 1); ) and you can extract the string with memcpy. e.g. memcpy(buffer, p, q - p + 1).

Charles Bailey
+1  A: 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char * extractDriveName(const char *path, char separator, int maxLen)
{
    char *outBuffer;
    int outBufferSize, i, j;
    int sepOcur;

    outBufferSize = strlen(path) + 1;
    outBufferSize = outBufferSize > maxLen ? maxLen : outBufferSize;

    outBuffer = (char *) malloc(outBufferSize);

    // Error allocating memory.
    if(outBuffer == NULL)
     return NULL;

    memset(outBuffer, 0, outBufferSize);

    for(i = 0, sepOcur = 0, j = 0; i < outBufferSize; i++)
    {
     if(path[i] == separator)
      sepOcur ++;

     if(sepOcur >= 0 && sepOcur < 3)
      outBuffer[j++] = path[i];
     else if(sepOcur == 3)
      break;  
    }

    // Don't forget to free the buffer if 
    return outBuffer;   
}

int main(void)
{
    char path [] = "/Volumes/Media/Music/Arcade Fire/Black Mirror.aac";

    char * driveName = extractDriveName(path, '/', strlen(path) + 1);

    if(driveName != NULL)
    {
     printf("Path location: '%s'\n", path);
     printf("Drive name: '%s'\n", driveName);
     free(driveName);
    }
    else
    {
     printf("Error allocating memory\n");
    }
}

Output:

Path location: '/Volumes/Media/Music/Arcade Fire/Black Mirror.aac'
Drive name: '/Volumes/Media'
Fernando Miguélez
+3  A: 

I think sscanf could be appropriate:

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

void test(char const* path) {
    int len;
    if(sscanf(path, "/Volumes/%*[^/]%n", &len) != EOF) {
        char *drive = malloc(len + 1);
        strncpy(drive, path, len);
        drive[len] = '\0';

        printf("drive is %s\n", drive);
        free(drive);
    } else {
        printf("match failure\n");
    }
}

int main() {
    test("/Volumes/Media/Foo");
    test("/Volumes/Media");
    test("/Volumes");
}

Output:

drive is /Volumes/Media
drive is /Volumes/Media
match failure
Johannes Schaub - litb
Thanks - that was simple, educational, and it worked!
jimtut
+1  A: 

I think that the easiest way is to use strtok(). This function will split a string in tokens separated by one of the characters in the separators string.

If your original path is in str and you want the second part in part:

strtok(str, "/");        /* To get the first token */
part=strtok(NULL, "/");  /* To get the second token that you want */

Note that strtok() will change str, so it should not be a const. If you have a const string, you might use stdrup(), which is not standard but normally available, to create a copy.

Also note that strtok() is not thread safe.

ahy1
strtok is not threadsafe, and is largely deprecated. Use strtok_r or strsep instead. Cheers.
dmckee
True that strtok() is not thread safe. I've added a note about that in the answer. I didn't know that strtok() is deprecated, and I also didn't know strtok_r() is part of standard C, but I don't have a copy of the official standard to look it up.
ahy1
Uh. Perhaps deprecated is to strong a word. Maybe "use of strtok is largely discouraged"... I believe that strsep is the C90 standard replacement for strtok (and the Mac OS X manpage seems to confirm this). Cheers.
dmckee
strtok is standard C, strtok_r is from POSIX.1, strsep was introduced in 4.3BSD
Christoph
A: 

You example makes it appear that you are working in a Macintosh environment. I suspect that there is an Apple API to get the volume on which a particular file resides.

Any reason not to use that?

Edit: Looking at your profile, I suspect I guessed wrong about your environment. I can't help you for windows. Good luck. I'll leave this here in case anyone is looking for the same answer on a Mac.


From Mac Forums I find "Getting the current working Volume name?" which seems to be the same question. There is a nice discussion there, but they dpn't seem to come to a single answer.

Google is your friend.


Another possibility: BSD Path to Volume Name & Vice-Versa at CocoaDev Forums.

dmckee
This is on a Mac, but the string-manipulation question is fairly generic, and I assume it will help others as it's helping me!
jimtut