鱼C论坛

 找回密码
 立即注册
查看: 874|回复: 8

[已解决]C 有没有路径处理方面的内置库或三方库(GCC/MINGW/LINUX/WINDOWS)

[复制链接]
发表于 2021-11-25 13:59:13 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 hrpzcf 于 2021-11-25 14:39 编辑

路径分割:路径,文件名,扩展名
路径处理:获取上一级路径,等等

为了有动力学习C,用C实现一个以前用Python写的小工具,才发现什么都要自己写,轮子都不知道要怎么找,太累了

最佳答案
2021-11-26 21:32:32
C语言的我没找到,C++可以用boost.filesystem(在c++17中被收入标准库)
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2021-11-25 14:09:43 From FishC Mobile | 显示全部楼层
这些都是自己写的范畴吧,哪来现成的库
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2021-11-25 14:33:28 | 显示全部楼层
wp231957 发表于 2021-11-25 14:09
这些都是自己写的范畴吧,哪来现成的库

自己写也不是不行,就是写完可能还要不少测试来发现问题
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2021-11-25 23:48:24 | 显示全部楼层
up
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2021-11-26 02:41:49 | 显示全部楼层
hrpzcf 发表于 2021-11-25 14:33
自己写也不是不行,就是写完可能还要不少测试来发现问题

把python的os.path的几个相应函数的源码拿来改改不就好了~~
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-11-26 21:32:32 | 显示全部楼层    本楼为最佳答案   
C语言的我没找到,C++可以用boost.filesystem(在c++17中被收入标准库)
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2021-11-27 09:26:36 | 显示全部楼层
lightninng 发表于 2021-11-26 02:41
把python的os.path的几个相应函数的源码拿来改改不就好了~~

os.path的具体实现好像是在C源码里的,说实话我在Python项目源代码包找了半天没找到在哪,所以我还是自己写了,linux路径处理还好,windows的就很麻烦,各种出乎意料的路径
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-11-27 10:09:32 | 显示全部楼层
hrpzcf 发表于 2021-11-27 09:26
os.path的具体实现好像是在C源码里的,说实话我在Python项目源代码包找了半天没找到在哪,所以我还是自己 ...

我这边的路径是:
  1. /usr/lib/python3.9/posixpath.py
复制代码

  1. """Common operations on Posix pathnames.

  2. Instead of importing this module directly, import os and refer to
  3. this module as os.path.  The "os.path" name is an alias for this
  4. module on Posix systems; on other systems (e.g. Windows),
  5. os.path provides the same operations in a manner specific to that
  6. platform, and is an alias to another module (e.g. ntpath).

  7. Some of this can actually be useful on non-Posix systems too, e.g.
  8. for manipulation of the pathname component of URLs.
  9. """

  10. # Strings representing various path-related bits and pieces.
  11. # These are primarily for export; internally, they are hardcoded.
  12. # Should be set before imports for resolving cyclic dependency.
  13. curdir = '.'
  14. pardir = '..'
  15. extsep = '.'
  16. sep = '/'
  17. pathsep = ':'
  18. defpath = '/bin:/usr/bin'
  19. altsep = None
  20. devnull = '/dev/null'

  21. import os
  22. import sys
  23. import stat
  24. import genericpath
  25. from genericpath import *

  26. __all__ = ["normcase","isabs","join","splitdrive","split","splitext",
  27.            "basename","dirname","commonprefix","getsize","getmtime",
  28.            "getatime","getctime","islink","exists","lexists","isdir","isfile",
  29.            "ismount", "expanduser","expandvars","normpath","abspath",
  30.            "samefile","sameopenfile","samestat",
  31.            "curdir","pardir","sep","pathsep","defpath","altsep","extsep",
  32.            "devnull","realpath","supports_unicode_filenames","relpath",
  33.            "commonpath"]


  34. def _get_sep(path):
  35.     if isinstance(path, bytes):
  36.         return b'/'
  37.     else:
  38.         return '/'

  39. # Normalize the case of a pathname.  Trivial in Posix, string.lower on Mac.
  40. # On MS-DOS this may also turn slashes into backslashes; however, other
  41. # normalizations (such as optimizing '../' away) are not allowed
  42. # (another function should be defined to do that).

  43. def normcase(s):
  44.     """Normalize case of pathname.  Has no effect under Posix"""
  45.     return os.fspath(s)


  46. # Return whether a path is absolute.
  47. # Trivial in Posix, harder on the Mac or MS-DOS.

  48. def isabs(s):
  49.     """Test whether a path is absolute"""
  50.     s = os.fspath(s)
  51.     sep = _get_sep(s)
  52.     return s.startswith(sep)


  53. # Join pathnames.
  54. # Ignore the previous parts if a part is absolute.
  55. # Insert a '/' unless the first part is empty or already ends in '/'.

  56. def join(a, *p):
  57.     """Join two or more pathname components, inserting '/' as needed.
  58.     If any component is an absolute path, all previous path components
  59.     will be discarded.  An empty last part will result in a path that
  60.     ends with a separator."""
  61.     a = os.fspath(a)
  62.     sep = _get_sep(a)
  63.     path = a
  64.     try:
  65.         if not p:
  66.             path[:0] + sep  #23780: Ensure compatible data type even if p is null.
  67.         for b in map(os.fspath, p):
  68.             if b.startswith(sep):
  69.                 path = b
  70.             elif not path or path.endswith(sep):
  71.                 path += b
  72.             else:
  73.                 path += sep + b
  74.     except (TypeError, AttributeError, BytesWarning):
  75.         genericpath._check_arg_types('join', a, *p)
  76.         raise
  77.     return path


  78. # Split a path in head (everything up to the last '/') and tail (the
  79. # rest).  If the path ends in '/', tail will be empty.  If there is no
  80. # '/' in the path, head  will be empty.
  81. # Trailing '/'es are stripped from head unless it is the root.

  82. def split(p):
  83.     """Split a pathname.  Returns tuple "(head, tail)" where "tail" is
  84.     everything after the final slash.  Either part may be empty."""
  85.     p = os.fspath(p)
  86.     sep = _get_sep(p)
  87.     i = p.rfind(sep) + 1
  88.     head, tail = p[:i], p[i:]
  89.     if head and head != sep*len(head):
  90.         head = head.rstrip(sep)
  91.     return head, tail


  92. # Split a path in root and extension.
  93. # The extension is everything starting at the last dot in the last
  94. # pathname component; the root is everything before that.
  95. # It is always true that root + ext == p.

  96. def splitext(p):
  97.     p = os.fspath(p)
  98.     if isinstance(p, bytes):
  99.         sep = b'/'
  100.         extsep = b'.'
  101.     else:
  102.         sep = '/'
  103.         extsep = '.'
  104.     return genericpath._splitext(p, sep, None, extsep)
  105. splitext.__doc__ = genericpath._splitext.__doc__

  106. # Split a pathname into a drive specification and the rest of the
  107. # path.  Useful on DOS/Windows/NT; on Unix, the drive is always empty.

  108. def splitdrive(p):
  109.     """Split a pathname into drive and path. On Posix, drive is always
  110.     empty."""
  111.     p = os.fspath(p)
  112.     return p[:0], p


  113. # Return the tail (basename) part of a path, same as split(path)[1].

  114. def basename(p):
  115.     """Returns the final component of a pathname"""
  116.     p = os.fspath(p)
  117.     sep = _get_sep(p)
  118.     i = p.rfind(sep) + 1
  119.     return p[i:]


  120. # Return the head (dirname) part of a path, same as split(path)[0].

  121. def dirname(p):
  122.     """Returns the directory component of a pathname"""
  123.     p = os.fspath(p)
  124.     sep = _get_sep(p)
  125.     i = p.rfind(sep) + 1
  126.     head = p[:i]
  127.     if head and head != sep*len(head):
  128.         head = head.rstrip(sep)
  129.     return head


  130. # Is a path a symbolic link?
  131. # This will always return false on systems where os.lstat doesn't exist.

  132. def islink(path):
  133.     """Test whether a path is a symbolic link"""
  134.     try:
  135.         st = os.lstat(path)
  136.     except (OSError, ValueError, AttributeError):
  137.         return False
  138.     return stat.S_ISLNK(st.st_mode)

  139. # Being true for dangling symbolic links is also useful.

  140. def lexists(path):
  141.     """Test whether a path exists.  Returns True for broken symbolic links"""
  142.     try:
  143.         os.lstat(path)
  144.     except (OSError, ValueError):
  145.         return False
  146.     return True


  147. # Is a path a mount point?
  148. # (Does this work for all UNIXes?  Is it even guaranteed to work by Posix?)

  149. def ismount(path):
  150.     """Test whether a path is a mount point"""
  151.     try:
  152.         s1 = os.lstat(path)
  153.     except (OSError, ValueError):
  154.         # It doesn't exist -- so not a mount point. :-)
  155.         return False
  156.     else:
  157.         # A symlink can never be a mount point
  158.         if stat.S_ISLNK(s1.st_mode):
  159.             return False

  160.     if isinstance(path, bytes):
  161.         parent = join(path, b'..')
  162.     else:
  163.         parent = join(path, '..')
  164.     parent = realpath(parent)
  165.     try:
  166.         s2 = os.lstat(parent)
  167.     except (OSError, ValueError):
  168.         return False

  169.     dev1 = s1.st_dev
  170.     dev2 = s2.st_dev
  171.     if dev1 != dev2:
  172.         return True     # path/.. on a different device as path
  173.     ino1 = s1.st_ino
  174.     ino2 = s2.st_ino
  175.     if ino1 == ino2:
  176.         return True     # path/.. is the same i-node as path
  177.     return False


  178. # Expand paths beginning with '~' or '~user'.
  179. # '~' means $HOME; '~user' means that user's home directory.
  180. # If the path doesn't begin with '~', or if the user or $HOME is unknown,
  181. # the path is returned unchanged (leaving error reporting to whatever
  182. # function is called with the expanded path as argument).
  183. # See also module 'glob' for expansion of *, ? and [...] in pathnames.
  184. # (A function should also be defined to do full *sh-style environment
  185. # variable expansion.)

  186. def expanduser(path):
  187.     """Expand ~ and ~user constructions.  If user or $HOME is unknown,
  188.     do nothing."""
  189.     path = os.fspath(path)
  190.     if isinstance(path, bytes):
  191.         tilde = b'~'
  192.     else:
  193.         tilde = '~'
  194.     if not path.startswith(tilde):
  195.         return path
  196.     sep = _get_sep(path)
  197.     i = path.find(sep, 1)
  198.     if i < 0:
  199.         i = len(path)
  200.     if i == 1:
  201.         if 'HOME' not in os.environ:
  202.             import pwd
  203.             try:
  204.                 userhome = pwd.getpwuid(os.getuid()).pw_dir
  205.             except KeyError:
  206.                 # bpo-10496: if the current user identifier doesn't exist in the
  207.                 # password database, return the path unchanged
  208.                 return path
  209.         else:
  210.             userhome = os.environ['HOME']
  211.     else:
  212.         import pwd
  213.         name = path[1:i]
  214.         if isinstance(name, bytes):
  215.             name = str(name, 'ASCII')
  216.         try:
  217.             pwent = pwd.getpwnam(name)
  218.         except KeyError:
  219.             # bpo-10496: if the user name from the path doesn't exist in the
  220.             # password database, return the path unchanged
  221.             return path
  222.         userhome = pwent.pw_dir
  223.     if isinstance(path, bytes):
  224.         userhome = os.fsencode(userhome)
  225.         root = b'/'
  226.     else:
  227.         root = '/'
  228.     userhome = userhome.rstrip(root)
  229.     return (userhome + path[i:]) or root


  230. # Expand paths containing shell variable substitutions.
  231. # This expands the forms $variable and ${variable} only.
  232. # Non-existent variables are left unchanged.

  233. _varprog = None
  234. _varprogb = None

  235. def expandvars(path):
  236.     """Expand shell variables of form $var and ${var}.  Unknown variables
  237.     are left unchanged."""
  238.     path = os.fspath(path)
  239.     global _varprog, _varprogb
  240.     if isinstance(path, bytes):
  241.         if b'$' not in path:
  242.             return path
  243.         if not _varprogb:
  244.             import re
  245.             _varprogb = re.compile(br'\$(\w+|\{[^}]*\})', re.ASCII)
  246.         search = _varprogb.search
  247.         start = b'{'
  248.         end = b'}'
  249.         environ = getattr(os, 'environb', None)
  250.     else:
  251.         if '$' not in path:
  252.             return path
  253.         if not _varprog:
  254.             import re
  255.             _varprog = re.compile(r'\$(\w+|\{[^}]*\})', re.ASCII)
  256.         search = _varprog.search
  257.         start = '{'
  258.         end = '}'
  259.         environ = os.environ
  260.     i = 0
  261.     while True:
  262.         m = search(path, i)
  263.         if not m:
  264.             break
  265.         i, j = m.span(0)
  266.         name = m.group(1)
  267.         if name.startswith(start) and name.endswith(end):
  268.             name = name[1:-1]
  269.         try:
  270.             if environ is None:
  271.                 value = os.fsencode(os.environ[os.fsdecode(name)])
  272.             else:
  273.                 value = environ[name]
  274.         except KeyError:
  275.             i = j
  276.         else:
  277.             tail = path[j:]
  278.             path = path[:i] + value
  279.             i = len(path)
  280.             path += tail
  281.     return path


  282. # Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B.
  283. # It should be understood that this may change the meaning of the path
  284. # if it contains symbolic links!

  285. def normpath(path):
  286.     """Normalize path, eliminating double slashes, etc."""
  287.     path = os.fspath(path)
  288.     if isinstance(path, bytes):
  289.         sep = b'/'
  290.         empty = b''
  291.         dot = b'.'
  292.         dotdot = b'..'
  293.     else:
  294.         sep = '/'
  295.         empty = ''
  296.         dot = '.'
  297.         dotdot = '..'
  298.     if path == empty:
  299.         return dot
  300.     initial_slashes = path.startswith(sep)
  301.     # POSIX allows one or two initial slashes, but treats three or more
  302.     # as single slash.
  303.     # (see http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13)
  304.     if (initial_slashes and
  305.         path.startswith(sep*2) and not path.startswith(sep*3)):
  306.         initial_slashes = 2
  307.     comps = path.split(sep)
  308.     new_comps = []
  309.     for comp in comps:
  310.         if comp in (empty, dot):
  311.             continue
  312.         if (comp != dotdot or (not initial_slashes and not new_comps) or
  313.              (new_comps and new_comps[-1] == dotdot)):
  314.             new_comps.append(comp)
  315.         elif new_comps:
  316.             new_comps.pop()
  317.     comps = new_comps
  318.     path = sep.join(comps)
  319.     if initial_slashes:
  320.         path = sep*initial_slashes + path
  321.     return path or dot


  322. def abspath(path):
  323.     """Return an absolute path."""
  324.     path = os.fspath(path)
  325.     if not isabs(path):
  326.         if isinstance(path, bytes):
  327.             cwd = os.getcwdb()
  328.         else:
  329.             cwd = os.getcwd()
  330.         path = join(cwd, path)
  331.     return normpath(path)


  332. # Return a canonical path (i.e. the absolute location of a file on the
  333. # filesystem).

  334. def realpath(filename):
  335.     """Return the canonical path of the specified filename, eliminating any
  336. symbolic links encountered in the path."""
  337.     filename = os.fspath(filename)
  338.     path, ok = _joinrealpath(filename[:0], filename, {})
  339.     return abspath(path)

  340. # Join two paths, normalizing and eliminating any symbolic links
  341. # encountered in the second path.
  342. def _joinrealpath(path, rest, seen):
  343.     if isinstance(path, bytes):
  344.         sep = b'/'
  345.         curdir = b'.'
  346.         pardir = b'..'
  347.     else:
  348.         sep = '/'
  349.         curdir = '.'
  350.         pardir = '..'

  351.     if isabs(rest):
  352.         rest = rest[1:]
  353.         path = sep

  354.     while rest:
  355.         name, _, rest = rest.partition(sep)
  356.         if not name or name == curdir:
  357.             # current dir
  358.             continue
  359.         if name == pardir:
  360.             # parent dir
  361.             if path:
  362.                 path, name = split(path)
  363.                 if name == pardir:
  364.                     path = join(path, pardir, pardir)
  365.             else:
  366.                 path = pardir
  367.             continue
  368.         newpath = join(path, name)
  369.         if not islink(newpath):
  370.             path = newpath
  371.             continue
  372.         # Resolve the symbolic link
  373.         if newpath in seen:
  374.             # Already seen this path
  375.             path = seen[newpath]
  376.             if path is not None:
  377.                 # use cached value
  378.                 continue
  379.             # The symlink is not resolved, so we must have a symlink loop.
  380.             # Return already resolved part + rest of the path unchanged.
  381.             return join(newpath, rest), False
  382.         seen[newpath] = None # not resolved symlink
  383.         path, ok = _joinrealpath(path, os.readlink(newpath), seen)
  384.         if not ok:
  385.             return join(path, rest), False
  386.         seen[newpath] = path # resolved symlink

  387.     return path, True


  388. supports_unicode_filenames = (sys.platform == 'darwin')

  389. def relpath(path, start=None):
  390.     """Return a relative version of a path"""

  391.     if not path:
  392.         raise ValueError("no path specified")

  393.     path = os.fspath(path)
  394.     if isinstance(path, bytes):
  395.         curdir = b'.'
  396.         sep = b'/'
  397.         pardir = b'..'
  398.     else:
  399.         curdir = '.'
  400.         sep = '/'
  401.         pardir = '..'

  402.     if start is None:
  403.         start = curdir
  404.     else:
  405.         start = os.fspath(start)

  406.     try:
  407.         start_list = [x for x in abspath(start).split(sep) if x]
  408.         path_list = [x for x in abspath(path).split(sep) if x]
  409.         # Work out how much of the filepath is shared by start and path.
  410.         i = len(commonprefix([start_list, path_list]))

  411.         rel_list = [pardir] * (len(start_list)-i) + path_list[i:]
  412.         if not rel_list:
  413.             return curdir
  414.         return join(*rel_list)
  415.     except (TypeError, AttributeError, BytesWarning, DeprecationWarning):
  416.         genericpath._check_arg_types('relpath', path, start)
  417.         raise


  418. # Return the longest common sub-path of the sequence of paths given as input.
  419. # The paths are not normalized before comparing them (this is the
  420. # responsibility of the caller). Any trailing separator is stripped from the
  421. # returned path.

  422. def commonpath(paths):
  423.     """Given a sequence of path names, returns the longest common sub-path."""

  424.     if not paths:
  425.         raise ValueError('commonpath() arg is an empty sequence')

  426.     paths = tuple(map(os.fspath, paths))
  427.     if isinstance(paths[0], bytes):
  428.         sep = b'/'
  429.         curdir = b'.'
  430.     else:
  431.         sep = '/'
  432.         curdir = '.'

  433.     try:
  434.         split_paths = [path.split(sep) for path in paths]

  435.         try:
  436.             isabs, = set(p[:1] == sep for p in paths)
  437.         except ValueError:
  438.             raise ValueError("Can't mix absolute and relative paths") from None

  439.         split_paths = [[c for c in s if c and c != curdir] for s in split_paths]
  440.         s1 = min(split_paths)
  441.         s2 = max(split_paths)
  442.         common = s1
  443.         for i, c in enumerate(s1):
  444.             if c != s2[i]:
  445.                 common = s1[:i]
  446.                 break

  447.         prefix = sep if isabs else sep[:0]
  448.         return prefix + sep.join(common)
  449.     except (TypeError, AttributeError):
  450.         genericpath._check_arg_types('commonpath', *paths)
  451.         raise
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-11-27 10:18:23 | 显示全部楼层
windows 的在这里
  1. /usr/lib/python3.9/ntpath.py
复制代码

  1. # Module 'ntpath' -- common operations on WinNT/Win95 pathnames
  2. """Common pathname manipulations, WindowsNT/95 version.

  3. Instead of importing this module directly, import os and refer to this
  4. module as os.path.
  5. """

  6. # strings representing various path-related bits and pieces
  7. # These are primarily for export; internally, they are hardcoded.
  8. # Should be set before imports for resolving cyclic dependency.
  9. curdir = '.'
  10. pardir = '..'
  11. extsep = '.'
  12. sep = '\\'
  13. pathsep = ';'
  14. altsep = '/'
  15. defpath = '.;C:\\bin'
  16. devnull = 'nul'

  17. import os
  18. import sys
  19. import stat
  20. import genericpath
  21. from genericpath import *

  22. __all__ = ["normcase","isabs","join","splitdrive","split","splitext",
  23.            "basename","dirname","commonprefix","getsize","getmtime",
  24.            "getatime","getctime", "islink","exists","lexists","isdir","isfile",
  25.            "ismount", "expanduser","expandvars","normpath","abspath",
  26.            "curdir","pardir","sep","pathsep","defpath","altsep",
  27.            "extsep","devnull","realpath","supports_unicode_filenames","relpath",
  28.            "samefile", "sameopenfile", "samestat", "commonpath"]

  29. def _get_bothseps(path):
  30.     if isinstance(path, bytes):
  31.         return b'\\/'
  32.     else:
  33.         return '\\/'

  34. # Normalize the case of a pathname and map slashes to backslashes.
  35. # Other normalizations (such as optimizing '../' away) are not done
  36. # (this is done by normpath).

  37. def normcase(s):
  38.     """Normalize case of pathname.

  39.     Makes all characters lowercase and all slashes into backslashes."""
  40.     s = os.fspath(s)
  41.     if isinstance(s, bytes):
  42.         return s.replace(b'/', b'\\').lower()
  43.     else:
  44.         return s.replace('/', '\\').lower()


  45. # Return whether a path is absolute.
  46. # Trivial in Posix, harder on Windows.
  47. # For Windows it is absolute if it starts with a slash or backslash (current
  48. # volume), or if a pathname after the volume-letter-and-colon or UNC-resource
  49. # starts with a slash or backslash.

  50. def isabs(s):
  51.     """Test whether a path is absolute"""
  52.     s = os.fspath(s)
  53.     # Paths beginning with \\?\ are always absolute, but do not
  54.     # necessarily contain a drive.
  55.     if isinstance(s, bytes):
  56.         if s.replace(b'/', b'\\').startswith(b'\\\\?\\'):
  57.             return True
  58.     else:
  59.         if s.replace('/', '\\').startswith('\\\\?\\'):
  60.             return True
  61.     s = splitdrive(s)[1]
  62.     return len(s) > 0 and s[0] in _get_bothseps(s)


  63. # Join two (or more) paths.
  64. def join(path, *paths):
  65.     path = os.fspath(path)
  66.     if isinstance(path, bytes):
  67.         sep = b'\\'
  68.         seps = b'\\/'
  69.         colon = b':'
  70.     else:
  71.         sep = '\\'
  72.         seps = '\\/'
  73.         colon = ':'
  74.     try:
  75.         if not paths:
  76.             path[:0] + sep  #23780: Ensure compatible data type even if p is null.
  77.         result_drive, result_path = splitdrive(path)
  78.         for p in map(os.fspath, paths):
  79.             p_drive, p_path = splitdrive(p)
  80.             if p_path and p_path[0] in seps:
  81.                 # Second path is absolute
  82.                 if p_drive or not result_drive:
  83.                     result_drive = p_drive
  84.                 result_path = p_path
  85.                 continue
  86.             elif p_drive and p_drive != result_drive:
  87.                 if p_drive.lower() != result_drive.lower():
  88.                     # Different drives => ignore the first path entirely
  89.                     result_drive = p_drive
  90.                     result_path = p_path
  91.                     continue
  92.                 # Same drive in different case
  93.                 result_drive = p_drive
  94.             # Second path is relative to the first
  95.             if result_path and result_path[-1] not in seps:
  96.                 result_path = result_path + sep
  97.             result_path = result_path + p_path
  98.         ## add separator between UNC and non-absolute path
  99.         if (result_path and result_path[0] not in seps and
  100.             result_drive and result_drive[-1:] != colon):
  101.             return result_drive + sep + result_path
  102.         return result_drive + result_path
  103.     except (TypeError, AttributeError, BytesWarning):
  104.         genericpath._check_arg_types('join', path, *paths)
  105.         raise


  106. # Split a path in a drive specification (a drive letter followed by a
  107. # colon) and the path specification.
  108. # It is always true that drivespec + pathspec == p
  109. def splitdrive(p):
  110.     """Split a pathname into drive/UNC sharepoint and relative path specifiers.
  111.     Returns a 2-tuple (drive_or_unc, path); either part may be empty.

  112.     If you assign
  113.         result = splitdrive(p)
  114.     It is always true that:
  115.         result[0] + result[1] == p

  116.     If the path contained a drive letter, drive_or_unc will contain everything
  117.     up to and including the colon.  e.g. splitdrive("c:/dir") returns ("c:", "/dir")

  118.     If the path contained a UNC path, the drive_or_unc will contain the host name
  119.     and share up to but not including the fourth directory separator character.
  120.     e.g. splitdrive("//host/computer/dir") returns ("//host/computer", "/dir")

  121.     Paths cannot contain both a drive letter and a UNC path.

  122.     """
  123.     p = os.fspath(p)
  124.     if len(p) >= 2:
  125.         if isinstance(p, bytes):
  126.             sep = b'\\'
  127.             altsep = b'/'
  128.             colon = b':'
  129.         else:
  130.             sep = '\\'
  131.             altsep = '/'
  132.             colon = ':'
  133.         normp = p.replace(altsep, sep)
  134.         if (normp[0:2] == sep*2) and (normp[2:3] != sep):
  135.             # is a UNC path:
  136.             # vvvvvvvvvvvvvvvvvvvv drive letter or UNC path
  137.             # \\machine\mountpoint\directory\etc\...
  138.             #           directory ^^^^^^^^^^^^^^^
  139.             index = normp.find(sep, 2)
  140.             if index == -1:
  141.                 return p[:0], p
  142.             index2 = normp.find(sep, index + 1)
  143.             # a UNC path can't have two slashes in a row
  144.             # (after the initial two)
  145.             if index2 == index + 1:
  146.                 return p[:0], p
  147.             if index2 == -1:
  148.                 index2 = len(p)
  149.             return p[:index2], p[index2:]
  150.         if normp[1:2] == colon:
  151.             return p[:2], p[2:]
  152.     return p[:0], p


  153. # Split a path in head (everything up to the last '/') and tail (the
  154. # rest).  After the trailing '/' is stripped, the invariant
  155. # join(head, tail) == p holds.
  156. # The resulting head won't end in '/' unless it is the root.

  157. def split(p):
  158.     """Split a pathname.

  159.     Return tuple (head, tail) where tail is everything after the final slash.
  160.     Either part may be empty."""
  161.     p = os.fspath(p)
  162.     seps = _get_bothseps(p)
  163.     d, p = splitdrive(p)
  164.     # set i to index beyond p's last slash
  165.     i = len(p)
  166.     while i and p[i-1] not in seps:
  167.         i -= 1
  168.     head, tail = p[:i], p[i:]  # now tail has no slashes
  169.     # remove trailing slashes from head, unless it's all slashes
  170.     head = head.rstrip(seps) or head
  171.     return d + head, tail


  172. # Split a path in root and extension.
  173. # The extension is everything starting at the last dot in the last
  174. # pathname component; the root is everything before that.
  175. # It is always true that root + ext == p.

  176. def splitext(p):
  177.     p = os.fspath(p)
  178.     if isinstance(p, bytes):
  179.         return genericpath._splitext(p, b'\\', b'/', b'.')
  180.     else:
  181.         return genericpath._splitext(p, '\\', '/', '.')
  182. splitext.__doc__ = genericpath._splitext.__doc__


  183. # Return the tail (basename) part of a path.

  184. def basename(p):
  185.     """Returns the final component of a pathname"""
  186.     return split(p)[1]


  187. # Return the head (dirname) part of a path.

  188. def dirname(p):
  189.     """Returns the directory component of a pathname"""
  190.     return split(p)[0]

  191. # Is a path a symbolic link?
  192. # This will always return false on systems where os.lstat doesn't exist.

  193. def islink(path):
  194.     """Test whether a path is a symbolic link.
  195.     This will always return false for Windows prior to 6.0.
  196.     """
  197.     try:
  198.         st = os.lstat(path)
  199.     except (OSError, ValueError, AttributeError):
  200.         return False
  201.     return stat.S_ISLNK(st.st_mode)

  202. # Being true for dangling symbolic links is also useful.

  203. def lexists(path):
  204.     """Test whether a path exists.  Returns True for broken symbolic links"""
  205.     try:
  206.         st = os.lstat(path)
  207.     except (OSError, ValueError):
  208.         return False
  209.     return True

  210. # Is a path a mount point?
  211. # Any drive letter root (eg c:\)
  212. # Any share UNC (eg \\server\share)
  213. # Any volume mounted on a filesystem folder
  214. #
  215. # No one method detects all three situations. Historically we've lexically
  216. # detected drive letter roots and share UNCs. The canonical approach to
  217. # detecting mounted volumes (querying the reparse tag) fails for the most
  218. # common case: drive letter roots. The alternative which uses GetVolumePathName
  219. # fails if the drive letter is the result of a SUBST.
  220. try:
  221.     from nt import _getvolumepathname
  222. except ImportError:
  223.     _getvolumepathname = None
  224. def ismount(path):
  225.     """Test whether a path is a mount point (a drive root, the root of a
  226.     share, or a mounted volume)"""
  227.     path = os.fspath(path)
  228.     seps = _get_bothseps(path)
  229.     path = abspath(path)
  230.     root, rest = splitdrive(path)
  231.     if root and root[0] in seps:
  232.         return (not rest) or (rest in seps)
  233.     if rest in seps:
  234.         return True

  235.     if _getvolumepathname:
  236.         return path.rstrip(seps) == _getvolumepathname(path).rstrip(seps)
  237.     else:
  238.         return False


  239. # Expand paths beginning with '~' or '~user'.
  240. # '~' means $HOME; '~user' means that user's home directory.
  241. # If the path doesn't begin with '~', or if the user or $HOME is unknown,
  242. # the path is returned unchanged (leaving error reporting to whatever
  243. # function is called with the expanded path as argument).
  244. # See also module 'glob' for expansion of *, ? and [...] in pathnames.
  245. # (A function should also be defined to do full *sh-style environment
  246. # variable expansion.)

  247. def expanduser(path):
  248.     """Expand ~ and ~user constructs.

  249.     If user or $HOME is unknown, do nothing."""
  250.     path = os.fspath(path)
  251.     if isinstance(path, bytes):
  252.         tilde = b'~'
  253.     else:
  254.         tilde = '~'
  255.     if not path.startswith(tilde):
  256.         return path
  257.     i, n = 1, len(path)
  258.     while i < n and path[i] not in _get_bothseps(path):
  259.         i += 1

  260.     if 'USERPROFILE' in os.environ:
  261.         userhome = os.environ['USERPROFILE']
  262.     elif not 'HOMEPATH' in os.environ:
  263.         return path
  264.     else:
  265.         try:
  266.             drive = os.environ['HOMEDRIVE']
  267.         except KeyError:
  268.             drive = ''
  269.         userhome = join(drive, os.environ['HOMEPATH'])

  270.     if isinstance(path, bytes):
  271.         userhome = os.fsencode(userhome)

  272.     if i != 1: #~user
  273.         userhome = join(dirname(userhome), path[1:i])

  274.     return userhome + path[i:]


  275. # Expand paths containing shell variable substitutions.
  276. # The following rules apply:
  277. #       - no expansion within single quotes
  278. #       - '$$' is translated into '$'
  279. #       - '%%' is translated into '%' if '%%' are not seen in %var1%%var2%
  280. #       - ${varname} is accepted.
  281. #       - $varname is accepted.
  282. #       - %varname% is accepted.
  283. #       - varnames can be made out of letters, digits and the characters '_-'
  284. #         (though is not verified in the ${varname} and %varname% cases)
  285. # XXX With COMMAND.COM you can use any characters in a variable name,
  286. # XXX except '^|<>='.

  287. def expandvars(path):
  288.     """Expand shell variables of the forms $var, ${var} and %var%.

  289.     Unknown variables are left unchanged."""
  290.     path = os.fspath(path)
  291.     if isinstance(path, bytes):
  292.         if b'$' not in path and b'%' not in path:
  293.             return path
  294.         import string
  295.         varchars = bytes(string.ascii_letters + string.digits + '_-', 'ascii')
  296.         quote = b'\''
  297.         percent = b'%'
  298.         brace = b'{'
  299.         rbrace = b'}'
  300.         dollar = b'$'
  301.         environ = getattr(os, 'environb', None)
  302.     else:
  303.         if '$' not in path and '%' not in path:
  304.             return path
  305.         import string
  306.         varchars = string.ascii_letters + string.digits + '_-'
  307.         quote = '\''
  308.         percent = '%'
  309.         brace = '{'
  310.         rbrace = '}'
  311.         dollar = '$'
  312.         environ = os.environ
  313.     res = path[:0]
  314.     index = 0
  315.     pathlen = len(path)
  316.     while index < pathlen:
  317.         c = path[index:index+1]
  318.         if c == quote:   # no expansion within single quotes
  319.             path = path[index + 1:]
  320.             pathlen = len(path)
  321.             try:
  322.                 index = path.index(c)
  323.                 res += c + path[:index + 1]
  324.             except ValueError:
  325.                 res += c + path
  326.                 index = pathlen - 1
  327.         elif c == percent:  # variable or '%'
  328.             if path[index + 1:index + 2] == percent:
  329.                 res += c
  330.                 index += 1
  331.             else:
  332.                 path = path[index+1:]
  333.                 pathlen = len(path)
  334.                 try:
  335.                     index = path.index(percent)
  336.                 except ValueError:
  337.                     res += percent + path
  338.                     index = pathlen - 1
  339.                 else:
  340.                     var = path[:index]
  341.                     try:
  342.                         if environ is None:
  343.                             value = os.fsencode(os.environ[os.fsdecode(var)])
  344.                         else:
  345.                             value = environ[var]
  346.                     except KeyError:
  347.                         value = percent + var + percent
  348.                     res += value
  349.         elif c == dollar:  # variable or '$$'
  350.             if path[index + 1:index + 2] == dollar:
  351.                 res += c
  352.                 index += 1
  353.             elif path[index + 1:index + 2] == brace:
  354.                 path = path[index+2:]
  355.                 pathlen = len(path)
  356.                 try:
  357.                     index = path.index(rbrace)
  358.                 except ValueError:
  359.                     res += dollar + brace + path
  360.                     index = pathlen - 1
  361.                 else:
  362.                     var = path[:index]
  363.                     try:
  364.                         if environ is None:
  365.                             value = os.fsencode(os.environ[os.fsdecode(var)])
  366.                         else:
  367.                             value = environ[var]
  368.                     except KeyError:
  369.                         value = dollar + brace + var + rbrace
  370.                     res += value
  371.             else:
  372.                 var = path[:0]
  373.                 index += 1
  374.                 c = path[index:index + 1]
  375.                 while c and c in varchars:
  376.                     var += c
  377.                     index += 1
  378.                     c = path[index:index + 1]
  379.                 try:
  380.                     if environ is None:
  381.                         value = os.fsencode(os.environ[os.fsdecode(var)])
  382.                     else:
  383.                         value = environ[var]
  384.                 except KeyError:
  385.                     value = dollar + var
  386.                 res += value
  387.                 if c:
  388.                     index -= 1
  389.         else:
  390.             res += c
  391.         index += 1
  392.     return res


  393. # Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A\B.
  394. # Previously, this function also truncated pathnames to 8+3 format,
  395. # but as this module is called "ntpath", that's obviously wrong!

  396. def normpath(path):
  397.     """Normalize path, eliminating double slashes, etc."""
  398.     path = os.fspath(path)
  399.     if isinstance(path, bytes):
  400.         sep = b'\\'
  401.         altsep = b'/'
  402.         curdir = b'.'
  403.         pardir = b'..'
  404.         special_prefixes = (b'\\\\.\\', b'\\\\?\\')
  405.     else:
  406.         sep = '\\'
  407.         altsep = '/'
  408.         curdir = '.'
  409.         pardir = '..'
  410.         special_prefixes = ('\\\\.\\', '\\\\?\\')
  411.     if path.startswith(special_prefixes):
  412.         # in the case of paths with these prefixes:
  413.         # \\.\ -> device names
  414.         # \\?\ -> literal paths
  415.         # do not do any normalization, but return the path
  416.         # unchanged apart from the call to os.fspath()
  417.         return path
  418.     path = path.replace(altsep, sep)
  419.     prefix, path = splitdrive(path)

  420.     # collapse initial backslashes
  421.     if path.startswith(sep):
  422.         prefix += sep
  423.         path = path.lstrip(sep)

  424.     comps = path.split(sep)
  425.     i = 0
  426.     while i < len(comps):
  427.         if not comps[i] or comps[i] == curdir:
  428.             del comps[i]
  429.         elif comps[i] == pardir:
  430.             if i > 0 and comps[i-1] != pardir:
  431.                 del comps[i-1:i+1]
  432.                 i -= 1
  433.             elif i == 0 and prefix.endswith(sep):
  434.                 del comps[i]
  435.             else:
  436.                 i += 1
  437.         else:
  438.             i += 1
  439.     # If the path is now empty, substitute '.'
  440.     if not prefix and not comps:
  441.         comps.append(curdir)
  442.     return prefix + sep.join(comps)

  443. def _abspath_fallback(path):
  444.     """Return the absolute version of a path as a fallback function in case
  445.     `nt._getfullpathname` is not available or raises OSError. See bpo-31047 for
  446.     more.

  447.     """

  448.     path = os.fspath(path)
  449.     if not isabs(path):
  450.         if isinstance(path, bytes):
  451.             cwd = os.getcwdb()
  452.         else:
  453.             cwd = os.getcwd()
  454.         path = join(cwd, path)
  455.     return normpath(path)

  456. # Return an absolute path.
  457. try:
  458.     from nt import _getfullpathname

  459. except ImportError: # not running on Windows - mock up something sensible
  460.     abspath = _abspath_fallback

  461. else:  # use native Windows method on Windows
  462.     def abspath(path):
  463.         """Return the absolute version of a path."""
  464.         try:
  465.             return normpath(_getfullpathname(path))
  466.         except (OSError, ValueError):
  467.             return _abspath_fallback(path)

  468. try:
  469.     from nt import _getfinalpathname, readlink as _nt_readlink
  470. except ImportError:
  471.     # realpath is a no-op on systems without _getfinalpathname support.
  472.     realpath = abspath
  473. else:
  474.     def _readlink_deep(path):
  475.         # These error codes indicate that we should stop reading links and
  476.         # return the path we currently have.
  477.         # 1: ERROR_INVALID_FUNCTION
  478.         # 2: ERROR_FILE_NOT_FOUND
  479.         # 3: ERROR_DIRECTORY_NOT_FOUND
  480.         # 5: ERROR_ACCESS_DENIED
  481.         # 21: ERROR_NOT_READY (implies drive with no media)
  482.         # 32: ERROR_SHARING_VIOLATION (probably an NTFS paging file)
  483.         # 50: ERROR_NOT_SUPPORTED (implies no support for reparse points)
  484.         # 67: ERROR_BAD_NET_NAME (implies remote server unavailable)
  485.         # 87: ERROR_INVALID_PARAMETER
  486.         # 4390: ERROR_NOT_A_REPARSE_POINT
  487.         # 4392: ERROR_INVALID_REPARSE_DATA
  488.         # 4393: ERROR_REPARSE_TAG_INVALID
  489.         allowed_winerror = 1, 2, 3, 5, 21, 32, 50, 67, 87, 4390, 4392, 4393

  490.         seen = set()
  491.         while normcase(path) not in seen:
  492.             seen.add(normcase(path))
  493.             try:
  494.                 old_path = path
  495.                 path = _nt_readlink(path)
  496.                 # Links may be relative, so resolve them against their
  497.                 # own location
  498.                 if not isabs(path):
  499.                     # If it's something other than a symlink, we don't know
  500.                     # what it's actually going to be resolved against, so
  501.                     # just return the old path.
  502.                     if not islink(old_path):
  503.                         path = old_path
  504.                         break
  505.                     path = normpath(join(dirname(old_path), path))
  506.             except OSError as ex:
  507.                 if ex.winerror in allowed_winerror:
  508.                     break
  509.                 raise
  510.             except ValueError:
  511.                 # Stop on reparse points that are not symlinks
  512.                 break
  513.         return path

  514.     def _getfinalpathname_nonstrict(path):
  515.         # These error codes indicate that we should stop resolving the path
  516.         # and return the value we currently have.
  517.         # 1: ERROR_INVALID_FUNCTION
  518.         # 2: ERROR_FILE_NOT_FOUND
  519.         # 3: ERROR_DIRECTORY_NOT_FOUND
  520.         # 5: ERROR_ACCESS_DENIED
  521.         # 21: ERROR_NOT_READY (implies drive with no media)
  522.         # 32: ERROR_SHARING_VIOLATION (probably an NTFS paging file)
  523.         # 50: ERROR_NOT_SUPPORTED
  524.         # 67: ERROR_BAD_NET_NAME (implies remote server unavailable)
  525.         # 87: ERROR_INVALID_PARAMETER
  526.         # 123: ERROR_INVALID_NAME
  527.         # 1920: ERROR_CANT_ACCESS_FILE
  528.         # 1921: ERROR_CANT_RESOLVE_FILENAME (implies unfollowable symlink)
  529.         allowed_winerror = 1, 2, 3, 5, 21, 32, 50, 67, 87, 123, 1920, 1921

  530.         # Non-strict algorithm is to find as much of the target directory
  531.         # as we can and join the rest.
  532.         tail = ''
  533.         while path:
  534.             try:
  535.                 path = _getfinalpathname(path)
  536.                 return join(path, tail) if tail else path
  537.             except OSError as ex:
  538.                 if ex.winerror not in allowed_winerror:
  539.                     raise
  540.                 try:
  541.                     # The OS could not resolve this path fully, so we attempt
  542.                     # to follow the link ourselves. If we succeed, join the tail
  543.                     # and return.
  544.                     new_path = _readlink_deep(path)
  545.                     if new_path != path:
  546.                         return join(new_path, tail) if tail else new_path
  547.                 except OSError:
  548.                     # If we fail to readlink(), let's keep traversing
  549.                     pass
  550.                 path, name = split(path)
  551.                 # TODO (bpo-38186): Request the real file name from the directory
  552.                 # entry using FindFirstFileW. For now, we will return the path
  553.                 # as best we have it
  554.                 if path and not name:
  555.                     return path + tail
  556.                 tail = join(name, tail) if tail else name
  557.         return tail

  558.     def realpath(path):
  559.         path = normpath(path)
  560.         if isinstance(path, bytes):
  561.             prefix = b'\\\\?\\'
  562.             unc_prefix = b'\\\\?\\UNC\\'
  563.             new_unc_prefix = b'\\\\'
  564.             cwd = os.getcwdb()
  565.             # bpo-38081: Special case for realpath(b'nul')
  566.             if normcase(path) == normcase(os.fsencode(devnull)):
  567.                 return b'\\\\.\\NUL'
  568.         else:
  569.             prefix = '\\\\?\\'
  570.             unc_prefix = '\\\\?\\UNC\\'
  571.             new_unc_prefix = '\\\\'
  572.             cwd = os.getcwd()
  573.             # bpo-38081: Special case for realpath('nul')
  574.             if normcase(path) == normcase(devnull):
  575.                 return '\\\\.\\NUL'
  576.         had_prefix = path.startswith(prefix)
  577.         if not had_prefix and not isabs(path):
  578.             path = join(cwd, path)
  579.         try:
  580.             path = _getfinalpathname(path)
  581.             initial_winerror = 0
  582.         except OSError as ex:
  583.             initial_winerror = ex.winerror
  584.             path = _getfinalpathname_nonstrict(path)
  585.         # The path returned by _getfinalpathname will always start with \\?\ -
  586.         # strip off that prefix unless it was already provided on the original
  587.         # path.
  588.         if not had_prefix and path.startswith(prefix):
  589.             # For UNC paths, the prefix will actually be \\?\UNC\
  590.             # Handle that case as well.
  591.             if path.startswith(unc_prefix):
  592.                 spath = new_unc_prefix + path[len(unc_prefix):]
  593.             else:
  594.                 spath = path[len(prefix):]
  595.             # Ensure that the non-prefixed path resolves to the same path
  596.             try:
  597.                 if _getfinalpathname(spath) == path:
  598.                     path = spath
  599.             except OSError as ex:
  600.                 # If the path does not exist and originally did not exist, then
  601.                 # strip the prefix anyway.
  602.                 if ex.winerror == initial_winerror:
  603.                     path = spath
  604.         return path


  605. # Win9x family and earlier have no Unicode filename support.
  606. supports_unicode_filenames = (hasattr(sys, "getwindowsversion") and
  607.                               sys.getwindowsversion()[3] >= 2)

  608. def relpath(path, start=None):
  609.     """Return a relative version of a path"""
  610.     path = os.fspath(path)
  611.     if isinstance(path, bytes):
  612.         sep = b'\\'
  613.         curdir = b'.'
  614.         pardir = b'..'
  615.     else:
  616.         sep = '\\'
  617.         curdir = '.'
  618.         pardir = '..'

  619.     if start is None:
  620.         start = curdir

  621.     if not path:
  622.         raise ValueError("no path specified")

  623.     start = os.fspath(start)
  624.     try:
  625.         start_abs = abspath(normpath(start))
  626.         path_abs = abspath(normpath(path))
  627.         start_drive, start_rest = splitdrive(start_abs)
  628.         path_drive, path_rest = splitdrive(path_abs)
  629.         if normcase(start_drive) != normcase(path_drive):
  630.             raise ValueError("path is on mount %r, start on mount %r" % (
  631.                 path_drive, start_drive))

  632.         start_list = [x for x in start_rest.split(sep) if x]
  633.         path_list = [x for x in path_rest.split(sep) if x]
  634.         # Work out how much of the filepath is shared by start and path.
  635.         i = 0
  636.         for e1, e2 in zip(start_list, path_list):
  637.             if normcase(e1) != normcase(e2):
  638.                 break
  639.             i += 1

  640.         rel_list = [pardir] * (len(start_list)-i) + path_list[i:]
  641.         if not rel_list:
  642.             return curdir
  643.         return join(*rel_list)
  644.     except (TypeError, ValueError, AttributeError, BytesWarning, DeprecationWarning):
  645.         genericpath._check_arg_types('relpath', path, start)
  646.         raise


  647. # Return the longest common sub-path of the sequence of paths given as input.
  648. # The function is case-insensitive and 'separator-insensitive', i.e. if the
  649. # only difference between two paths is the use of '\' versus '/' as separator,
  650. # they are deemed to be equal.
  651. #
  652. # However, the returned path will have the standard '\' separator (even if the
  653. # given paths had the alternative '/' separator) and will have the case of the
  654. # first path given in the sequence. Additionally, any trailing separator is
  655. # stripped from the returned path.

  656. def commonpath(paths):
  657.     """Given a sequence of path names, returns the longest common sub-path."""

  658.     if not paths:
  659.         raise ValueError('commonpath() arg is an empty sequence')

  660.     paths = tuple(map(os.fspath, paths))
  661.     if isinstance(paths[0], bytes):
  662.         sep = b'\\'
  663.         altsep = b'/'
  664.         curdir = b'.'
  665.     else:
  666.         sep = '\\'
  667.         altsep = '/'
  668.         curdir = '.'

  669.     try:
  670.         drivesplits = [splitdrive(p.replace(altsep, sep).lower()) for p in paths]
  671.         split_paths = [p.split(sep) for d, p in drivesplits]

  672.         try:
  673.             isabs, = set(p[:1] == sep for d, p in drivesplits)
  674.         except ValueError:
  675.             raise ValueError("Can't mix absolute and relative paths") from None

  676.         # Check that all drive letters or UNC paths match. The check is made only
  677.         # now otherwise type errors for mixing strings and bytes would not be
  678.         # caught.
  679.         if len(set(d for d, p in drivesplits)) != 1:
  680.             raise ValueError("Paths don't have the same drive")

  681.         drive, path = splitdrive(paths[0].replace(altsep, sep))
  682.         common = path.split(sep)
  683.         common = [c for c in common if c and c != curdir]

  684.         split_paths = [[c for c in s if c and c != curdir] for s in split_paths]
  685.         s1 = min(split_paths)
  686.         s2 = max(split_paths)
  687.         for i, c in enumerate(s1):
  688.             if c != s2[i]:
  689.                 common = common[:i]
  690.                 break
  691.         else:
  692.             common = common[:len(s1)]

  693.         prefix = drive + sep if isabs else drive
  694.         return prefix + sep.join(common)
  695.     except (TypeError, AttributeError):
  696.         genericpath._check_arg_types('commonpath', *paths)
  697.         raise


  698. try:
  699.     # The genericpath.isdir implementation uses os.stat and checks the mode
  700.     # attribute to tell whether or not the path is a directory.
  701.     # This is overkill on Windows - just pass the path to GetFileAttributes
  702.     # and check the attribute from there.
  703.     from nt import _isdir as isdir
  704. except ImportError:
  705.     # Use genericpath.isdir as imported above.
  706.     pass
复制代码

评分

参与人数 1荣誉 +5 鱼币 +5 贡献 +3 收起 理由
hrpzcf + 5 + 5 + 3 鱼C有你更精彩^_^

查看全部评分

小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-4-25 13:43

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表