从 C 中的路径名中提取基本路径

发布于 2024-11-08 21:04:46 字数 804 浏览 0 评论 0原文

问题

如何从 C 中的路径名中提取基本路径?

C 语言或 C 运行时库中是否有内置函数可以从 C 中的路径名中提取基本路径?< /strong>

我问的基本上与 这个问题

注意:我更喜欢跨平台解决方案,但我在 Windows 中工作,因此如果有 Windows API 调用可以执行此操作,我仍然想知道。

示例

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

参考

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 prefer 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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

踏雪无痕 2024-11-15 21:04:46

在 Windows 上,有 _splitpath

例子

#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;
}

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;
}
披肩女神 2024-11-15 21:04:46

C 语言或 C 运行时中是否有内置函数可以从 C 中的路径名中提取基本路径?

不,没有。路径名的规则是特定于平台的,因此标准不涵盖它们。

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.

烟酉 2024-11-15 21:04:46

在 Windows 中,您可以使用 API 调用“PathRemoveFileSpec” http ://msdn.microsoft.com/en-us/library/bb773748(v=vs.85).aspx

由于不同操作系统之间的文件系统存在差异,跨平台解决方案实际上不可能实现。

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.

如若梦似彩虹 2024-11-15 21:04:46

只需从后到前循环,直到遇到第一个\

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

神回复 2024-11-15 21:04:46

WinAPI (shlwapi) PathRemoveFileSpec 应该完成所有这些操作,但 .\file 除外,它将作为 . 返回

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

迷荒 2024-11-15 21:04:46

我认为 Windows 上最好的解决方案是使用 _splitpath 正如建议的那样,在 Linux 上使用类似 basename 的内容(更多在此处)。

也就是说,由于有人已经建议实现我自己的(并且因为我在等待答案时已经完成了),所以这就是我的想法。它不是跨平台的,并且不会检查 /valid/ 路径或扩展短路径名或相对路径名。

// 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;
}

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;
}
青芜 2024-11-15 21:04:46

没有标准的 C99 函数可以执行此操作。 POSIX 有 dirname(),但在 Windows 上这对您没有多大帮助。不过,实现自己的功能对您来说应该不会太难;只需搜索字符串,查找最后一次出现的目录分隔符,并丢弃其后的任何内容。

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.

酷炫老祖宗 2024-11-15 21:04:46

str 之前是完整路径和文件名,str 之后只是路径:

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';

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';
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文