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';
精彩评论