【PYTHON3学习】os.path.isdir判断文件夹却返回false的问题——追根溯源!

今天学习os.path.isdir发现如下问题:

目录如下:

  • 问题来源
  • 为什么会出现这个现象?
    • isdir()解析
    • 分析源代码
    • 做做实验
  • 结论

1.问题来源

def dir_L2(Dir,keyword):
	for x in os.listdir(Dir):					
		if os.path.isdir(x):
			nextDir=os.path.join(Dir,x)	
			dir_L2(nextDir,keyword)
		else:
			if keyword in x:
				print(os.path.join(Dir,x).replace(os.getcwd(),'.'))	
		....
#当遍历到当前目录下第三层的文件夹FILE2(ROOT-FILE1-FILE2)时,os.path.isdir会返回False
#导致不遍历子文件中的所有文件

经过测试后修改如下

def dir_L2(Dir,keyword):
	for x in os.listdir(Dir):
		nextDir=os.path.join(Dir,x)				
		if os.path.isdir(nextDir):
			dir_L2(nextDir,keyword)
		else:
			if keyword in x:
				print(nextDir.replace(os.getcwd(),'.'))			
		....
#此时可成功判断

2.为什么会出现这种现象呢?

我们来首先看看os.path.isdir源代码如下(VS2019看源代码:Ctrl+左键)

def isdir(s):
    """Return true if the pathname refers to an existing directory."""
    try:
        st = os.stat(s)
    except OSError:
        return False
    return stat.S_ISDIR(st.st_mode)

def S_ISDIR(mode):
    """Return True if mode is from a directory."""
    return S_IFMT(mode) == S_IFDIR
    
S_IFDIR  = 0o040000  # directory

def S_IFMT(mode):
    """Return the portion of the file's mode that describes the
    file type.
    """
    return mode & 0o170000

2.1.isdir()解析

1) 简要描述os.stat及st_mode剖析

  • struct stat的结构如下
struct stat {
dev_t         st_dev;      /* device */
ino_t         st_ino;      /* inode */
mode_t        st_mode;     /* protection */
nlink_t       st_nlink;    /* number of hard links */
uid_t         st_uid;      /* user ID of owner */
gid_t         st_gid;      /* group ID of owner */
dev_t         st_rdev;     /* device type (if inode device) */
off_t         st_size;     /* total size, in bytes */
blksize_t     st_blksize;  /* blocksize for filesystem I/O */
blkcnt_t      st_blocks;   /* number of blocks allocated */
time_t        st_atime;    /* time of last access */
time_t        st_mtime;    /* time of last modification */
time_t        st_ctime;    /* time of last status change */
};

其中,st_mode的类型mode_t.
mode_t其实就是普通的```unsigned int.``

2)解剖st_mode

目前,st_mode使用了其低19bit. 0170000 => 1+ 3*5 = 16.
其中,最低的9位(0-8)是权限,9-11是id,12-15是类型。
具体定义如下:

S_IFMT     0170000   bitmask for the file type bitfields
S_IFSOCK   0140000   socket
S_IFLNK    0120000   symbolic link
S_IFREG    0100000   regular file
S_IFBLK    0060000   block device
S_IFDIR    0040000   directory
S_IFCHR    0020000   character device
S_IFIFO    0010000   fifo
S_ISUID    0004000   set UID bit
S_ISGID    0002000   set GID bit (see below)
S_ISVTX    0001000   sticky bit (see below)
S_IRWXU    00700     mask for file owner permissions
S_IRUSR    00400     owner has read permission
S_IWUSR    00200     owner has write permission
S_IXUSR    00100     owner has execute permission
S_IRWXG    00070     mask for group permissions
S_IRGRP    00040     group has read permission
S_IWGRP    00020     group has write permission
S_IXGRP    00010     group has execute permission
S_IRWXO    00007     mask for permissions for others (not in group)
S_IROTH    00004     others have read permission
S_IWOTH    00002     others have write permisson
S_IXOTH    00001     others have execute permission

2.2.分析源代码

1) S_IFMT()和S_ISDIR()

def S_IFMT(mode):
    """Return the portion of the file's mode that describes the
    file type.
    """
    return mode & 0o170000
def S_ISDIR(mode):
    """Return True if mode is from a directory."""
    return S_IFMT(mode) == S_IFDIR
    
S_IFDIR  = 0o040000  # directory

S_IFMT()将传入的mode_t类型参数与S_IFMT=0o170000位与,由上述知,S_IFMT文件类型位域的位掩码,所得结果为文件的特征(包括权限、ID、类型)——此处我们需要文件类型——S_IFDIR ==0o040000

2) isdir()

def isdir(s):
    """Return true if the pathname refers to an existing directory."""
    try:
        st = os.stat(s)
    except OSError:
        return False
    return stat.S_ISDIR(st.st_mode)

此处便好理解了,若st.st_mode&S_IFMT==S_IFDIR,则返回True

2.3 做做实验

1) 首先判断第三层文件所返回的st_mode

已知当前目录为I:\vs2019_Data\PythonLearning
PythonLearning下有目录I:\vs2019_Data\PythonLearning\.vs\PythonLearning\v16\.suo

  • 测试代码如下
import os
def dir(Dir):
	for x in os.listdir(Dir):			
			dirinfo=os.stat(x)
			nextDir=os.path.join(Dir,x)						#下一文件路径
			print('当前文件%s绝对路径为:%s'%(x,nextDir))
			dirtype=dirinfo.st_mode & 0o170000
			print('文件类型代码:%o'%dirtype)
			dir(nextDir)

Dirpath='I:\\vs2019_Data\\PythonLearning'
dir(os.getcwd())
#运行结果如下
已加载“__main__”
已加载“runpy”
当前文件.vs绝对路径为:I:\vs2019_Data\PythonLearning\.vs
文件类型代码:40000
[WinError 2] The system cannot find the file specified: 'PythonLearning'
堆栈跟踪:
 >  File "I:\vs2019_Data\PythonLearning\code_test.py", line 4, in dir
 >    dirinfo=os.stat(x)
 >  File "I:\vs2019_Data\PythonLearning\code_test.py", line 9, in dir
 >    dir(nextDir)
 >  File "I:\vs2019_Data\PythonLearning\code_test.py", line 12, in <module>
 >    dir(os.getcwd())

根据输出:[WinError 2] The system cannot find the file specified: ‘PythonLearning’
问题出在dirinfo=os.stat(x),此处无法判断文件夹PythonLearning的stat特征。

  • 故修改测试代码如下:
import os
def dir(Dir):
	for x in os.listdir(Dir):		
		dirinfo=os.stat(x)
		print('当前目录:%s'%os.getcwd())
		nextDir=os.path.join(Dir,x)						#下一文件路径
		print('当前文件%s绝对路径为:%s'%(x,nextDir))
		dirtype=dirinfo.st_mode & 0o170000
		print('文件类型代码:%o'%dirtype)
		os.chdir(nextDir)
		dir(nextDir)

Dirpath='I:\\vs2019_Data\\PythonLearning'
dir(os.getcwd())

由于是判断x的特征,且每次遍历到第三层文件夹时,便报错。故我判断可能是当前工作目录(os.getcwd()可获得当前工作目录)一直未更新,故造成第三层文件夹名称x与当前目录之间出现断裂,解释器无法判断当前xstat
故上述代码在每次循环修改了当前目录,测试成功,输出如下
【PYTHON3学习】os.path.isdir判断文件夹却返回false的问题——追根溯源!_第1张图片
显然,经过修改当前目录,其文件代码类型显示正确。

3.结论

os.path.isdir判断文件夹却返回 false的原因是:
当前目录与所遍历文件夹之间路径出现断裂,导致在判断时无法返回正确的st_mode

你可能感兴趣的:(python)