pyxllib 0.3.200__py3-none-any.whl → 3.201.1__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 +0 -7
- pyxllib/algo/geo.py +0 -4
- pyxllib/algo/treelib.py +0 -4
- pyxllib/algo/unitlib.py +0 -4
- pyxllib/autogui/autogui.py +1 -7
- pyxllib/data/echarts.py +0 -4
- pyxllib/data/jsonlib.py +0 -4
- pyxllib/data/pglib.py +1 -17
- pyxllib/ext/demolib.py +6 -1
- pyxllib/ext/robustprocfile.py +0 -4
- pyxllib/ext/unixlib.py +1 -7
- pyxllib/ext/utools.py +0 -6
- pyxllib/ext/wjxlib.py +10 -7
- pyxllib/ext/yuquelib.py +9 -4
- pyxllib/file/docxlib.py +0 -4
- pyxllib/file/movielib.py +0 -4
- pyxllib/file/pdflib.py +0 -4
- pyxllib/file/specialist/__init__.py +0 -4
- pyxllib/file/specialist/filelib.py +0 -4
- pyxllib/file/xlsxlib.py +2 -11
- pyxllib/prog/cachetools.py +1 -7
- pyxllib/prog/pupil.py +11 -0
- pyxllib/prog/specialist/__init__.py +0 -43
- pyxllib/prog/xlosenv.py +2 -0
- pyxllib/text/ahocorasick.py +0 -3
- pyxllib/text/airscript.js +10 -0
- pyxllib/text/jinjalib.py +0 -5
- pyxllib/text/levenshtein.py +2 -2
- pyxllib/text/xmllib.py +0 -6
- pyxllib-3.201.1.dist-info/METADATA +296 -0
- {pyxllib-0.3.200.dist-info → pyxllib-3.201.1.dist-info}/RECORD +33 -34
- pyxllib/ext/old.py +0 -663
- pyxllib-0.3.200.dist-info/METADATA +0 -48
- {pyxllib-0.3.200.dist-info → pyxllib-3.201.1.dist-info}/WHEEL +0 -0
- {pyxllib-0.3.200.dist-info → pyxllib-3.201.1.dist-info}/licenses/LICENSE +0 -0
pyxllib/ext/old.py
DELETED
@@ -1,663 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
# -*- coding: utf-8 -*-
|
3
|
-
# @Author : 陈坤泽
|
4
|
-
# @Email : 877362867@qq.com
|
5
|
-
# @Date : 2018/07/12 09:32
|
6
|
-
|
7
|
-
|
8
|
-
""" 一系列还未整理的旧代码
|
9
|
-
|
10
|
-
任何模块代码的第一个字符串用来写文档注释
|
11
|
-
|
12
|
-
因为util下的debuglib、textlib、filelib的功能拆分并不是特别精确,所以一般不对外开放接口
|
13
|
-
而是由util作为统一的一个大工具箱接口对外开放
|
14
|
-
"""
|
15
|
-
|
16
|
-
import filecmp
|
17
|
-
import shutil
|
18
|
-
import sys
|
19
|
-
import textwrap
|
20
|
-
from os.path import join as pathjoin
|
21
|
-
from collections import OrderedDict, Counter
|
22
|
-
import base64
|
23
|
-
import requests
|
24
|
-
|
25
|
-
from bs4 import BeautifulSoup
|
26
|
-
|
27
|
-
|
28
|
-
def ________C_文本处理________():
|
29
|
-
pass
|
30
|
-
|
31
|
-
|
32
|
-
def 从部门Confluence获取数据(url):
|
33
|
-
cookies = getattr(从部门Confluence获取数据, 'cookies', None)
|
34
|
-
if cookies: # 如果存储了cookies,尝试使用
|
35
|
-
r = requests.get(url, cookies=cookies)
|
36
|
-
if not cookies or not r.cookies._cookies: # 如果没有cookies或者读取失败(使用_cookies是否为空判断登陆是否成功),则重新登陆获取cookies
|
37
|
-
r = requests.get('http://doc.klxuexi.org/login.action', auth=('chenkz', 'klxx11235813'))
|
38
|
-
cookies = r.cookies
|
39
|
-
r = requests.get(url, cookies=cookies)
|
40
|
-
从部门Confluence获取数据.cookies = cookies
|
41
|
-
return r.text
|
42
|
-
|
43
|
-
|
44
|
-
def ReadFromUrl(url):
|
45
|
-
"""从url获得文本数据
|
46
|
-
|
47
|
-
对特殊的网页有专门优化
|
48
|
-
"""
|
49
|
-
if 'paste.ubuntu.com' in url:
|
50
|
-
return read_from_ubuntu(url)
|
51
|
-
elif url.startswith('http://doc.klxuexi.org'): # 这个写法主要是针对工时统计表
|
52
|
-
# TODO:如果是工时表,应该做成DataFrame结构数据
|
53
|
-
text = 从部门Confluence获取数据(url)
|
54
|
-
soup = BeautifulSoup(text, 'lxml') # 解析网页得到soup对象
|
55
|
-
content = soup.find_all(name='div', attrs={'id': 'main-content'})[0] # 提取conteng部分的内容
|
56
|
-
return content.get_text('\t') # 仅获得文本信息,每项用\t隔开
|
57
|
-
else:
|
58
|
-
r = requests.get(url)
|
59
|
-
soup = BeautifulSoup(r.text, 'lxml')
|
60
|
-
return soup.get_text()
|
61
|
-
|
62
|
-
|
63
|
-
def EnsureContent(ob=None, encoding='utf8'):
|
64
|
-
"""
|
65
|
-
未输入ob参数时,自动从控制台获取文本
|
66
|
-
|
67
|
-
输入的如果是字符串内容,则返回字符串内容
|
68
|
-
输入的如果是文件名,则读取文件的内容返回
|
69
|
-
输入的如果是url,则返回html爬取内容
|
70
|
-
"""
|
71
|
-
# TODO: 如果输入的是一个文件指针,也能调用f.read()返回所有内容
|
72
|
-
# TODO: 增加鲁棒性判断,如果输入的不是字符串类型也要有出错判断
|
73
|
-
|
74
|
-
if ob is None:
|
75
|
-
return sys.stdin.read() # 注意输入是按 Ctrl + D 结束
|
76
|
-
elif ob.find('\n') >= 0 or len(ob) > 200: # 如果存在回车符,不用想了,直接认为是字符串
|
77
|
-
return ob
|
78
|
-
elif os.path.exists(ob): # 如果存在这样的文件,那就读取文件内容
|
79
|
-
if ob.endswith('.pdf'): # 如果是pdf格式,则读取后转换为文本格式
|
80
|
-
res = map(lambda page: page.getText('text'), fitz.open(ob))
|
81
|
-
return '\n'.join(res)
|
82
|
-
elif ob.endswith('.docx'):
|
83
|
-
import textract
|
84
|
-
text = textract.process(ob)
|
85
|
-
return text.decode(encoding, errors='ignore')
|
86
|
-
elif ob.endswith('.doc'):
|
87
|
-
from pyxllib.ex.win32lib import XlWin32WordApplication
|
88
|
-
app = XlWin32WordApplication.get_app()
|
89
|
-
a = app.open_doc(ob)
|
90
|
-
s = a.content
|
91
|
-
a.Close()
|
92
|
-
return s
|
93
|
-
else: # 其他按文本格式处理
|
94
|
-
if ob.endswith(r'.tex'):
|
95
|
-
encoding = 'gbk' # TODO:强制转为gbk,这个后续要改
|
96
|
-
with open(ob, 'r', errors='ignore', encoding=encoding) as f:
|
97
|
-
# 默认编码是跟平台有关,比如windows是gbk
|
98
|
-
return f.read()
|
99
|
-
elif ob.startswith('http'):
|
100
|
-
try:
|
101
|
-
return ReadFromUrl(ob)
|
102
|
-
except:
|
103
|
-
# 读取失败则返回原内容
|
104
|
-
return ob
|
105
|
-
elif isinstance(ob, pd.DataFrame):
|
106
|
-
# 还未开发
|
107
|
-
pass
|
108
|
-
else:
|
109
|
-
# 判断不了的情况,也认为是字符串
|
110
|
-
return ob
|
111
|
-
|
112
|
-
|
113
|
-
################################################################################
|
114
|
-
# 文 本 处 理
|
115
|
-
################################################################################
|
116
|
-
|
117
|
-
|
118
|
-
def PrintFullTable(df, *, 最后一列左对齐=False, columns=None):
|
119
|
-
if isinstance(df, (list, tuple)):
|
120
|
-
df = pd.DataFrame.from_records(df, columns=columns)
|
121
|
-
|
122
|
-
if len(df) < 1:
|
123
|
-
return
|
124
|
-
"""参考文档: http://pandas.pydata.org/pandas-docs/stable/options.html"""
|
125
|
-
with pd.option_context('display.max_rows', None, # 没有行数限制
|
126
|
-
'display.max_columns', None, # 没有列数限制(超过列数会分行显示)
|
127
|
-
'display.width', None, # 没有列宽限制
|
128
|
-
'display.max_colwidth', 10 ** 6, # 单列宽度上限
|
129
|
-
# 'display.colheader_justify', 'left', # 列标题左对齐
|
130
|
-
'display.unicode.east_asian_width', True, # 中文输出必备选项,用来控制正确的域宽
|
131
|
-
):
|
132
|
-
if 最后一列左对齐: # 但是这里涉及中文的时候又会出错~~
|
133
|
-
# df.iloc[:, -1] = (lambda s: s.str.ljust(s.str.len().max()))(df.iloc[:, -1]) # 最后一列左对齐
|
134
|
-
def func(s):
|
135
|
-
return s.str.ljust(s.str.len().max())
|
136
|
-
|
137
|
-
df.iloc[:, -1] = func(df.iloc[:, -1]) # 最后一列左对齐
|
138
|
-
print(df)
|
139
|
-
# print(df.info())
|
140
|
-
|
141
|
-
|
142
|
-
def 重定向到浏览器显示(fileName=None):
|
143
|
-
"""第一次执行时,必须给一个参数,代表重定向的输出文件名
|
144
|
-
|
145
|
-
第二次执行时,不要输入参数,此时会弹出chrome.exe显示已保存的所有输出内容
|
146
|
-
"""
|
147
|
-
if fileName:
|
148
|
-
重定向到浏览器显示.fileName = fileName
|
149
|
-
重定向到浏览器显示.oldTarget = sys.stdout
|
150
|
-
sys.stdout = open(fileName, 'w')
|
151
|
-
else: # 如果没写参数,则显示
|
152
|
-
sys.stdout = 重定向到浏览器显示.oldTarget
|
153
|
-
subprocess.run(['chrome.exe', 重定向到浏览器显示.fileName])
|
154
|
-
|
155
|
-
|
156
|
-
def regular_cut(old_str, pattern, flags=0):
|
157
|
-
r"""返回第1个参数是新的new_str,去掉了被提取的元素
|
158
|
-
返回第2个参数是提取出来的数据列表
|
159
|
-
|
160
|
-
>>> regular_cut('abc123de4f', r'\d+')
|
161
|
-
('abcdef', ['123', '4'])
|
162
|
-
>>> regular_cut('abc123de4f', r'c(\d+)')
|
163
|
-
('abde4f', ['123'])
|
164
|
-
"""
|
165
|
-
new_str = re.sub(pattern, '', old_str, flags=flags)
|
166
|
-
elements = re.findall(pattern, old_str, flags=flags)
|
167
|
-
return new_str, elements
|
168
|
-
|
169
|
-
|
170
|
-
def research(pattern, string):
|
171
|
-
""" .能匹配所有字符
|
172
|
-
返回第一个匹配的字符串(的group(1)),结果会去掉左右空白
|
173
|
-
如果找不到则返回空字符串
|
174
|
-
"""
|
175
|
-
m = re.search(pattern, string, flags=re.DOTALL)
|
176
|
-
return m.group(1).strip() if m else ''
|
177
|
-
|
178
|
-
|
179
|
-
def ________D_文件目录相关函数________():
|
180
|
-
""""""
|
181
|
-
pass
|
182
|
-
|
183
|
-
|
184
|
-
################################################################################
|
185
|
-
### 目录相关函数
|
186
|
-
################################################################################
|
187
|
-
|
188
|
-
|
189
|
-
def SetWkDir(wkDir=None, key=None):
|
190
|
-
r"""用过的工作目录存在字典wkdirs {int:string},原始目录用0索引,上一个目录用-1索引
|
191
|
-
新设的目录可以添加自己的索引key
|
192
|
-
|
193
|
-
SetWkDir(0)
|
194
|
-
SetWkDir('word')
|
195
|
-
SetWkDir(0)
|
196
|
-
SetWkDir(-1)
|
197
|
-
"""
|
198
|
-
wkDir = str(wkDir)
|
199
|
-
SetWkDir.wkdirs = getattr(SetWkDir, 'wkdirs', {'0': os.getcwd(), '-1': os.getcwd()})
|
200
|
-
wkdirs = SetWkDir.wkdirs
|
201
|
-
lastWkDir = os.getcwd()
|
202
|
-
|
203
|
-
if wkDir in wkdirs:
|
204
|
-
os.chdir(wkdirs[wkDir])
|
205
|
-
elif wkDir == '': # 如果输入空字符串,则返回当前工作目录(这在使用os.path.dirname('a.tex')时是会发生的)
|
206
|
-
return os.getcwd()
|
207
|
-
else:
|
208
|
-
os.chdir(wkDir) # 切换到目标工作目录
|
209
|
-
if key: # 如果输入了key,则存储当前工作目录
|
210
|
-
wkdirs[key] = wkDir
|
211
|
-
|
212
|
-
wkdirs[-1] = lastWkDir
|
213
|
-
return lastWkDir # 返回切换前的目录
|
214
|
-
|
215
|
-
|
216
|
-
def SmartCopyFiles(files, inFolder, outFolder):
|
217
|
-
"""将files里的文件移到folder目录里,如果folder里已经存在对应文件则自动进行备份"""
|
218
|
-
inFd = File(inFolder)
|
219
|
-
outFd = File(outFolder)
|
220
|
-
|
221
|
-
for file in files:
|
222
|
-
inFile = inFd / file
|
223
|
-
if not inFile.exists(): # 如果原目录里并不含有该文件则continue
|
224
|
-
continue
|
225
|
-
outFile = outFd / file
|
226
|
-
if outFile.exists():
|
227
|
-
if filecmp.cmp(inFile, outFile):
|
228
|
-
continue # 如果两个文件是相同的,不用操作,可以直接处理下一个
|
229
|
-
else:
|
230
|
-
File(outFile).backup() # 如果不相同,则对outFile进行备份
|
231
|
-
shutil.copy2(inFile, outFile)
|
232
|
-
File(outFile).backup() # 对拷贝过来的文件也提前做好备份
|
233
|
-
|
234
|
-
|
235
|
-
def MyMove(folder1, folder2):
|
236
|
-
"""将目录1里的文件复制到目录2,如果目录2已存在文件,则对其调用备份"""
|
237
|
-
pass
|
238
|
-
|
239
|
-
|
240
|
-
def 多规则字符串筛选(列表, glob筛选=None, *, 正则筛选=None, 指定名筛选=None, 去除备份文件=False):
|
241
|
-
"""该函数主要供文件处理使用,其它字符串算法慎用"""
|
242
|
-
if glob筛选:
|
243
|
-
列表 = list(filter(lambda x: File(x).match(glob筛选), 列表))
|
244
|
-
|
245
|
-
# 只挑选出满足正则条件的文件名(目录名)
|
246
|
-
if 正则筛选:
|
247
|
-
列表 = list(filter(lambda x: re.match(正则筛选, x, flags=re.IGNORECASE), 列表))
|
248
|
-
|
249
|
-
if 指定名筛选:
|
250
|
-
列表 = list(filter(lambda x: x in 指定名筛选, 列表))
|
251
|
-
|
252
|
-
if 去除备份文件:
|
253
|
-
列表 = list(filter(lambda x: not File(x).backup_time, 列表))
|
254
|
-
|
255
|
-
return 列表
|
256
|
-
|
257
|
-
|
258
|
-
def 递归删除空目录(rootPath):
|
259
|
-
fd = CBaseFolder(rootPath)
|
260
|
-
for f in fd.Folders():
|
261
|
-
d = CBaseFolder(pathjoin(rootPath, f))
|
262
|
-
if d.大小():
|
263
|
-
递归删除空目录(d.name)
|
264
|
-
else:
|
265
|
-
d.删除()
|
266
|
-
|
267
|
-
|
268
|
-
class CBaseFolder(object):
|
269
|
-
def __init__(self, s='.'):
|
270
|
-
"""TODO:应该支持'a/*.eps'这种操作,指定目录的同时,也进行了文件筛选"""
|
271
|
-
self.path = GetFullPathClass(s)
|
272
|
-
self.name = str(self.path)
|
273
|
-
self.files = self.Files()
|
274
|
-
if os.path.exists(self.name):
|
275
|
-
self.folderStats = os.stat(s)
|
276
|
-
|
277
|
-
def Files(self, glob筛选=None, *, 正则筛选=None, 指定名筛选=None, 去除备份文件=False):
|
278
|
-
"""注意:正则规则和一般的文件匹配规则是不一样的!
|
279
|
-
比如glob中的'*.log',在正则中应该谢伟'.*[.]log'
|
280
|
-
|
281
|
-
使用举例:
|
282
|
-
f.Files('*.tex', 正则筛选=r'ch[a-zA-Z]*[.]tex', 指定名筛选 = ('chePre.tex', 'cheRev.tex'))
|
283
|
-
"""
|
284
|
-
if not self:
|
285
|
-
return list()
|
286
|
-
|
287
|
-
ls = os.listdir(self.name)
|
288
|
-
ls = list(filter(self.IsFile, ls))
|
289
|
-
files = 多规则字符串筛选(ls, glob筛选, 正则筛选=正则筛选, 指定名筛选=指定名筛选, 去除备份文件=去除备份文件)
|
290
|
-
files = natural_sort(files)
|
291
|
-
return files
|
292
|
-
|
293
|
-
def 递归获取文件(self, 过滤器=lambda x: True):
|
294
|
-
"""过滤器输入一个函数,文件名要满足指定条件才会被提取"""
|
295
|
-
for f in list(filter(过滤器, self.files)): yield pathjoin(self.path, f)
|
296
|
-
for folder in self.Folders():
|
297
|
-
try: # 有些系统目录会读取不了
|
298
|
-
fd = CBaseFolder(pathjoin(self.name, folder))
|
299
|
-
for f in fd.递归获取文件(过滤器): yield f
|
300
|
-
except:
|
301
|
-
continue
|
302
|
-
|
303
|
-
def Folders(self, glob筛选=None, *, 正则筛选=None, 指定名筛选=None, 去除备份文件=False):
|
304
|
-
ls = os.listdir(self.name)
|
305
|
-
ls = list(filter(self.IsFolder, ls))
|
306
|
-
return 多规则字符串筛选(ls, glob筛选, 正则筛选=正则筛选, 指定名筛选=指定名筛选, 去除备份文件=去除备份文件)
|
307
|
-
|
308
|
-
def IsFile(self, f):
|
309
|
-
return os.path.isfile(os.path.join(self.name, f))
|
310
|
-
|
311
|
-
def IsFolder(self, f):
|
312
|
-
if f in ('$RECYCLE.BIN', 'Recovery', 'System Volume Information'): # 过滤掉无需访问的目录
|
313
|
-
return False
|
314
|
-
else:
|
315
|
-
return os.path.isdir(os.path.join(self.name, f))
|
316
|
-
|
317
|
-
def FilesRename(self, origin, target, *, 目标目录=None):
|
318
|
-
"""使用正则规则匹配文件名,并重命名"""
|
319
|
-
files = self.Files(正则筛选=origin)
|
320
|
-
if not 目标目录: # 如果没有设置目标目录,则以该类所在目录为准
|
321
|
-
目标目录 = self.name
|
322
|
-
for fn in files:
|
323
|
-
f = File(os.path.join(self.name, fn))
|
324
|
-
目标名称 = re.sub(origin, target, fn, flags=re.IGNORECASE)
|
325
|
-
f.rename(os.path.join(目标目录, 目标名称))
|
326
|
-
|
327
|
-
def 递归输出文件列表(self, *, 当前层级=0, 控制宽度=None):
|
328
|
-
"""占秋意见:可以根据扩展名简化输出内容"""
|
329
|
-
fileString = ', '.join(self.files)
|
330
|
-
if isinstance(控制宽度, int):
|
331
|
-
fileString = textwrap.shorten(fileString, 控制宽度, placeholder='...')
|
332
|
-
s = '{}【{}】({}): {}'.format('\t' * 当前层级, self.path.stem, len(self.files), fileString)
|
333
|
-
print(s)
|
334
|
-
for folder in self.Folders():
|
335
|
-
try: # 有些系统目录会读取不了
|
336
|
-
fd = CBaseFolder(pathjoin(self.name, folder))
|
337
|
-
fd.递归输出文件列表(当前层级=当前层级 + 1, 控制宽度=控制宽度)
|
338
|
-
except:
|
339
|
-
continue
|
340
|
-
|
341
|
-
def 大小(self):
|
342
|
-
return File(self.name).size
|
343
|
-
|
344
|
-
def 删除(self):
|
345
|
-
shutil.rmtree(self.name, ignore_errors=True)
|
346
|
-
|
347
|
-
def __bool__(self):
|
348
|
-
return os.path.isdir(str(self.path))
|
349
|
-
|
350
|
-
def __str__(self):
|
351
|
-
return self.name
|
352
|
-
|
353
|
-
|
354
|
-
def 自定义正则规则转为标准正则表达式(s):
|
355
|
-
s = s.replace('?', r'[\u4e00-\u9fa5]') # 中文问号匹配任意一个中文字符
|
356
|
-
return s
|
357
|
-
|
358
|
-
|
359
|
-
def 文件搜索匹配(源目录, 自定义正则规则, *, 目标类型=('文件', '目录')):
|
360
|
-
""" 目标类型=('文件','目录') """
|
361
|
-
匹配文件 = list()
|
362
|
-
源目录前缀长度 = len(源目录)
|
363
|
-
正则规则 = 自定义正则规则转为标准正则表达式(自定义正则规则)
|
364
|
-
所有目录 = tuple(os.walk(源目录))
|
365
|
-
for 当前目录名, 包含目录, 包含文件 in 所有目录:
|
366
|
-
parts = File(当前目录名).parts
|
367
|
-
if '.git' in parts or '$RECYCLE.BIN' in parts: # 去掉'.git'这个备份目录,'$RECYCLE.BIN'这个不知啥鬼目录
|
368
|
-
continue
|
369
|
-
相对目录 = 当前目录名[源目录前缀长度 + 1:]
|
370
|
-
if '目录' in 目标类型:
|
371
|
-
if re.search(正则规则, 相对目录):
|
372
|
-
匹配文件.append(相对目录)
|
373
|
-
if '文件' in 目标类型:
|
374
|
-
for 文件 in 包含文件:
|
375
|
-
相对路径 = os.path.join(相对目录, 文件)
|
376
|
-
if re.search(正则规则, 相对路径):
|
377
|
-
匹配文件.append(相对路径)
|
378
|
-
return natural_sort(匹配文件)
|
379
|
-
|
380
|
-
|
381
|
-
def 文件复制(源目录, 自定义正则规则, 目标目录, 新正则名称, *, 目标类型=('文件',)):
|
382
|
-
""" 目标类型=('文件','目录') """
|
383
|
-
匹配文件 = 文件搜索匹配(源目录, 自定义正则规则, 目标类型=目标类型)
|
384
|
-
# 获得匹配文件后,需要从后往前改,否则产生连锁反应会索引不到
|
385
|
-
|
386
|
-
|
387
|
-
def 文件重命名(源目录, 自定义正则规则, 新正则名称, *, 目标类型=('文件',), 目标目录=None, 调试=True, 输出=True, 覆盖操作=False):
|
388
|
-
"""因为这个操作风险非常大,所以默认情况下是调试模式,必须手动指定进入非调试模式才会进行实际工作
|
389
|
-
|
390
|
-
使用示例:文件重命名(r'D:\2017LaTeX\B暑假教材\高数\高一教师版 - 测试\figs - 副本',
|
391
|
-
r'^(.*?)-eps-converted-to[.]png', r'\1.png', 调试=False)
|
392
|
-
"""
|
393
|
-
ls = list()
|
394
|
-
匹配文件 = 文件搜索匹配(源目录, 自定义正则规则, 目标类型=目标类型)
|
395
|
-
正则规则 = 自定义正则规则转为标准正则表达式(自定义正则规则)
|
396
|
-
if not 目标目录:
|
397
|
-
目标目录 = 源目录
|
398
|
-
for f in reversed(匹配文件):
|
399
|
-
f2 = re.sub(正则规则, 新正则名称, f)
|
400
|
-
ls.append([f, f2])
|
401
|
-
if not 调试:
|
402
|
-
targetName = os.path.join(目标目录, f2)
|
403
|
-
f3 = File(targetName)
|
404
|
-
if f3:
|
405
|
-
print('文件已存在:', f3.name)
|
406
|
-
if 覆盖操作:
|
407
|
-
f3.delete()
|
408
|
-
os.rename(os.path.join(源目录, f), targetName)
|
409
|
-
else:
|
410
|
-
os.rename(os.path.join(源目录, f), targetName)
|
411
|
-
|
412
|
-
df = pd.DataFrame.from_records(ls, columns=('原文件名', '目标文件名'))
|
413
|
-
if 输出:
|
414
|
-
PrintFullTable(df)
|
415
|
-
return df
|
416
|
-
|
417
|
-
|
418
|
-
# def 目录下查找文本(目录, 文件名筛选, 目标文本):
|
419
|
-
# ls = 文件搜索匹配(目录, 文件名筛选, 目标类型=('文件',))
|
420
|
-
# ls = list(filter(lambda x: Path(x).backup_time == '', ls)) # 去除备份文件
|
421
|
-
# ls = natural_sort(ls)
|
422
|
-
# 文件名 = list()
|
423
|
-
# 出现次数 = list()
|
424
|
-
# 行号 = list()
|
425
|
-
# for i, fileName in enumerate(ls):
|
426
|
-
# cl = ContentLine(pathjoin(目录, fileName))
|
427
|
-
# lines = cl.regular_search(目标文本)
|
428
|
-
# if lines:
|
429
|
-
# 文件名.append(fileName)
|
430
|
-
# 出现次数.append(len(lines))
|
431
|
-
# 行号.append(str(lines))
|
432
|
-
#
|
433
|
-
# pf = pd.DataFrame({'文件名': 文件名, '出现次数': 出现次数, '行号': 行号}, columns=['文件名', '出现次数', '行号'])
|
434
|
-
# pf.sort_values(by=['出现次数'], ascending=False, inplace=True)
|
435
|
-
# PrintFullTable(pf, 最后一列左对齐=True)
|
436
|
-
# return pf
|
437
|
-
|
438
|
-
|
439
|
-
def 目录下查找文本(目录, 文件名筛选, 目标文本, *, 模式='表格'):
|
440
|
-
"""
|
441
|
-
表格模式:统计每个文件中出现的总次数
|
442
|
-
行文本模式:显示所有匹配的行文本
|
443
|
-
"""
|
444
|
-
ls = 文件搜索匹配(目录, 文件名筛选, 目标类型=('文件',))
|
445
|
-
ls = list(filter(lambda x: File(x).backup_time == '', ls)) # 去除备份文件
|
446
|
-
ls = natural_sort(ls)
|
447
|
-
if 模式 == '表格':
|
448
|
-
table = list()
|
449
|
-
for i, fileName in enumerate(ls):
|
450
|
-
cl = ContentLine(pathjoin(目录, fileName))
|
451
|
-
lines = cl.regular_search(目标文本)
|
452
|
-
if lines:
|
453
|
-
table.append((fileName, len(lines), refine_digits_set(lines)))
|
454
|
-
|
455
|
-
pf = pd.DataFrame.from_records(table, columns=('文件名', '出现次数', '行号'))
|
456
|
-
for i in range(len(pf)):
|
457
|
-
pf['出现次数'][i] = int(pf['出现次数'][i])
|
458
|
-
pf.sort_values(by=['出现次数'], ascending=False, inplace=True)
|
459
|
-
PrintFullTable(pf, 最后一列左对齐=True)
|
460
|
-
return pf
|
461
|
-
elif 模式 == '行文本':
|
462
|
-
for i, fileName in enumerate(ls):
|
463
|
-
cl = ContentLine(pathjoin(目录, fileName))
|
464
|
-
lines = cl.regular_search(目标文本)
|
465
|
-
if lines:
|
466
|
-
print()
|
467
|
-
# print(fileName) # 不输出根目录版本
|
468
|
-
print(pathjoin(目录, fileName)) # 输出根目录版本
|
469
|
-
print(cl.lines_content(lines))
|
470
|
-
else:
|
471
|
-
raise TypeError
|
472
|
-
|
473
|
-
|
474
|
-
def 目录下统计单词出现频数(目录, 文件名筛选, 目标文本=r'(\\?[a-zA-Z]+)(?![a-zA-Z])'):
|
475
|
-
"""默认会找所有单词,以及tex命令"""
|
476
|
-
ls = 文件搜索匹配(目录, 文件名筛选, 目标类型=('文件',))
|
477
|
-
s = list()
|
478
|
-
for fileName in [f for f in ls if not File(f).backup_time]: # 去除备份文件
|
479
|
-
c = File(pathjoin(目录, fileName)).read()
|
480
|
-
s.append(c)
|
481
|
-
s = '\n'.join(s)
|
482
|
-
|
483
|
-
# ls = re.findall(r'(?<=\\)([a-zA-Z]+)(?![a-zA-Z])', s) # 统计tex命令数量
|
484
|
-
# ls = re.findall(r'([a-zA-Z]+)(?![a-zA-Z])', s) # 统计单词次数
|
485
|
-
# ls = re.findall(r'(\\?[a-zA-Z]+)(?![a-zA-Z])', s) # tex和普通单词综合性搜索
|
486
|
-
ls = re.findall(目标文本, s)
|
487
|
-
d = OrderedDict(sorted(Counter(ls).items(), key=lambda t: -t[1]))
|
488
|
-
pf = pd.DataFrame({'关键字': list(d.keys()), '出现次数': list(d.values())}, columns=['关键字', '出现次数'])
|
489
|
-
PrintFullTable(pf)
|
490
|
-
return pf
|
491
|
-
|
492
|
-
|
493
|
-
def GetFullPathClass(s):
|
494
|
-
"""如果输入的是相对路径,会解析为绝对路径"""
|
495
|
-
# p = Path(s).resolve() # 奕本的电脑这句话运行不了
|
496
|
-
p = File(s)
|
497
|
-
if not s.startswith('\\') and not p.drive:
|
498
|
-
p = File.cwd() / p
|
499
|
-
return p
|
500
|
-
|
501
|
-
|
502
|
-
____other = """"""
|
503
|
-
|
504
|
-
mydecrypt = base64.b64decode
|
505
|
-
|
506
|
-
|
507
|
-
class LengthFormatter:
|
508
|
-
""" 长度换算类,可以在允许浮点小误差的场景使用
|
509
|
-
TODO 需要精确运算也是可以的,那就要用分数类来存储底层值了
|
510
|
-
|
511
|
-
# 默认标准长度是mm,所以初始化一个233数字,就等于用233mm初始化
|
512
|
-
>>> LengthFormatter(233).cm # 然后可以转化为cm,计算厘米单位下的长度值
|
513
|
-
23.3
|
514
|
-
>>> LengthFormatter('233pt') # 可以用带单位的字符串
|
515
|
-
81.78mm
|
516
|
-
>>> LengthFormatter('233.45 pt') # 支持小数、有空格等格式
|
517
|
-
81.94mm
|
518
|
-
|
519
|
-
应用举例:把长度超过12cm的hspace都限制在12cm以内
|
520
|
-
>> s = NestEnv(s).latexcmd1('hspace').bracket('{', inner=True).\
|
521
|
-
replace(lambda x: '12cm' if LengthFormatter(x).cm > 12 else x)
|
522
|
-
"""
|
523
|
-
|
524
|
-
# 所有其他单位长度与参照长度mm之间比例关系
|
525
|
-
ratio = {'pt': 0.351, # 点
|
526
|
-
'bp': 0.353, # 大点,≈1pt
|
527
|
-
'dd': 0.376, # 迪多,=1.07pt
|
528
|
-
'pc': 4.218, # 派卡,=12pt
|
529
|
-
'sp': 1 / 65536, # 定标点,65536sp=1pt
|
530
|
-
'cm': 10, # 厘米
|
531
|
-
'cc': 4.513, # 西塞罗
|
532
|
-
'in': 25.4, # 英寸,=72.27pt
|
533
|
-
'em': 18, # 1em≈当前字体中M的宽度,在正文12pt情况下,一般为18pt
|
534
|
-
'ex': 12, # 1ex≈当前字体中x的高度,暂按12pt处理
|
535
|
-
}
|
536
|
-
|
537
|
-
def __init__(self, v=0):
|
538
|
-
if isinstance(v, (int, float)):
|
539
|
-
self.__dict__['mm'] = v
|
540
|
-
elif isinstance(v, str):
|
541
|
-
m = re.match(r'(-?\d+(?:\.\d*)?)\s*(' + '|'.join(list(self.ratio.keys()) + ['mm']) + ')$', v)
|
542
|
-
if not m: raise ValueError(f'不存在的长度单位类型:{v}')
|
543
|
-
self.__dict__['mm'] = 0
|
544
|
-
self.__setitem__(m.group(2), float(m.group(1)))
|
545
|
-
else:
|
546
|
-
raise ValueError(f'不存在的长度单位类型:{v}')
|
547
|
-
|
548
|
-
def __repr__(self):
|
549
|
-
return '{:.2f}mm'.format(self.__dict__['mm'])
|
550
|
-
|
551
|
-
def __getattr__(self, key):
|
552
|
-
if key in self.ratio.keys():
|
553
|
-
return self.__dict__['mm'] / self.ratio[key]
|
554
|
-
else:
|
555
|
-
raise ValueError(f'不存在的长度单位类型:{key}')
|
556
|
-
|
557
|
-
def __setitem__(self, key, value):
|
558
|
-
if key == 'mm':
|
559
|
-
self.__dict__['mm'] = value
|
560
|
-
elif key in self.ratio.keys():
|
561
|
-
self.__dict__['mm'] = value * self.ratio[key]
|
562
|
-
else:
|
563
|
-
raise ValueError(f'不存在的长度单位类型:{key}')
|
564
|
-
|
565
|
-
|
566
|
-
____image = """
|
567
|
-
暂时还没空整理的一些图片功能
|
568
|
-
"""
|
569
|
-
|
570
|
-
|
571
|
-
def 缩放目录下所有png图片(folder, rate=120):
|
572
|
-
"""rate可以控制缩放比例,正常是100
|
573
|
-
再乘120是不想缩放太多,理论上乘100是正常比例
|
574
|
-
|
575
|
-
旧函数名:缩放目录下所有png图片
|
576
|
-
"""
|
577
|
-
fd = Dir(folder)
|
578
|
-
for f in fd.select_files('*.png'):
|
579
|
-
try:
|
580
|
-
im = Image.open(str(f))
|
581
|
-
if 'dpi' in im.info:
|
582
|
-
if im.info['dpi'][0] in (305, 610): # 这个是magick转换过来的特殊值,不能缩放
|
583
|
-
continue # 180920周四,610是为欧龙加的,欧龙双师需要设置-density 240
|
584
|
-
# print(files, im.info) # dpi: 600
|
585
|
-
# print(查看图片的Exif信息(im))
|
586
|
-
s = list(im.size)
|
587
|
-
s[0] = int(s[0] / im.info['dpi'][0] * rate)
|
588
|
-
s[1] = int(s[1] / im.info['dpi'][1] * rate)
|
589
|
-
im = im.resize(s, Image.ANTIALIAS)
|
590
|
-
im.save(str(f))
|
591
|
-
except:
|
592
|
-
print('无法处理图片:', f)
|
593
|
-
continue
|
594
|
-
|
595
|
-
|
596
|
-
def 缩放目录下所有png图片2(folder, scale=1.0):
|
597
|
-
"""rate可以控制缩放比例,正常是100
|
598
|
-
再乘120是不想缩放太多,理论上乘100是正常比例
|
599
|
-
|
600
|
-
旧函数名:缩放目录下所有png图片2
|
601
|
-
"""
|
602
|
-
fd = Dir(folder)
|
603
|
-
for f in fd.select_files('*.png'):
|
604
|
-
im = Image.open(str(f))
|
605
|
-
s = list(im.size)
|
606
|
-
s[0] = int(s[0] * scale)
|
607
|
-
s[1] = int(s[1] * scale)
|
608
|
-
im = im.resize(s, Image.ANTIALIAS)
|
609
|
-
im.save(str(f))
|
610
|
-
|
611
|
-
|
612
|
-
def 查看目录下png图片信息(folder):
|
613
|
-
"""
|
614
|
-
旧函数名:查看目录下png图片信息
|
615
|
-
"""
|
616
|
-
fd = Dir(folder)
|
617
|
-
ls = list()
|
618
|
-
for f in fd.select_files('*.png'):
|
619
|
-
im = Image.open(str(f))
|
620
|
-
d0, d1 = im.info['dpi'] if 'dpi' in im.info else ('', '')
|
621
|
-
# 处理eps格式
|
622
|
-
epsFile = f.with_suffix('.eps')
|
623
|
-
if epsFile:
|
624
|
-
epsSize, epsIm = epsFile.大小(), Image.open(epsFile.name)
|
625
|
-
boundingBox = epsIm.info['BoundingBox'].replace(' ', ',') if 'BoundingBox' in epsIm.info else ''
|
626
|
-
else:
|
627
|
-
epsSize, boundingBox = '', ''
|
628
|
-
# 处理pdf格式
|
629
|
-
pdfFile = File(f.name[:-4] + '-eps-converted-to.pdf')
|
630
|
-
pdfSize = pdfFile.size if pdfFile else ''
|
631
|
-
# 存储到列表
|
632
|
-
ls.append((f, im.size[0], im.size[1], d0, d1,
|
633
|
-
f.size, f.mtime.strftime(' %y%m%d-%H%M%S'),
|
634
|
-
epsSize, boundingBox, pdfSize))
|
635
|
-
df = pd.DataFrame.from_records(ls,
|
636
|
-
columns=('fileName', 'width', 'height', 'dpi_w', 'dpi_h', 'size', 'time', 'epsSize',
|
637
|
-
'boundingBox', 'pdfSize'))
|
638
|
-
with pd.option_context('display.max_colwidth', -1, 'display.max_columns', 20,
|
639
|
-
'display.width', 200): # 上下文控制格式
|
640
|
-
print(df)
|
641
|
-
return df
|
642
|
-
|
643
|
-
|
644
|
-
def make_color_transparent(image, color, thresh2=0):
|
645
|
-
""" 将指定颜色转为透明
|
646
|
-
https://stackoverflow.com/questions/765736/using-pil-to-make-all-white-pixels-transparent/765829
|
647
|
-
|
648
|
-
旧函数名:MakeColorTransparent
|
649
|
-
"""
|
650
|
-
from PIL import ImageMath
|
651
|
-
|
652
|
-
def distance2(a, b):
|
653
|
-
return (a[0] - b[0]) * (a[0] - b[0]) + (a[1] - b[1]) * (a[1] - b[1]) + (a[2] - b[2]) * (a[2] - b[2])
|
654
|
-
|
655
|
-
image = image.convert("RGBA")
|
656
|
-
red, green, blue, alpha = image.split()
|
657
|
-
image.putalpha(ImageMath.eval("""convert(((((t - d(c, (r, g, b))) >> 31) + 1) ^ 1) * a, 'L')""",
|
658
|
-
t=thresh2, d=distance2, c=color, r=red, g=green, b=blue, a=alpha))
|
659
|
-
return image
|
660
|
-
|
661
|
-
|
662
|
-
if __name__ == '__main__':
|
663
|
-
print('测试')
|