pyxllib 0.0.43__py3-none-any.whl → 0.3.197__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyxllib/__init__.py +9 -2
- pyxllib/algo/__init__.py +8 -0
- pyxllib/algo/disjoint.py +54 -0
- pyxllib/algo/geo.py +541 -0
- pyxllib/{util/mathlib.py → algo/intervals.py} +172 -36
- pyxllib/algo/matcher.py +389 -0
- pyxllib/algo/newbie.py +166 -0
- pyxllib/algo/pupil.py +629 -0
- pyxllib/algo/shapelylib.py +67 -0
- pyxllib/algo/specialist.py +241 -0
- pyxllib/algo/stat.py +494 -0
- pyxllib/algo/treelib.py +149 -0
- pyxllib/algo/unitlib.py +66 -0
- pyxllib/autogui/__init__.py +5 -0
- pyxllib/autogui/activewin.py +246 -0
- pyxllib/autogui/all.py +9 -0
- pyxllib/autogui/autogui.py +852 -0
- pyxllib/autogui/uiautolib.py +362 -0
- pyxllib/autogui/virtualkey.py +102 -0
- pyxllib/autogui/wechat.py +827 -0
- pyxllib/autogui/wechat_msg.py +421 -0
- pyxllib/autogui/wxautolib.py +84 -0
- pyxllib/cv/__init__.py +1 -11
- pyxllib/cv/expert.py +267 -0
- pyxllib/cv/{imlib.py → imfile.py} +18 -83
- pyxllib/cv/imhash.py +39 -0
- pyxllib/cv/pupil.py +9 -0
- pyxllib/cv/rgbfmt.py +1525 -0
- pyxllib/cv/slidercaptcha.py +137 -0
- pyxllib/cv/trackbartools.py +163 -49
- pyxllib/cv/xlcvlib.py +1040 -0
- pyxllib/cv/xlpillib.py +423 -0
- pyxllib/data/__init__.py +0 -0
- pyxllib/data/echarts.py +240 -0
- pyxllib/data/jsonlib.py +89 -0
- pyxllib/{util/oss2_.py → data/oss.py} +11 -9
- pyxllib/data/pglib.py +1127 -0
- pyxllib/data/sqlite.py +568 -0
- pyxllib/{util → data}/sqllib.py +13 -31
- pyxllib/ext/JLineViewer.py +505 -0
- pyxllib/ext/__init__.py +6 -0
- pyxllib/{util → ext}/demolib.py +119 -35
- pyxllib/ext/drissionlib.py +277 -0
- pyxllib/ext/kq5034lib.py +12 -0
- pyxllib/{util/main.py → ext/old.py} +122 -284
- pyxllib/ext/qt.py +449 -0
- pyxllib/ext/robustprocfile.py +497 -0
- pyxllib/ext/seleniumlib.py +76 -0
- pyxllib/{util/tklib.py → ext/tk.py} +10 -11
- pyxllib/ext/unixlib.py +827 -0
- pyxllib/ext/utools.py +351 -0
- pyxllib/{util/webhooklib.py → ext/webhook.py} +45 -17
- pyxllib/ext/win32lib.py +40 -0
- pyxllib/ext/wjxlib.py +88 -0
- pyxllib/ext/wpsapi.py +124 -0
- pyxllib/ext/xlwork.py +9 -0
- pyxllib/ext/yuquelib.py +1105 -0
- pyxllib/file/__init__.py +17 -0
- pyxllib/file/docxlib.py +761 -0
- pyxllib/{util → file}/gitlib.py +40 -27
- pyxllib/file/libreoffice.py +165 -0
- pyxllib/file/movielib.py +148 -0
- pyxllib/file/newbie.py +10 -0
- pyxllib/file/onenotelib.py +1469 -0
- pyxllib/file/packlib/__init__.py +330 -0
- pyxllib/{util → file/packlib}/zipfile.py +598 -195
- pyxllib/file/pdflib.py +426 -0
- pyxllib/file/pupil.py +185 -0
- pyxllib/file/specialist/__init__.py +685 -0
- pyxllib/{basic/_5_dirlib.py → file/specialist/dirlib.py} +364 -93
- pyxllib/file/specialist/download.py +193 -0
- pyxllib/file/specialist/filelib.py +2829 -0
- pyxllib/file/xlsxlib.py +3131 -0
- pyxllib/file/xlsyncfile.py +341 -0
- pyxllib/prog/__init__.py +5 -0
- pyxllib/prog/cachetools.py +64 -0
- pyxllib/prog/deprecatedlib.py +233 -0
- pyxllib/prog/filelock.py +42 -0
- pyxllib/prog/ipyexec.py +253 -0
- pyxllib/prog/multiprogs.py +940 -0
- pyxllib/prog/newbie.py +451 -0
- pyxllib/prog/pupil.py +1197 -0
- pyxllib/{sitepackages.py → prog/sitepackages.py} +5 -3
- pyxllib/prog/specialist/__init__.py +391 -0
- pyxllib/prog/specialist/bc.py +203 -0
- pyxllib/prog/specialist/browser.py +497 -0
- pyxllib/prog/specialist/common.py +347 -0
- pyxllib/prog/specialist/datetime.py +199 -0
- pyxllib/prog/specialist/tictoc.py +240 -0
- pyxllib/prog/specialist/xllog.py +180 -0
- pyxllib/prog/xlosenv.py +108 -0
- pyxllib/stdlib/__init__.py +17 -0
- pyxllib/{util → stdlib}/tablepyxl/__init__.py +1 -3
- pyxllib/{util → stdlib}/tablepyxl/style.py +1 -1
- pyxllib/{util → stdlib}/tablepyxl/tablepyxl.py +2 -4
- pyxllib/text/__init__.py +8 -0
- pyxllib/text/ahocorasick.py +39 -0
- pyxllib/text/airscript.js +744 -0
- pyxllib/text/charclasslib.py +121 -0
- pyxllib/text/jiebalib.py +267 -0
- pyxllib/text/jinjalib.py +32 -0
- pyxllib/text/jsa_ai_prompt.md +271 -0
- pyxllib/text/jscode.py +922 -0
- pyxllib/text/latex/__init__.py +158 -0
- pyxllib/text/levenshtein.py +303 -0
- pyxllib/text/nestenv.py +1215 -0
- pyxllib/text/newbie.py +300 -0
- pyxllib/text/pupil/__init__.py +8 -0
- pyxllib/text/pupil/common.py +1121 -0
- pyxllib/text/pupil/xlalign.py +326 -0
- pyxllib/text/pycode.py +47 -0
- pyxllib/text/specialist/__init__.py +8 -0
- pyxllib/text/specialist/common.py +112 -0
- pyxllib/text/specialist/ptag.py +186 -0
- pyxllib/text/spellchecker.py +172 -0
- pyxllib/text/templates/echart_base.html +11 -0
- pyxllib/text/templates/highlight_code.html +17 -0
- pyxllib/text/templates/latex_editor.html +103 -0
- pyxllib/text/vbacode.py +17 -0
- pyxllib/text/xmllib.py +747 -0
- pyxllib/xl.py +39 -0
- pyxllib/xlcv.py +17 -0
- pyxllib-0.3.197.dist-info/METADATA +48 -0
- pyxllib-0.3.197.dist-info/RECORD +126 -0
- {pyxllib-0.0.43.dist-info → pyxllib-0.3.197.dist-info}/WHEEL +4 -5
- pyxllib/basic/_1_strlib.py +0 -945
- pyxllib/basic/_2_timelib.py +0 -488
- pyxllib/basic/_3_pathlib.py +0 -916
- pyxllib/basic/_4_loglib.py +0 -419
- pyxllib/basic/__init__.py +0 -54
- pyxllib/basic/arrow_.py +0 -250
- pyxllib/basic/chardet_.py +0 -66
- pyxllib/basic/dirlib.py +0 -529
- pyxllib/basic/dprint.py +0 -202
- pyxllib/basic/extension.py +0 -12
- pyxllib/basic/judge.py +0 -31
- pyxllib/basic/log.py +0 -204
- pyxllib/basic/pathlib_.py +0 -705
- pyxllib/basic/pytictoc.py +0 -102
- pyxllib/basic/qiniu_.py +0 -61
- pyxllib/basic/strlib.py +0 -761
- pyxllib/basic/timer.py +0 -132
- pyxllib/cv/cv.py +0 -834
- pyxllib/cv/cvlib/_1_geo.py +0 -543
- pyxllib/cv/cvlib/_2_cvprcs.py +0 -309
- pyxllib/cv/cvlib/_2_imgproc.py +0 -594
- pyxllib/cv/cvlib/_3_pilprcs.py +0 -80
- pyxllib/cv/cvlib/_4_cvimg.py +0 -211
- pyxllib/cv/cvlib/__init__.py +0 -10
- pyxllib/cv/debugtools.py +0 -82
- pyxllib/cv/fitz_.py +0 -300
- pyxllib/cv/installer.py +0 -42
- pyxllib/debug/_0_installer.py +0 -38
- pyxllib/debug/_1_typelib.py +0 -277
- pyxllib/debug/_2_chrome.py +0 -198
- pyxllib/debug/_3_showdir.py +0 -161
- pyxllib/debug/_4_bcompare.py +0 -140
- pyxllib/debug/__init__.py +0 -49
- pyxllib/debug/bcompare.py +0 -132
- pyxllib/debug/chrome.py +0 -198
- pyxllib/debug/installer.py +0 -38
- pyxllib/debug/showdir.py +0 -158
- pyxllib/debug/typelib.py +0 -278
- pyxllib/image/__init__.py +0 -12
- pyxllib/torch/__init__.py +0 -20
- pyxllib/torch/modellib.py +0 -37
- pyxllib/torch/trainlib.py +0 -344
- pyxllib/util/__init__.py +0 -20
- pyxllib/util/aip_.py +0 -141
- pyxllib/util/casiadb.py +0 -59
- pyxllib/util/excellib.py +0 -495
- pyxllib/util/filelib.py +0 -612
- pyxllib/util/jsondata.py +0 -27
- pyxllib/util/jsondata2.py +0 -92
- pyxllib/util/labelmelib.py +0 -139
- pyxllib/util/onepy/__init__.py +0 -29
- pyxllib/util/onepy/onepy.py +0 -574
- pyxllib/util/onepy/onmanager.py +0 -170
- pyxllib/util/pyautogui_.py +0 -219
- pyxllib/util/textlib.py +0 -1305
- pyxllib/util/unorder.py +0 -22
- pyxllib/util/xmllib.py +0 -639
- pyxllib-0.0.43.dist-info/METADATA +0 -39
- pyxllib-0.0.43.dist-info/RECORD +0 -80
- pyxllib-0.0.43.dist-info/top_level.txt +0 -1
- {pyxllib-0.0.43.dist-info → pyxllib-0.3.197.dist-info/licenses}/LICENSE +0 -0
pyxllib/basic/dirlib.py
DELETED
@@ -1,529 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
# -*- coding: utf-8 -*-
|
3
|
-
# @Author : 陈坤泽
|
4
|
-
# @Email : 877362867@qq.com
|
5
|
-
# @Data : 2020/05/30
|
6
|
-
|
7
|
-
|
8
|
-
import filecmp
|
9
|
-
import logging
|
10
|
-
import math
|
11
|
-
import os
|
12
|
-
import re
|
13
|
-
import shutil
|
14
|
-
|
15
|
-
# 大小写不敏感字典
|
16
|
-
from requests.structures import CaseInsensitiveDict
|
17
|
-
|
18
|
-
from .arrow_ import Datetime
|
19
|
-
from .strlib import strfind, natural_sort
|
20
|
-
from .pathlib_ import Path
|
21
|
-
from .log import *
|
22
|
-
|
23
|
-
____file = """
|
24
|
-
路径、文件、目录相关操作功能
|
25
|
-
|
26
|
-
主要是为了提供readfile、wrritefile函数
|
27
|
-
与普通的读写文件相比,有以下优点:
|
28
|
-
1、智能识别pkl等特殊格式文件的处理
|
29
|
-
2、智能处理编码
|
30
|
-
3、目录不存在自动创建
|
31
|
-
4、自动备份旧文件,而不是强制覆盖写入
|
32
|
-
|
33
|
-
其他相关文件处理组件:isfile、get_encoding、ensure_folders
|
34
|
-
以及同时支持文件或文件夹的对比复制删除等操作的函数:filescmp、filesdel、filescopy
|
35
|
-
"""
|
36
|
-
|
37
|
-
|
38
|
-
class Dir(Path):
|
39
|
-
r"""类似NestEnv思想的文件夹处理类
|
40
|
-
|
41
|
-
这里的测试可以全程自己造一个
|
42
|
-
"""
|
43
|
-
__slots__ = ('files', '_origin_wkdir')
|
44
|
-
|
45
|
-
def __init__(self, path=None, *, root=None, files=None):
|
46
|
-
"""根目录、工作目录
|
47
|
-
|
48
|
-
>> Dir() # 以当前文件夹作为root
|
49
|
-
>> Dir(r'C:/pycode/code4101py') # 指定目录
|
50
|
-
"""
|
51
|
-
super().__init__(path, root=root)
|
52
|
-
self.files = files or [] # 初始默认没有选中任何文件(文件夹)
|
53
|
-
|
54
|
-
@property
|
55
|
-
def absfiles(self):
|
56
|
-
"""返回所有files的绝对路径"""
|
57
|
-
return [self.fullpath + '/' + f for f in self.files]
|
58
|
-
|
59
|
-
@property
|
60
|
-
def filepaths(self):
|
61
|
-
"""返回所有files的path对象"""
|
62
|
-
return [self / f for f in self.files]
|
63
|
-
|
64
|
-
def select(self, patter, nsort=True, **kwargs):
|
65
|
-
r""" 增加选中文件,从filesmatch衍生而来,参数含义见 filesfilter
|
66
|
-
|
67
|
-
:param nsort: 是否使用自然排序,关闭可以加速
|
68
|
-
|
69
|
-
注意select和exclude的增减操作是不断叠加的,而不是每次重置!
|
70
|
-
如果需要重置,应该重新定义一个Folder类
|
71
|
-
|
72
|
-
>>> Dir('C:/pycode/code4101py').select('*.pyw').select('ckz.py')
|
73
|
-
C:/pycode/code4101py: ['ol批量修改文本.pyw', 'ckz.py']
|
74
|
-
>>> Dir('C:/pycode/code4101py').select('**/*.pyw').select('ckz.py')
|
75
|
-
C:/pycode/code4101py: ['ol批量修改文本.pyw', 'chenkz/批量修改文本.pyw', 'winr/bc.pyw', 'winr/reg/FileBackup.pyw', 'ckz.py']
|
76
|
-
|
77
|
-
>>> Dir('C:/pycode/code4101py').select('*.py', min_size=200*1024) # 200kb以上的文件
|
78
|
-
C:/pycode/code4101py: ['liangyb.py']
|
79
|
-
|
80
|
-
>> Dir(r'C:/pycode/code4101py').select('*.py', min_mtime=Datetime(2020, 3, 1)) # 修改时间在3月1日以上的
|
81
|
-
"""
|
82
|
-
files = filesmatch(patter, root=self.fullpath, **kwargs)
|
83
|
-
files = self.files + files
|
84
|
-
if nsort: files = natural_sort(files)
|
85
|
-
return Dir(self._path, files=files)
|
86
|
-
|
87
|
-
def procfiles(self, func, start=None, end=None, ref_dir=None, pinterval=None, max_workers=1, interrupt=True):
|
88
|
-
""" 对选中的文件迭代处理
|
89
|
-
|
90
|
-
:param func: 对每个文件进行处理的自定义接口函数
|
91
|
-
参数 p: 输入参数 Path 对象
|
92
|
-
return: 可以没有返回值,当有返回值时,会作为信息,表示要输出查看
|
93
|
-
TODO 以后可以返回字典结构,用不同的key表示不同的功能,可以控制些高级功能
|
94
|
-
|
95
|
-
TODO 增设可以bfs还是dfs的功能?
|
96
|
-
|
97
|
-
|
98
|
-
将目录 test 的所有文件拷贝到 test2 目录 示例代码:
|
99
|
-
|
100
|
-
def func(p1, p2):
|
101
|
-
p1.copy(p2)
|
102
|
-
|
103
|
-
Dir('test').select('**/*', type_='file').procfiles(func, ref_dir='test2')
|
104
|
-
|
105
|
-
"""
|
106
|
-
if ref_dir:
|
107
|
-
ref_dir = Dir(ref_dir)
|
108
|
-
files1 = self.filepaths
|
109
|
-
files2 = [(ref_dir / self.files[i]) for i in range(len(self.files))]
|
110
|
-
|
111
|
-
def wrap_func(data):
|
112
|
-
func(*data)
|
113
|
-
|
114
|
-
data = zip(files1, files2)
|
115
|
-
|
116
|
-
else:
|
117
|
-
data = self.filepaths
|
118
|
-
wrap_func = func
|
119
|
-
|
120
|
-
Iterate(data).run(wrap_func, start=start, end=end, pinterval=pinterval,
|
121
|
-
max_workers=max_workers, interrupt=interrupt)
|
122
|
-
|
123
|
-
def select_invert(self, patter='**/*', nsort=True, **kwargs):
|
124
|
-
""" 反选,在"全集"中,选中当前状态下没有被选中的那些文件
|
125
|
-
|
126
|
-
这里设置的选择模式,是指全集的选择范围
|
127
|
-
"""
|
128
|
-
files = Dir(self).select(patter, nsort, **kwargs).files
|
129
|
-
cur_files = set(self.files)
|
130
|
-
new_files = []
|
131
|
-
for f in files:
|
132
|
-
if f not in cur_files:
|
133
|
-
new_files.append(f)
|
134
|
-
return Dir(self._path, files=new_files)
|
135
|
-
|
136
|
-
def exclude(self, patter, **kwargs):
|
137
|
-
""" 去掉部分选中文件
|
138
|
-
|
139
|
-
d1 = Dir('test').select('**/*.eps')
|
140
|
-
d2 = d1.exclude('subdir/*.eps')
|
141
|
-
d3 = d2.select_invert(type_='file')
|
142
|
-
print(d1.files) # ['AA20pH-c1=1-1.eps', 'AA20pH-c1=1-2.eps', 'subdir/AA20pH-c1=1-2 - 副本.eps']
|
143
|
-
print(d2.files) # ['AA20pH-c1=1-1.eps', 'AA20pH-c1=1-2.eps']
|
144
|
-
print(d3.files) # ['subdir/AA20pH-c1=1-2 - 副本.eps']
|
145
|
-
"""
|
146
|
-
files = set(filesmatch(patter, root=self.fullpath, **kwargs))
|
147
|
-
new_files = []
|
148
|
-
for f in self.files:
|
149
|
-
if f not in files:
|
150
|
-
new_files.append(f)
|
151
|
-
return Dir(self._path, files=new_files)
|
152
|
-
|
153
|
-
def __repr__(self):
|
154
|
-
return f'{self.root}: {self.files}'
|
155
|
-
|
156
|
-
def __enter__(self):
|
157
|
-
""" 使用with模式可以进行工作目录切换
|
158
|
-
|
159
|
-
注意!注意!注意!
|
160
|
-
切换工作目录和多线程混合使用会有意想不到的坑,要慎重!
|
161
|
-
"""
|
162
|
-
self._origin_wkdir = os.getcwd()
|
163
|
-
os.chdir(self.fullpath)
|
164
|
-
return self
|
165
|
-
|
166
|
-
def __exit__(self, exc_type, exc_val, exc_tb):
|
167
|
-
os.chdir(self._origin_wkdir)
|
168
|
-
|
169
|
-
|
170
|
-
def filescmp(f1, f2, shallow=True):
|
171
|
-
"""只有两个存在且是同类型的文件或文件夹,内容相同才会返回True,否则均返回False
|
172
|
-
:param f1: 待比较的第1个文件(文件夹)
|
173
|
-
:param f2: 待比较的第2个文件(文件夹)
|
174
|
-
:param shallow: 默认True,即是利用os.stat()返回的基本信息进行比较
|
175
|
-
例如其中的文件大小,但修改时间等是不影响差异判断的
|
176
|
-
如果设为False,则会打开比较具体内容,速度会慢一点
|
177
|
-
"""
|
178
|
-
if os.path.isfile(f1) and os.path.isfile(f2):
|
179
|
-
cmp = filecmp.cmp(f1, f2, shallow)
|
180
|
-
elif os.path.isdir(f1) and os.path.isdir(f2):
|
181
|
-
# 文件夹只确保直接子目录下的清单名称,不比较具体每个文件内容是否相同,和子目录相同
|
182
|
-
t = filecmp.dircmp(f1, f2, shallow)
|
183
|
-
cmp = False
|
184
|
-
try:
|
185
|
-
if not t.left_only and not t.right_only:
|
186
|
-
cmp = True
|
187
|
-
except TypeError:
|
188
|
-
pass
|
189
|
-
else: # 有不存在的文件
|
190
|
-
cmp = False
|
191
|
-
return cmp
|
192
|
-
|
193
|
-
|
194
|
-
def filesfilter(files, *, root=os.curdir, type_=None,
|
195
|
-
ignore_backup=False, ignore_special=False,
|
196
|
-
min_size=None, max_size=None,
|
197
|
-
min_ctime=None, max_ctime=None, min_mtime=None, max_mtime=None):
|
198
|
-
"""
|
199
|
-
:param files: 类list对象
|
200
|
-
:param type_:
|
201
|
-
None,所有文件
|
202
|
-
'file',只匹配文件
|
203
|
-
'dir', 只匹配目录
|
204
|
-
:param ignore_backup: 如果设为False,会过滤掉自定义的备份文件格式,不获取备份类文件
|
205
|
-
:param ignore_special: 自动过滤掉 '.git'、'$RECYCLE.BIN' 目录下文件
|
206
|
-
:param min_size: 文件大小过滤,单位Byte
|
207
|
-
:param max_size: ~
|
208
|
-
:param min_ctime: 创建时间的过滤,格式'2019-09-01'或'2019-09-01 00:00'
|
209
|
-
:param max_ctime: ~
|
210
|
-
:param min_mtime: 修改时间的过滤
|
211
|
-
:param max_mtime: ~
|
212
|
-
:return:
|
213
|
-
"""
|
214
|
-
|
215
|
-
def judge(f):
|
216
|
-
if root: f = os.path.join(root, f)
|
217
|
-
if type_ == 'file' and not os.path.isfile(f):
|
218
|
-
return False
|
219
|
-
elif type_ == 'dir' and not os.path.isdir(f):
|
220
|
-
return False
|
221
|
-
|
222
|
-
msg = os.stat(f)
|
223
|
-
if min_size is not None or max_size is not None:
|
224
|
-
size = Path(f).size
|
225
|
-
if min_size is not None and size < min_size: return False
|
226
|
-
if max_size is not None and size > max_size: return False
|
227
|
-
|
228
|
-
if min_ctime or max_ctime:
|
229
|
-
file_ctime = msg.st_ctime
|
230
|
-
if min_ctime and Datetime(file_ctime) < min_ctime: return False
|
231
|
-
if max_ctime and Datetime(file_ctime) > max_ctime: return False
|
232
|
-
|
233
|
-
if min_mtime or max_mtime:
|
234
|
-
file_mtime = msg.st_mtime
|
235
|
-
if min_mtime and Datetime(file_mtime) < min_mtime: return False
|
236
|
-
if max_mtime and Datetime(file_mtime) > max_mtime: return False
|
237
|
-
|
238
|
-
if ignore_special:
|
239
|
-
parts = Path(f).parts
|
240
|
-
if '.git' in parts or '$RECYCLE.BIN' in parts:
|
241
|
-
return False
|
242
|
-
|
243
|
-
if ignore_backup and Path(f).backup_time:
|
244
|
-
return False
|
245
|
-
|
246
|
-
return True
|
247
|
-
|
248
|
-
root = os.path.abspath(root)
|
249
|
-
return list(filter(judge, files))
|
250
|
-
|
251
|
-
|
252
|
-
def filesmatch(patter, *, root=os.curdir, **kwargs) -> list:
|
253
|
-
r"""
|
254
|
-
:param patter:
|
255
|
-
str,
|
256
|
-
不含*、?、<、>,普通筛选规则
|
257
|
-
含*、?、<、>,支持Path.glob的通配符模式,使用**可以表示任意子目录
|
258
|
-
glob其实支持[0-9]这种用法,但是[、]在文件名中是合法的,
|
259
|
-
为了明确要使用glob模式,我这里改成<>模式
|
260
|
-
**/*,是不会匹配到根目录的
|
261
|
-
re.Patter,正则筛选规则(这种方法会比较慢,但是很灵活) 或者其他有match成员函数的类也可以
|
262
|
-
会获得当前工作目录下的所有文件相对路径,组成list
|
263
|
-
对list的所有元素使用re.match进行匹配
|
264
|
-
list、tuple、set对象
|
265
|
-
对每一个元素,递归调用filesmatch
|
266
|
-
其他参数都是文件筛选功能,详见filesfilter中介绍
|
267
|
-
:return: 匹配到的所有存在的文件、文件夹,返回“相对路径”
|
268
|
-
|
269
|
-
TODO patter大小写问题?会导致匹配缺失的bug吗?
|
270
|
-
|
271
|
-
>>> os.chdir('F:/work/filesmatch') # 工作目录
|
272
|
-
|
273
|
-
1、普通匹配
|
274
|
-
>>> filesmatch('a') # 匹配当前目录下的文件a,或者目录a
|
275
|
-
['a']
|
276
|
-
>>> filesmatch('b/a/')
|
277
|
-
['b\\a']
|
278
|
-
>>> filesmatch('b/..\\a/')
|
279
|
-
['a']
|
280
|
-
>>> filesmatch('c') # 不存在c则返回 []
|
281
|
-
[]
|
282
|
-
|
283
|
-
2、通配符模式
|
284
|
-
>>> filesmatch('work/*.png') # 支持通配符
|
285
|
-
[]
|
286
|
-
>>> filesmatch('*.png') # 支持通配符
|
287
|
-
['1.png', '1[.png', 'logo.png']
|
288
|
-
>>> filesmatch('**/*.png') # 包含所有子目录下的png图片
|
289
|
-
['1.png', '1[.png', 'logo.png', 'a\\2.png']
|
290
|
-
>>> filesmatch('?.png')
|
291
|
-
['1.png']
|
292
|
-
>>> filesmatch('[0-9]/<0-9>.txt') # 用<0-9>表示[0-9]模式
|
293
|
-
['[0-9]\\3.txt']
|
294
|
-
|
295
|
-
3、正则模式
|
296
|
-
>>> filesmatch(re.compile(r'\d\[\.png$'))
|
297
|
-
['1[.png']
|
298
|
-
|
299
|
-
4、其他高级用法
|
300
|
-
>>> filesmatch('**/*', type_='dir', max_size=0) # 筛选空目录
|
301
|
-
['b', '[0-9]']
|
302
|
-
>>> filesmatch('**/*', type_='file', max_size=0) # 筛选空文件
|
303
|
-
['b/a', '[0-9]/3.txt']
|
304
|
-
"""
|
305
|
-
root = os.path.abspath(root)
|
306
|
-
|
307
|
-
# 0 规则匹配
|
308
|
-
# patter = str(patter) # 200916周三14:59,这样会处理不了正则,要关掉
|
309
|
-
glob_chars_pos = strfind(patter, ('*', '?', '<', '>')) if isinstance(patter, str) else -1
|
310
|
-
|
311
|
-
# 1 普通文本匹配 (没有通配符,单文件查找)
|
312
|
-
if isinstance(patter, str) and glob_chars_pos == -1:
|
313
|
-
path = Path(patter, root=root)
|
314
|
-
if path.exists(): # 文件存在
|
315
|
-
p = str(Path(patter, root=root).resolve())
|
316
|
-
if p.startswith(root): p = p[len(root) + 1:]
|
317
|
-
res = [p]
|
318
|
-
else: # 文件不存在
|
319
|
-
res = []
|
320
|
-
# 2 glob通配符匹配
|
321
|
-
elif isinstance(patter, str) and glob_chars_pos != -1:
|
322
|
-
patter = patter.replace('/', '\\')
|
323
|
-
t = patter[:glob_chars_pos].rfind('\\')
|
324
|
-
# 计算出这批文件实际所在的目录dirname
|
325
|
-
if t == -1: # 模式里没有套子文件夹
|
326
|
-
dirname, basename = root, patter
|
327
|
-
else: # 模式里有套子文件夹
|
328
|
-
dirname, basename = os.path.abspath(os.path.join(root, patter[:t])), patter[t + 1:]
|
329
|
-
basename = basename.replace('<', '[').replace('>', ']')
|
330
|
-
files = map(str, Path(dirname).glob(basename))
|
331
|
-
|
332
|
-
n = len(root) + 1
|
333
|
-
res = [(x[n:] if x.startswith(root) else x) for x in files]
|
334
|
-
# 3 正则匹配 (只要有match成员函数就行,不一定非要正则对象)
|
335
|
-
elif hasattr(patter, 'match'):
|
336
|
-
files = filesmatch('**/*', root=root)
|
337
|
-
res = list(filter(lambda x: patter.match(x), files))
|
338
|
-
# 4 list等迭代对象
|
339
|
-
elif isinstance(patter, (list, tuple, set)):
|
340
|
-
res = []
|
341
|
-
for p in patter: res += filesmatch(p, root=root)
|
342
|
-
else:
|
343
|
-
raise TypeError
|
344
|
-
|
345
|
-
# 2 filetype的筛选
|
346
|
-
res = filesfilter(res, root=root, **kwargs)
|
347
|
-
|
348
|
-
return [x.replace('\\', '/') for x in res]
|
349
|
-
|
350
|
-
|
351
|
-
def filesdel(path, **kwargs):
|
352
|
-
"""删除文件或文件夹
|
353
|
-
支持filesfilter的筛选规则
|
354
|
-
"""
|
355
|
-
for f in filesmatch(path, **kwargs):
|
356
|
-
if os.path.isfile(f):
|
357
|
-
os.remove(f)
|
358
|
-
else:
|
359
|
-
shutil.rmtree(f)
|
360
|
-
# TODO 确保删除后再执行后续代码 但是一直觉得这样写很别扭
|
361
|
-
while os.path.exists(f): pass
|
362
|
-
|
363
|
-
|
364
|
-
def _files_copy_move_base(src, dst, filefunc, dirfunc,
|
365
|
-
*, if_exists=None, treeroot=None, **kwargs):
|
366
|
-
# 1 辅助函数
|
367
|
-
def proc_onefile(f, dst):
|
368
|
-
# dprint(f, dst)
|
369
|
-
# 1 解析dst参数:对文件或目录不同情况做预处理
|
370
|
-
# (输入的时候dst_可以只是目标的父目录,要推算出实际要存储的目标名)
|
371
|
-
if os.path.isfile(f):
|
372
|
-
if os.path.isdir(dst) or dst[-1] in ('/', '\\'):
|
373
|
-
dst = os.path.join(dst, os.path.basename(f))
|
374
|
-
func = filefunc
|
375
|
-
else:
|
376
|
-
if dst[0] in ('/', '\\'):
|
377
|
-
dst = os.path.join(dst, os.path.basename(f))
|
378
|
-
func = dirfunc
|
379
|
-
|
380
|
-
# 2 根据目标是否已存在和if_exists分类处理
|
381
|
-
Path(dst).ensure_dir(pathtype='file')
|
382
|
-
# 目前存在,且不是把文件移向文件夹的操作
|
383
|
-
if os.path.exists(dst):
|
384
|
-
# 根据if_exists参数情况分类处理
|
385
|
-
if if_exists is None: # 智能判断
|
386
|
-
if not filescmp(f, dst): # 如果内容不同则backup
|
387
|
-
Path(dst).backup(move=True)
|
388
|
-
func(f, dst)
|
389
|
-
elif os.path.abspath(f).lower() == os.path.abspath(dst).lower():
|
390
|
-
# 如果内容相同,再判断其是否实际是一个文件,则调用重命名功能
|
391
|
-
os.rename(f, dst)
|
392
|
-
elif if_exists == 'backup':
|
393
|
-
Path(dst).backup(move=True)
|
394
|
-
func(f, dst)
|
395
|
-
elif if_exists == 'replace':
|
396
|
-
filesdel(dst)
|
397
|
-
func(f, dst)
|
398
|
-
elif if_exists == 'ignore':
|
399
|
-
pass # 跳过,不处理
|
400
|
-
else:
|
401
|
-
raise ValueError
|
402
|
-
else:
|
403
|
-
func(f, dst) # TODO 这里有bug \2020LaTeX\C春季教材\初数\初一上\Word+外包商原稿
|
404
|
-
|
405
|
-
# 2 主体代码
|
406
|
-
files = filesmatch(src, **kwargs)
|
407
|
-
|
408
|
-
if len(files) == 1:
|
409
|
-
proc_onefile(files[0], dst)
|
410
|
-
elif len(files) > 1: # 多文件模式拆解为单文件模式操作
|
411
|
-
# 如果设置了 treeroot,这里要预处理下
|
412
|
-
if treeroot:
|
413
|
-
treeroot = filesmatch(treeroot)[0]
|
414
|
-
if treeroot[-1] not in ('/', '\\'):
|
415
|
-
treeroot += '/'
|
416
|
-
n = len(treeroot) if treeroot else 0
|
417
|
-
if treeroot: treeroot = treeroot.replace('\\', '/')
|
418
|
-
|
419
|
-
# 迭代操作
|
420
|
-
for f in files:
|
421
|
-
dst_ = dst
|
422
|
-
if treeroot and f.startswith(treeroot):
|
423
|
-
dst_ = os.path.join(dst, f[n:])
|
424
|
-
proc_onefile(f, dst_)
|
425
|
-
|
426
|
-
|
427
|
-
def filescopy(src, dst, *, if_exists=None, treeroot=None, **kwargs):
|
428
|
-
r"""会自动添加不存在的目录的拷贝
|
429
|
-
:param src: 要处理的目标
|
430
|
-
'a',复制文件a,或者整个文件夹a
|
431
|
-
'a/*.txt',复制文件夹下所有的txt文件
|
432
|
-
更多匹配模式详见 filesmatch
|
433
|
-
:param dst: 移到目标位置
|
434
|
-
'a',
|
435
|
-
如果a是已存在的目录,效果同'a/'
|
436
|
-
如果是已存在的文件,且src只有一个要复制的文件,也是合法的。否则报错
|
437
|
-
错误类型包括,把一个目录复制到已存在的文件
|
438
|
-
把多个文件复制到已存在的文件
|
439
|
-
如果a不存在,则
|
440
|
-
src只是一个待复制的文件时是合法的
|
441
|
-
'a/',(可以省略写具体值,只写父级目录)将src匹配到的所有文件,放到目标a目录下
|
442
|
-
:param if_exists: backup和replace含智能处理,如果内容相同则直接ignore
|
443
|
-
'ignore',跳过
|
444
|
-
'backup'(默认),备份
|
445
|
-
注意多文件操作时,来源不同的文件夹可能有同名文件
|
446
|
-
'replace',强制替换
|
447
|
-
:param treeroot: 输入一个目录名开启该功能选项 (此模式下dst末尾强制要有一个'/')
|
448
|
-
对src中匹配到的所有文件,都会去掉treeroot的父目录前缀
|
449
|
-
然后将剩下文件的所有相对路径结构,拷贝到dst目录下
|
450
|
-
示例:将a目录下所有png图片原结构拷贝到b目录下
|
451
|
-
filescopy('a/**/*.png', 'b/', if_exists='replace', treeroot='a')
|
452
|
-
友情提示:treeroot要跟src使用同样的相对或绝对路径值,否则可能出现意外错误
|
453
|
-
|
454
|
-
>> filescopy('filesmatch/**/*.png', 'filesmatch+/', treeroot='filesmatch')
|
455
|
-
filesmatch: 1.png,a/2.png -> filesmatch+:1.png,a/2.png
|
456
|
-
|
457
|
-
>> filescopy('filesmatch/**/*.png', 'filesmatch+/')
|
458
|
-
filesmatch: 1.png,a/2.png -> filesmatch+:1.png,2.png
|
459
|
-
|
460
|
-
TODO filescopy和filesmove还是有瑕疵和效率问题的,有空要继续优化
|
461
|
-
"""
|
462
|
-
return _files_copy_move_base(src, dst, shutil.copy2, shutil.copytree,
|
463
|
-
if_exists=if_exists, treeroot=treeroot, **kwargs)
|
464
|
-
|
465
|
-
|
466
|
-
def filesmove(src, dst, *, if_exists=None, treeroot=None, **kwargs):
|
467
|
-
r"""与filescopy高度相同,见filescopy文档
|
468
|
-
|
469
|
-
>> filesmove('a.xslx', 'A.xlsx', if_exists='replace') # 等价于 os.rename('a.xlsx', 'A.xlsx')
|
470
|
-
"""
|
471
|
-
return _files_copy_move_base(src, dst, shutil.move, shutil.move,
|
472
|
-
if_exists=if_exists, treeroot=treeroot, **kwargs)
|
473
|
-
|
474
|
-
|
475
|
-
def refinepath(s, reserve=''):
|
476
|
-
"""
|
477
|
-
:param reserve: 保留的字符,例如输入'*?',会保留这两个字符作为通配符
|
478
|
-
"""
|
479
|
-
if not s: return s
|
480
|
-
# 1 去掉路径中的不可见字符,注意这里第1个参数里有一个不可见字符!别乱动这里的代码!
|
481
|
-
s = s.replace(chr(8234), '')
|
482
|
-
chars = set(r'\/:*?"<>|') - set(reserve)
|
483
|
-
for ch in chars: # windows路径中不能包含的字符
|
484
|
-
s = s.replace(ch, '')
|
485
|
-
|
486
|
-
# 2 去除目录、文件名前后的空格
|
487
|
-
s = re.sub(r'\s+([/\\])', r'\1', s)
|
488
|
-
s = re.sub(r'([/\\])\s+', r'\1', s)
|
489
|
-
|
490
|
-
return s
|
491
|
-
|
492
|
-
|
493
|
-
def writefile(ob, path='', *, encoding='utf8', if_exists='backup', suffix=None, root=None, etag=None) -> str:
|
494
|
-
"""往文件path写入ob内容
|
495
|
-
:param ob: 写入的内容
|
496
|
-
如果要写txt文本文件且ob不是文本对象,只会进行简单的字符串化
|
497
|
-
:param path: 写入的文件名,使用空字符串时,会使用etag值
|
498
|
-
:param encoding: 强制写入的编码
|
499
|
-
:param if_exists: 如果文件已存在,要进行的操作
|
500
|
-
:param suffix: 文件扩展名
|
501
|
-
以'.'为开头,设置“候补扩展名”,即只在fn没有指明扩展名时,会采用
|
502
|
-
:param root: 相对位置
|
503
|
-
:return: 返回写入的文件名,这个主要是在写临时文件时有用
|
504
|
-
"""
|
505
|
-
if etag is None: etag = (not path)
|
506
|
-
return Path(path, suffix, root).write(ob,
|
507
|
-
encoding=encoding, if_exists=if_exists,
|
508
|
-
etag=etag).fullpath
|
509
|
-
|
510
|
-
|
511
|
-
def merge_dir(src, dst, if_exists='ignore'):
|
512
|
-
""" 将src目录下的数据拷贝到dst目录
|
513
|
-
"""
|
514
|
-
|
515
|
-
def func(p1, p2):
|
516
|
-
p1.copy(p2, if_exists=if_exists)
|
517
|
-
|
518
|
-
# 只拷文件和空目录,不然逻辑会乱
|
519
|
-
Dir(src).select('**/*', type_='dir', max_size=0).select('**/*', type_='file').procfiles(func, ref_dir=dst)
|
520
|
-
|
521
|
-
|
522
|
-
def extract_files(src, dst, pattern, if_exists='replace'):
|
523
|
-
""" 提取满足pattern模式的文件
|
524
|
-
"""
|
525
|
-
d1, d2 = Dir(src), Dir(dst)
|
526
|
-
files = d1.select(pattern).files
|
527
|
-
for f in files:
|
528
|
-
p1, p2 = Path(d1 / f), Path(d2 / f)
|
529
|
-
p1.copy(p2, if_exists=if_exists)
|