对pathlib进行扩展

pathlib 自 Python 3.4 以后成为了 Python 的标准库,该库非常的好用,大大简化了目录的管理。但也有一些不足,可以对其进行扩展,使其更加好用。

扩展的代码

# 项目:standard python lib
# 模块:path and file
# 作者:黄涛
# License:GPL
# Email:[email protected]
# 创建:2016-03-11 12:21
# 修改:2016-04-13 21:01
# 修改:2016-08-13 新增__iter__ 和 extractall功能
# 修订:2016-11-19 修改Path的实现方式 

import pathlib
import os
from codecs import BOM_UTF8,BOM_LE,BOM_BE
from .click import *

BOM_CODE={
    BOM_UTF8:'utf_8',
    BOM_LE:'utf_16_le',
    BOM_BE:'utf_16_be',
    }
    
DEFAULT_CODES='utf8','gbk','utf16','big5'

def is_installed(file_name):
    '''
    确认指定的文件是否已被安装。
    '''
    from sysconfig import get_path
    paths=[get_path(name) for name in ('platlib','scripts')]
    if os.name=='nt':
        file_name=file_name.lower()
        paths=[path.lower() for path in paths]
    return any([file_name.startswith(path) for path in paths])

def is_dev(cmd=None):
    import sys
    cmd=cmd or sys.argv[0]
    if('wsgi' in cmd):
        return False
    return 'test' in cmd or (not is_installed(cmd))

def decode(d):
    '''
    对指定的二进制,进行智能解码,适配适当的编码。返回解码后的字符串。
    '''
    for k in BOM_CODE:
        if k==d[:len(k)]:
            return d[len(k):].decode(BOM_CODE[k])
    for encoding in DEFAULT_CODES:
        try:
            return d.decode(encoding)
        except:
            pass
    raise Exception('解码失败')

_Parent= pathlib.WindowsPath if os.name=='nt' else pathlib.PosixPath

class Path(_Parent):
    __slots__=()
    def __new__(cls,path='.',*args,**kwargs):
        if isinstance(path,str):
            if path.startswith('~'):  # 支持用户目录开头
                path=os.path.expanduser(path)
            elif path.startswith('%'): # 支持环境变量转义
                path=os.path.expandvars(path)
        return super().__new__(cls,path,*args,**kwargs)
    
    def read(self,*args,**kwargs):
        '''以指定的参数读取文件'''
        with self.open(*args,**kwargs)as fn:
            return fn.read()

    def ensure(self,parents=True):
        '''确保目录存在,如果目录不存在则直接创建'''
        if not self.exists():
            self.mkdir(parents=parents)
            
    @property
    def text(self):
        '''读取文件,并返回字符串'''
        return decode(self.read('rb'))

    @text.setter
    def text(self,text):
        '''写入文本文件'''
        self.write(text=text)
        
    @property
    def lines(self):
        '''按行读取文件'''
        return self.text.splitlines()

    @lines.setter
    def lines(self,lines):
        '''按行写入文件'''
        self.write(*lines)
        
    def write(self,*lines,text=None,data=None,encoding='utf8',
              parents=False):
        '''写文件'''
        if parents:
            self.parent.ensure()
        if lines:
            text="\n".join(lines)
        if text:
            data=text.encode(encoding)
        if data:
            with self.open('wb')as fn:
                fn.write(data)

    def sheets(self,index=None):
        ''' 提供读取指定worksheet的功能,其中index可以为序号,
            也可以为表的名称。'''
        import xlrd
        book=xlrd.open_workbook(filename=str(self))
        if isinstance(index,int):
            sheet=book.sheet_by_index(index)
        elif isinstance(index,str):
            sheet=book.sheet_by_name(index)
        return sheet and sheet._cell_values
        
    def iter_sheets(self):
        '''如果指定的文件为excel文件,则可以迭代读取本文件的数据。
        返回:表的索引、表名、数据'''
        import xlrd 
        book=xlrd.open_workbook(filename=str(self))
        for index,sheet in enumerate(book.sheets()):
            yield index,sheet.name,sheet._cell_values

    @property
    def xmlroot(self):
        '''如果指定的文件为xml文件,则返回本文件的根元素'''
        import lxml.etree
        return lxml.etree.parse(str(self)).getroot()
        
    def __iter__(self):
        '''根据文件的不同,迭代返回不同的内容。支持如下文件:
        1、文本文件,按行返回文本
        2、Excel文件,返回表索引、表名及表中数据
        3、目录,则返回本目录下所有文件
        4、del文件,按行返回数据。
        5、csv文件,按行返回数据。
        '''
        if self.is_dir():
            return self.glob('*')
        suffix=self.lsuffix
        if suffix.startswith('.xls'):
            return self.iter_sheets()
        elif suffix=='.xml':
            return self.xmlroot.iterchildren()
        elif suffix=='.del':
            import re
            none_pattern=re.compile(",(?=,)")
            for line in self.lines:
                if line:
                    yield eval(none_pattern.sub(",None",line))
        elif suffix=='.csv':
            import csv
            with self.open() as fn:
                yield from csv.reader(fn)

    def extractall(self,path='.',members=None):
        '''如本文件为tar打包文件,则解压缩至指定目录'''
        import tarfile
        path=str(Path(path))
        tarfile.open(str(self),'r').extractall(path,members)
        
    @property
    def lsuffix(self):
        '''返回小写的扩展名'''
        return self.suffix.lower()
        
    @property
    def pname(self):
        '''返回不带扩展名的文件名'''
        return self.with_suffix("").name
    
    def rmtree(self):
        '''删除整个目录'''
        import shutil
        shutil.rmtree(str(self))

    def chdir(self):
        if self.is_dir():
            os.chdir(str(self))

@command(description='Windows 格式文件转换为 Unix 文件格式')
@arg('files',nargs='*',help='待转换的文件',metavar='file')
def convert(files):
    for file in files:
        Path(file).lines=Path(file).lines
        print('转换文件"%s"成功'%(file))

新增功能介绍

支持家目录及目录扩展

支持自动对目录进行扩展,如果目录中包含 "~""%appdata%"等内容时,系统会自动进行扩展。其好处是显而意见的,如果不同操作系统的文件都放在相同的目录,那么我们不就用去管操作系统的差异。其使用代码如下:

import os

print(Path('~/abc')
if os.name!='posix':
    print(Path('%appdata%/abc')

增加 read 方法

用于读取指定的文件,其参数为open所需要的参数。代码示例如下:

fn=Path('~/abc.txt')
s=fn.read()
print(s)

增加 ensure 方法

对指定的目录的进行检查,如指定的目录不存在,则自动创建。代码示例如下:

Path('~/abc').ensure()

增加 text 属性

该属性用于读写文本文件的内容。示例代码如下:

Path('~/a.txt').text='This is a test.\nHello world.\n'

print(Path('~/a.txt').text)

** 注:文本文件在读取时,其编码可以由系统自动进行只别;写入时编码为 UTF8 。**

增加 lines 属性

该属性以行的方式读取或写入文本。代码如下:

lines=['This is a test.',
           'Hello world']
Path('~/a.txt').lines=lines
print(*(Path('~/a.txt').lines),sep='\n')

增加 write 方法

写入文件,可以按行写入,按文本写入、或直接写数据。示例代码如下:

lines=['This is a test.',
           'Hello world']
Path('~/a.txt').wreite(*lines)
print(*(Path('~/a.txt').lines),sep='\n')

增加 sheets 方法

如果指定的文件为 Excel 文件,根据工作表的名字或索引读取该工作表的数据。示例代码如下:

data=Path('~/abc.xlsx').sheets(0)
data=Path('~/abc.xls').sheets('Sheet1')

增加 iter_sheets 方法

for idx,name,data in Path('~/abc.xls').iter_sheets():
    print(name,idx)

增加 xmlroot 属性

如指定文件为 xml 文件,则返回该文件的根元素。

for i in Path('a.xml').xlmroot:
    print(i.tag)

增加 __iter__ 方法

根据指定目录或文件的不同返回不同的内容:

  1. 目录,则返回当前目录下所有的文或目录。示例代码如下:
for d in Path('.'):
    print(d)
  1. Excel文件,返回索引、表名及表中的数据。示例代码如下:
for idx,name,data in Path('~/abc.xlsx'):
    print(idx,name)
  1. txt文本文件。则按行返回数据。示例代码如下:
for line in Path('a.txt'):
    print(line)

增加 lsuffix 只读属性

返回指定文件的小写扩展名,便于判断文件的类型。示例代码如下:

if Path(filename).lsuffix in ('.xls','.xlsx','.xlsm'):
    print('Excel 文件')

增加 pname 只读属性

返回指定文件的文件名(不含扩展名)。示例代码如下:

print(Path('~/abc/def.txt').pname)

增加 chdir 方法

将指定的目录作为当前工作目录。示例代码如下:

Path('~/SendTo').chdir()

增加 rmtree 方法

将指定的目录全部删除。示例代码如下:

Path('~/test').rmtree()

你可能感兴趣的:(对pathlib进行扩展)