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.
Files changed (186) hide show
  1. pyxllib/__init__.py +9 -2
  2. pyxllib/algo/__init__.py +8 -0
  3. pyxllib/algo/disjoint.py +54 -0
  4. pyxllib/algo/geo.py +541 -0
  5. pyxllib/{util/mathlib.py → algo/intervals.py} +172 -36
  6. pyxllib/algo/matcher.py +389 -0
  7. pyxllib/algo/newbie.py +166 -0
  8. pyxllib/algo/pupil.py +629 -0
  9. pyxllib/algo/shapelylib.py +67 -0
  10. pyxllib/algo/specialist.py +241 -0
  11. pyxllib/algo/stat.py +494 -0
  12. pyxllib/algo/treelib.py +149 -0
  13. pyxllib/algo/unitlib.py +66 -0
  14. pyxllib/autogui/__init__.py +5 -0
  15. pyxllib/autogui/activewin.py +246 -0
  16. pyxllib/autogui/all.py +9 -0
  17. pyxllib/autogui/autogui.py +852 -0
  18. pyxllib/autogui/uiautolib.py +362 -0
  19. pyxllib/autogui/virtualkey.py +102 -0
  20. pyxllib/autogui/wechat.py +827 -0
  21. pyxllib/autogui/wechat_msg.py +421 -0
  22. pyxllib/autogui/wxautolib.py +84 -0
  23. pyxllib/cv/__init__.py +1 -11
  24. pyxllib/cv/expert.py +267 -0
  25. pyxllib/cv/{imlib.py → imfile.py} +18 -83
  26. pyxllib/cv/imhash.py +39 -0
  27. pyxllib/cv/pupil.py +9 -0
  28. pyxllib/cv/rgbfmt.py +1525 -0
  29. pyxllib/cv/slidercaptcha.py +137 -0
  30. pyxllib/cv/trackbartools.py +163 -49
  31. pyxllib/cv/xlcvlib.py +1040 -0
  32. pyxllib/cv/xlpillib.py +423 -0
  33. pyxllib/data/__init__.py +0 -0
  34. pyxllib/data/echarts.py +240 -0
  35. pyxllib/data/jsonlib.py +89 -0
  36. pyxllib/{util/oss2_.py → data/oss.py} +11 -9
  37. pyxllib/data/pglib.py +1127 -0
  38. pyxllib/data/sqlite.py +568 -0
  39. pyxllib/{util → data}/sqllib.py +13 -31
  40. pyxllib/ext/JLineViewer.py +505 -0
  41. pyxllib/ext/__init__.py +6 -0
  42. pyxllib/{util → ext}/demolib.py +119 -35
  43. pyxllib/ext/drissionlib.py +277 -0
  44. pyxllib/ext/kq5034lib.py +12 -0
  45. pyxllib/{util/main.py → ext/old.py} +122 -284
  46. pyxllib/ext/qt.py +449 -0
  47. pyxllib/ext/robustprocfile.py +497 -0
  48. pyxllib/ext/seleniumlib.py +76 -0
  49. pyxllib/{util/tklib.py → ext/tk.py} +10 -11
  50. pyxllib/ext/unixlib.py +827 -0
  51. pyxllib/ext/utools.py +351 -0
  52. pyxllib/{util/webhooklib.py → ext/webhook.py} +45 -17
  53. pyxllib/ext/win32lib.py +40 -0
  54. pyxllib/ext/wjxlib.py +88 -0
  55. pyxllib/ext/wpsapi.py +124 -0
  56. pyxllib/ext/xlwork.py +9 -0
  57. pyxllib/ext/yuquelib.py +1105 -0
  58. pyxllib/file/__init__.py +17 -0
  59. pyxllib/file/docxlib.py +761 -0
  60. pyxllib/{util → file}/gitlib.py +40 -27
  61. pyxllib/file/libreoffice.py +165 -0
  62. pyxllib/file/movielib.py +148 -0
  63. pyxllib/file/newbie.py +10 -0
  64. pyxllib/file/onenotelib.py +1469 -0
  65. pyxllib/file/packlib/__init__.py +330 -0
  66. pyxllib/{util → file/packlib}/zipfile.py +598 -195
  67. pyxllib/file/pdflib.py +426 -0
  68. pyxllib/file/pupil.py +185 -0
  69. pyxllib/file/specialist/__init__.py +685 -0
  70. pyxllib/{basic/_5_dirlib.py → file/specialist/dirlib.py} +364 -93
  71. pyxllib/file/specialist/download.py +193 -0
  72. pyxllib/file/specialist/filelib.py +2829 -0
  73. pyxllib/file/xlsxlib.py +3131 -0
  74. pyxllib/file/xlsyncfile.py +341 -0
  75. pyxllib/prog/__init__.py +5 -0
  76. pyxllib/prog/cachetools.py +64 -0
  77. pyxllib/prog/deprecatedlib.py +233 -0
  78. pyxllib/prog/filelock.py +42 -0
  79. pyxllib/prog/ipyexec.py +253 -0
  80. pyxllib/prog/multiprogs.py +940 -0
  81. pyxllib/prog/newbie.py +451 -0
  82. pyxllib/prog/pupil.py +1197 -0
  83. pyxllib/{sitepackages.py → prog/sitepackages.py} +5 -3
  84. pyxllib/prog/specialist/__init__.py +391 -0
  85. pyxllib/prog/specialist/bc.py +203 -0
  86. pyxllib/prog/specialist/browser.py +497 -0
  87. pyxllib/prog/specialist/common.py +347 -0
  88. pyxllib/prog/specialist/datetime.py +199 -0
  89. pyxllib/prog/specialist/tictoc.py +240 -0
  90. pyxllib/prog/specialist/xllog.py +180 -0
  91. pyxllib/prog/xlosenv.py +108 -0
  92. pyxllib/stdlib/__init__.py +17 -0
  93. pyxllib/{util → stdlib}/tablepyxl/__init__.py +1 -3
  94. pyxllib/{util → stdlib}/tablepyxl/style.py +1 -1
  95. pyxllib/{util → stdlib}/tablepyxl/tablepyxl.py +2 -4
  96. pyxllib/text/__init__.py +8 -0
  97. pyxllib/text/ahocorasick.py +39 -0
  98. pyxllib/text/airscript.js +744 -0
  99. pyxllib/text/charclasslib.py +121 -0
  100. pyxllib/text/jiebalib.py +267 -0
  101. pyxllib/text/jinjalib.py +32 -0
  102. pyxllib/text/jsa_ai_prompt.md +271 -0
  103. pyxllib/text/jscode.py +922 -0
  104. pyxllib/text/latex/__init__.py +158 -0
  105. pyxllib/text/levenshtein.py +303 -0
  106. pyxllib/text/nestenv.py +1215 -0
  107. pyxllib/text/newbie.py +300 -0
  108. pyxllib/text/pupil/__init__.py +8 -0
  109. pyxllib/text/pupil/common.py +1121 -0
  110. pyxllib/text/pupil/xlalign.py +326 -0
  111. pyxllib/text/pycode.py +47 -0
  112. pyxllib/text/specialist/__init__.py +8 -0
  113. pyxllib/text/specialist/common.py +112 -0
  114. pyxllib/text/specialist/ptag.py +186 -0
  115. pyxllib/text/spellchecker.py +172 -0
  116. pyxllib/text/templates/echart_base.html +11 -0
  117. pyxllib/text/templates/highlight_code.html +17 -0
  118. pyxllib/text/templates/latex_editor.html +103 -0
  119. pyxllib/text/vbacode.py +17 -0
  120. pyxllib/text/xmllib.py +747 -0
  121. pyxllib/xl.py +39 -0
  122. pyxllib/xlcv.py +17 -0
  123. pyxllib-0.3.197.dist-info/METADATA +48 -0
  124. pyxllib-0.3.197.dist-info/RECORD +126 -0
  125. {pyxllib-0.0.43.dist-info → pyxllib-0.3.197.dist-info}/WHEEL +4 -5
  126. pyxllib/basic/_1_strlib.py +0 -945
  127. pyxllib/basic/_2_timelib.py +0 -488
  128. pyxllib/basic/_3_pathlib.py +0 -916
  129. pyxllib/basic/_4_loglib.py +0 -419
  130. pyxllib/basic/__init__.py +0 -54
  131. pyxllib/basic/arrow_.py +0 -250
  132. pyxllib/basic/chardet_.py +0 -66
  133. pyxllib/basic/dirlib.py +0 -529
  134. pyxllib/basic/dprint.py +0 -202
  135. pyxllib/basic/extension.py +0 -12
  136. pyxllib/basic/judge.py +0 -31
  137. pyxllib/basic/log.py +0 -204
  138. pyxllib/basic/pathlib_.py +0 -705
  139. pyxllib/basic/pytictoc.py +0 -102
  140. pyxllib/basic/qiniu_.py +0 -61
  141. pyxllib/basic/strlib.py +0 -761
  142. pyxllib/basic/timer.py +0 -132
  143. pyxllib/cv/cv.py +0 -834
  144. pyxllib/cv/cvlib/_1_geo.py +0 -543
  145. pyxllib/cv/cvlib/_2_cvprcs.py +0 -309
  146. pyxllib/cv/cvlib/_2_imgproc.py +0 -594
  147. pyxllib/cv/cvlib/_3_pilprcs.py +0 -80
  148. pyxllib/cv/cvlib/_4_cvimg.py +0 -211
  149. pyxllib/cv/cvlib/__init__.py +0 -10
  150. pyxllib/cv/debugtools.py +0 -82
  151. pyxllib/cv/fitz_.py +0 -300
  152. pyxllib/cv/installer.py +0 -42
  153. pyxllib/debug/_0_installer.py +0 -38
  154. pyxllib/debug/_1_typelib.py +0 -277
  155. pyxllib/debug/_2_chrome.py +0 -198
  156. pyxllib/debug/_3_showdir.py +0 -161
  157. pyxllib/debug/_4_bcompare.py +0 -140
  158. pyxllib/debug/__init__.py +0 -49
  159. pyxllib/debug/bcompare.py +0 -132
  160. pyxllib/debug/chrome.py +0 -198
  161. pyxllib/debug/installer.py +0 -38
  162. pyxllib/debug/showdir.py +0 -158
  163. pyxllib/debug/typelib.py +0 -278
  164. pyxllib/image/__init__.py +0 -12
  165. pyxllib/torch/__init__.py +0 -20
  166. pyxllib/torch/modellib.py +0 -37
  167. pyxllib/torch/trainlib.py +0 -344
  168. pyxllib/util/__init__.py +0 -20
  169. pyxllib/util/aip_.py +0 -141
  170. pyxllib/util/casiadb.py +0 -59
  171. pyxllib/util/excellib.py +0 -495
  172. pyxllib/util/filelib.py +0 -612
  173. pyxllib/util/jsondata.py +0 -27
  174. pyxllib/util/jsondata2.py +0 -92
  175. pyxllib/util/labelmelib.py +0 -139
  176. pyxllib/util/onepy/__init__.py +0 -29
  177. pyxllib/util/onepy/onepy.py +0 -574
  178. pyxllib/util/onepy/onmanager.py +0 -170
  179. pyxllib/util/pyautogui_.py +0 -219
  180. pyxllib/util/textlib.py +0 -1305
  181. pyxllib/util/unorder.py +0 -22
  182. pyxllib/util/xmllib.py +0 -639
  183. pyxllib-0.0.43.dist-info/METADATA +0 -39
  184. pyxllib-0.0.43.dist-info/RECORD +0 -80
  185. pyxllib-0.0.43.dist-info/top_level.txt +0 -1
  186. {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)