开发者

Extract base path from a pathname in C

Question

How do you extract the base path from pathname in C?

Are there any functions built into the C language or the C-Runtime Library to extract the base path from a pathname in C?

I'm asking basically the opposite of this question.

NOTE: I p开发者_JAVA百科refer a cross-platform solution, but I'm working in Windows so if there is a Windows API call that does this, I'd still like to know.

Examples

Input              | Output
---------------------------------
C:\path\to\file   -> C:\path\to\
C:\path\to\file.c -> C:\path\to\
C:\file           -> C:\
.\file            -> .\
.\                -> .\
\                 -> \

References

  • Extract file name from full path in C using MSVS2005


On Windows there is _splitpath.

Example

#include <Windows.h>
#include <tchar.h>

// Use your own error codes here
#define SUCCESS                     0L
#define FAILURE_NULL_ARGUMENT       1L
#define FAILURE_API_CALL            2L
#define FAILURE_INSUFFICIENT_BUFFER 3L

DWORD GetBasePathFromPathName( LPCTSTR szPathName,
                               LPTSTR  szBasePath,
                               DWORD   dwBasePathSize )
{
  TCHAR   szDrive[_MAX_DRIVE] = { 0 };
  TCHAR   szDir[_MAX_DIR]     = { 0 };
  TCHAR   szFname[_MAX_FNAME] = { 0 };
  TCHAR   szExt[_MAX_EXT]     = { 0 };
  size_t  PathLength;
  DWORD   dwReturnCode;

  // Parameter validation
  if( szPathName == NULL || szBasePath == NULL )
  {
    return FAILURE_NULL_ARGUMENT;
  }

  // Split the path into it's components
  dwReturnCode = _tsplitpath_s( szPathName, szDrive, _MAX_DRIVE, szDir, _MAX_DIR, szFname, _MAX_FNAME, szExt, _MAX_EXT );
  if( dwReturnCode != 0 )
  {
    _ftprintf( stderr, TEXT("Error splitting path. _tsplitpath_s returned %d.\n"), dwReturnCode );
    return FAILURE_API_CALL;
  }

  // Check that the provided buffer is large enough to store the results and a terminal null character
  PathLength = _tcslen( szDrive ) + _tcslen( szDir );
  if( ( PathLength + sizeof( TCHAR ) ) > dwBasePathSize )
  {
    _ftprintf( stderr, TEXT("Insufficient buffer. Required %d. Provided: %d\n"), PathLength, dwBasePathSize );
    return FAILURE_INSUFFICIENT_BUFFER;
  }

  // Copy the szDrive and szDir into the provide buffer to form the basepath
  if( ( dwReturnCode = _tcscpy_s( szBasePath, dwBasePathSize, szDrive ) ) != 0 )
  {
    _ftprintf( stderr, TEXT("Error copying string. _tcscpy_s returned %d\n"), dwReturnCode );
    return FAILURE_API_CALL;
  }
  if( ( dwReturnCode = _tcscat_s( szBasePath, dwBasePathSize, szDir ) ) != 0 )
  {
    _ftprintf( stderr, TEXT("Error copying string. _tcscat_s returned %d\n"), dwReturnCode );
    return FAILURE_API_CALL;
  }
  return SUCCESS;
}


Are there any functions built into the C language or C-Runtime to extract the base path from a pathname in C?

No there are not. Rules for path names are platform specific and so the standard does not cover them.


In Windows you can use the API call "PathRemoveFileSpec" http://msdn.microsoft.com/en-us/library/bb773748(v=vs.85).aspx

Cross platform solutions will not really be possible due to variations in file systems bewtween different OS's.


Just loop from back to forward until you meet the first \


WinAPI (shlwapi) PathRemoveFileSpec should do all of that with the exception of .\file which would come back as .


There is no standard C99 function for doing this. POSIX has dirname(), but that won't help you much on Windows. It shouldn't be too hard for you to implement your own function, though; just search through the string, looking for the last occurrence of the directory separator, and discard anything after it.


I think the best solution on Windows is to use _splitpath as was suggested, to use something like basename on Linux (more on that here).

That said, since someone has already suggested implementing my own (and since I had already done it while I was waiting for an answer), here is what I came up with. It is not cross-platform and it does not check for /valid/ paths or expand short or relative path names.

// Retrieves the pathpath from a pathname.
//
// Returns: SUCCESS if the basepath is present and successfully copied to the p_base_path buffer
//          FAILURE_NULL_ARGUMENT if any arguments are NULL
//          FAILURE_INVALID_ARGUMENTS if either buffer size is less than 1
//          FAILURE_BUFFER_TOO_SMALL if the p_basepath buffer is too small
//          FAILURE_INVALID_PATH if the p_pathname doesn't have a path (e.g. C:, calc.exe, ?qwa)
//          FAILURE_API_CALL if there is an error from the underlying API calls
int get_base_path_from_pathname( const char*  const p_pathname,
                                 size_t             pathname_size,
                                 char* const        p_basepath,
                                 size_t             basepath_size );

int get_base_path_from_pathname( const char*  const p_pathname,
                                 size_t             pathname_size,
                                 char* const        p_basepath,
                                 size_t             basepath_size )
{
  char*  p_end_of_path;
  size_t path_length;
  int    return_code;

  // Parameter Validation
  if( p_pathname == NULL || p_basepath == NULL ) { return FAILURE_NULL_ARGUMENT; }
  if( pathname_size < 1 || basepath_size < 1 ) { return FAILURE_INVALID_ARGUMENTS; }

  // Returns a pointer to the last occurrence of \ in p_pathname or NULL if it is not found
  p_end_of_path = strrchr( p_pathname, '\\' );
  if( p_end_of_path == NULL )
  {
    // There is no path part
    return FAILURE_INVALID_PATH;
  } 
  else 
  {
    path_length = (size_t)( p_end_of_path - p_pathname + 1 );

    // Do some sanity checks on the length
    if( path_length < 1 ) { return FAILURE_INVALID_PATH; }
    if( ( path_length + 1 ) > basepath_size ) { return FAILURE_BUFFER_TOO_SMALL; }

    // Copy the base path into the out variable
    if( strncpy( p_basepath, p_pathname, path_length ) != 0 ) { return FAILURE_API_CALL; }
    p_basepath[path_length] = '\0';
  }

  return SUCCESS;
}


Before str is the full path and file name, after str is just the path:

char dir_ch = '\\'; // set dir_ch according to platform
char str[] = "C:\\path\\to\\file.c";
char *pch = &str[strlen(str)-1];

while(*pch != dir_ch) pch--;
pch++;
*pch = '\0';
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜