xtn-tools-pro 1.0.0.0.4__py3-none-any.whl → 1.0.0.0.6__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -8,11 +8,11 @@
8
8
  # --------------------------------------------------------------------------------------------------
9
9
  # 2024/4/17 xiatn V00.01.000 新建
10
10
  # --------------------------------------------------------------------------------------------------
11
- from xtn_tools_pro.tools_time import *
12
11
  from urllib import parse
12
+ from typing import List, Dict, Optional
13
+ from xtn_tools_pro.utils.time_utils import *
13
14
  from pymongo import MongoClient as _MongoClient
14
15
  from pymongo.database import Database as _Database
15
- from typing import List, Dict, Optional
16
16
  from pymongo.collection import Collection as _Collection
17
17
  from pymongo.errors import DuplicateKeyError, BulkWriteError
18
18
 
@@ -0,0 +1,397 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ # 说明:
5
+ # 程序说明xxxxxxxxxxxxxxxxxxx
6
+ # History:
7
+ # Date Author Version Modification
8
+ # --------------------------------------------------------------------------------------------------
9
+ # 2024/5/11 xiatn V00.01.000 新建
10
+ # --------------------------------------------------------------------------------------------------
11
+ import json
12
+ import pymysql
13
+ import datetime
14
+ from pymysql import err
15
+ from urllib import parse
16
+ from pymysql import cursors
17
+ from typing import List, Dict
18
+ from dbutils.pooled_db import PooledDB
19
+ from xtn_tools_pro.utils.log import Log
20
+ from xtn_tools_pro.utils.sql import get_insert_sql, get_insert_batch_sql, get_update_sql
21
+
22
+ log = Log(name="MysqlDB", color=True)
23
+
24
+
25
+ def auto_retry(func):
26
+ def wapper(*args, **kwargs):
27
+ for i in range(3):
28
+ try:
29
+ return func(*args, **kwargs)
30
+ except (err.InterfaceError, err.OperationalError) as e:
31
+ log.error(
32
+ """
33
+ error:%s
34
+ sql: %s
35
+ """
36
+ % (e, kwargs.get("sql") or args[1])
37
+ )
38
+
39
+ return wapper
40
+
41
+
42
+ class MysqlDBPro:
43
+ def __init__(self, ip, port, db, user_name, user_pass, **kwargs):
44
+ try:
45
+ self.connect_pool = PooledDB(
46
+ creator=pymysql, # 指定数据库连接的创建方法,这里使用的是pymysql作为创建方法
47
+ mincached=1, # 连接池中空闲连接的初始数量
48
+ maxcached=100, # 连接池中空闲连接的最大数量
49
+ maxconnections=100, # 连接池允许的最大连接数
50
+ blocking=True, # 当连接池达到最大连接数时,是否阻塞等待连接释放
51
+ ping=7, # 连接池中的连接在重新使用之前,是否需要进行ping操作来验证连接的有效性,这里的7是一个时间间隔,表示每隔7秒会对连接进行一次ping操作
52
+ host=ip, # 数据库主机的IP地址
53
+ port=port, # 数据库的端口号
54
+ user=user_name, # 连接数据库的用户名
55
+ passwd=user_pass, # 连接数据库的密码
56
+ db=db, # 连接的数据库名
57
+ charset="utf8mb4", # 连接数据库时使用的字符编码
58
+ cursorclass=cursors.SSCursor, # 指定使用的游标类,这里使用的是cursors.SSCursor,该游标类在多线程下大批量插入数据时可以减少内存的使用
59
+ ) # cursorclass 使用服务的游标,默认的在多线程下大批量插入数据会使内存递增
60
+
61
+ except Exception as e:
62
+ log.error(
63
+ """
64
+ 连接失败:
65
+ ip: {}
66
+ port: {}
67
+ db: {}
68
+ user_name: {}
69
+ user_pass: {}
70
+ exception: {}
71
+ """.format(
72
+ ip, port, db, user_name, user_pass, e
73
+ )
74
+ )
75
+ else:
76
+ log.debug("连接到mysql数据库 %s : %s" % (ip, db))
77
+
78
+ @classmethod
79
+ def from_url(cls, url, **kwargs):
80
+ """
81
+
82
+ Args:
83
+ url: mysql://username:password@ip:port/db?charset=utf8mb4
84
+ url: mysql://username:password@127.0.0.1:port/db?charset=utf8mb4
85
+ **kwargs:
86
+
87
+ Returns:
88
+
89
+ """
90
+ url_parsed = parse.urlparse(url)
91
+
92
+ db_type = url_parsed.scheme.strip()
93
+ if db_type != "mysql":
94
+ raise Exception(
95
+ "url error, expect mysql://username:ip:port/db?charset=utf8mb4, but get {}".format(
96
+ url
97
+ )
98
+ )
99
+
100
+ connect_params = {
101
+ "ip": url_parsed.hostname.strip(),
102
+ "port": url_parsed.port,
103
+ "user_name": url_parsed.username.strip(),
104
+ "user_pass": url_parsed.password.strip(),
105
+ "db": url_parsed.path.strip("/").strip(),
106
+ }
107
+
108
+ connect_params.update(kwargs)
109
+
110
+ return cls(**connect_params)
111
+
112
+ @staticmethod
113
+ def unescape_string(value):
114
+ if not isinstance(value, str):
115
+ return value
116
+ value = value.replace("\\0", "\0")
117
+ value = value.replace("\\\\", "\\")
118
+ value = value.replace("\\n", "\n")
119
+ value = value.replace("\\r", "\r")
120
+ value = value.replace("\\Z", "\032")
121
+ value = value.replace('\\"', '"')
122
+ value = value.replace("\\'", "'")
123
+ return value
124
+
125
+ def get_connection(self):
126
+ conn = self.connect_pool.connection(shareable=False)
127
+ # cursor = conn.cursor(cursors.SSCursor)
128
+ cursor = conn.cursor()
129
+
130
+ return conn, cursor
131
+
132
+ def close_connection(self, conn, cursor):
133
+ """
134
+ 关闭数据库连接和游标对象
135
+ :param conn:
136
+ :param cursor:
137
+ :return:
138
+ """
139
+ if conn:
140
+ conn.close()
141
+ if cursor:
142
+ cursor.close()
143
+
144
+ def execute(self, sql):
145
+ """
146
+ 执行sql
147
+ :param sql:
148
+ :return:
149
+ """
150
+ conn, cursor = None, None
151
+ try:
152
+ conn, cursor = self.get_connection()
153
+ cursor.execute(sql)
154
+ conn.commit()
155
+ except Exception as e:
156
+ log.error(
157
+ """
158
+ error:%s
159
+ sql: %s
160
+ """
161
+ % (e, sql)
162
+ )
163
+ return False
164
+ else:
165
+ return True
166
+ finally:
167
+ self.close_connection(conn, cursor)
168
+
169
+ def add(self, sql, exception_callfunc=None):
170
+ """
171
+ 单条 传入sql执行插入语句
172
+ :param sql: sql
173
+ :param exception_callfunc: 异常回调函数
174
+ :return: 添加行数
175
+ """
176
+ affect_count = None
177
+ conn, cursor = None, None
178
+
179
+ try:
180
+ conn, cursor = self.get_connection()
181
+ affect_count = cursor.execute(sql)
182
+ conn.commit()
183
+
184
+ except Exception as e:
185
+ log.error(
186
+ """
187
+ error:%s
188
+ sql: %s
189
+ """
190
+ % (e, sql)
191
+ )
192
+ if exception_callfunc:
193
+ exception_callfunc(e)
194
+ finally:
195
+ self.close_connection(conn, cursor)
196
+
197
+ return affect_count
198
+
199
+ def add_smart(self, table, data: Dict, **kwargs):
200
+ """
201
+ 单条 添加数据, 直接传递json格式的数据,不用拼sql
202
+ :param table: 表
203
+ :param data: 字典 {"xxx":"xxx"}
204
+ :param kwargs:
205
+ :return: 添加行数
206
+ """
207
+ sql = get_insert_sql(table, data, **kwargs)
208
+ return self.add(sql)
209
+
210
+ def add_batch(self, sql, datas: List[Dict]):
211
+ """
212
+ 批量 添加数据
213
+ 建议配合 get_insert_batch_sql() 生成sql
214
+ get_insert_batch_sql("user_copy1", [{"auth": 2, "id": "9", "email": "999"}]
215
+ :param sql:
216
+ insert ignore into `表` (字段1,字段2) values (%s, %s)
217
+ insert into `表` (`字段1`,`字段2`,`字段3`) values (%s, %s, %s)
218
+ 这里有多少个字段,values后面就要有多少个%s
219
+ :param datas: 列表 [{}, {}, {}]
220
+ :return:
221
+ """
222
+ affect_count = None
223
+ conn, cursor = None, None
224
+ try:
225
+ conn, cursor = self.get_connection()
226
+ affect_count = cursor.executemany(sql, datas)
227
+ conn.commit()
228
+
229
+ except Exception as e:
230
+ log.error(
231
+ """
232
+ error:%s
233
+ sql: %s
234
+ """
235
+ % (e, sql)
236
+ )
237
+ finally:
238
+ self.close_connection(conn, cursor)
239
+
240
+ return affect_count
241
+
242
+ def add_batch_smart(self, table, datas: List[Dict], **kwargs):
243
+ """
244
+ 批量 直接传递list格式的数据,不用拼sql
245
+ :param table: 表名
246
+ :param datas: 列表 [{}, {}, {}]
247
+ :param kwargs:
248
+ :return: 添加行数
249
+ """
250
+ sql, datas = get_insert_batch_sql(table, datas, **kwargs)
251
+ return self.add_batch(sql, datas)
252
+
253
+ def update(self, sql):
254
+ """
255
+ 更新
256
+ :param sql:
257
+ :return:
258
+ """
259
+ conn, cursor = None, None
260
+
261
+ try:
262
+ conn, cursor = self.get_connection()
263
+ cursor.execute(sql)
264
+ conn.commit()
265
+ except Exception as e:
266
+ log.error(
267
+ """
268
+ error:%s
269
+ sql: %s
270
+ """
271
+ % (e, sql)
272
+ )
273
+ return False
274
+ else:
275
+ return True
276
+ finally:
277
+ self.close_connection(conn, cursor)
278
+
279
+ def update_smart(self, table, data: Dict, condition):
280
+ """
281
+ 更新 无需拼sql
282
+ :param table: 表名
283
+ :param data: 数据 {"xxx":"xxx"}
284
+ :param condition: 更新条件 where后面的条件,如 condition='status=1'
285
+ :return:
286
+ """
287
+ sql = get_update_sql(table, data, condition)
288
+ return self.update(sql)
289
+
290
+ def delete(self, sql):
291
+ """
292
+ 删除
293
+ :param sql:
294
+ :return:
295
+ """
296
+ conn, cursor = None, None
297
+ try:
298
+ conn, cursor = self.get_connection()
299
+ cursor.execute(sql)
300
+ conn.commit()
301
+ except Exception as e:
302
+ log.error(
303
+ """
304
+ error:%s
305
+ sql: %s
306
+ """
307
+ % (e, sql)
308
+ )
309
+ return False
310
+ else:
311
+ return True
312
+ finally:
313
+ self.close_connection(conn, cursor)
314
+
315
+ @auto_retry
316
+ def find(self, sql, limit=0, to_json=False, conver_col=True):
317
+ """
318
+ 查询
319
+ 无数据: 返回None或[] 取决于limit
320
+ 有数据: 如果limit=1 则返回 一条数据(字段1, 字段2) 其余返回[(字段1, 字段2),(字段1, 字段2)]
321
+ :param sql:
322
+ :param limit:
323
+ :param to_json: 是否将查询结果转为json
324
+ :param conver_col: 是否处理查询结果,如date类型转字符串,json字符串转成json。仅当to_json=True时生效
325
+ :return:
326
+ """
327
+ conn, cursor = self.get_connection()
328
+
329
+ cursor.execute(sql)
330
+
331
+ if limit == 1:
332
+ result = cursor.fetchone() # 全部查出来,截取 不推荐使用
333
+ elif limit > 1:
334
+ result = cursor.fetchmany(limit) # 全部查出来,截取 不推荐使用
335
+ else:
336
+ result = cursor.fetchall()
337
+
338
+ if result is None:
339
+ return result
340
+
341
+ if to_json:
342
+ columns = [i[0] for i in cursor.description]
343
+
344
+ # 处理数据
345
+ def convert(col):
346
+ if isinstance(col, (datetime.date, datetime.time)):
347
+ return str(col)
348
+ elif isinstance(col, str) and (
349
+ col.startswith("{") or col.startswith("[")
350
+ ):
351
+ try:
352
+ # col = self.unescape_string(col)
353
+ return json.loads(col)
354
+ except:
355
+ return col
356
+ else:
357
+ # col = self.unescape_string(col)
358
+ return col
359
+
360
+ if limit == 1:
361
+ if conver_col:
362
+ result = [convert(col) for col in result]
363
+ result = dict(zip(columns, result))
364
+ else:
365
+ if conver_col:
366
+ result = [[convert(col) for col in row] for row in result]
367
+ result = [dict(zip(columns, r)) for r in result]
368
+
369
+ self.close_connection(conn, cursor)
370
+
371
+ return result
372
+
373
+
374
+ if __name__ == '__main__':
375
+ pass
376
+ # mysql_db = MysqlDBPro(ip="127.0.0.1", port=3306, db="xtn_home", user_name="root", user_pass="xtn-kk")
377
+ # sql = """insert into `user_copy1` (`id`, `email`, `auth`) values (8, '888', 2)"""
378
+ # print(mysql_db.add(sql))
379
+ # print(mysql_db.add_smart("user_copy1", {"id": "9", "email": "999"}))
380
+ # sql = "insert ignore into `user_copy1` (`id`,`email`) values (%s, %s)"
381
+ # sql, datas = get_insert_batch_sql("user_copy1", [{"auth": 2, "id": "9", "email": "999"}])
382
+ # print(mysql_db.add_batch(sql, datas))
383
+
384
+ # print(mysql_db.add_batch_smart("user_copy1", [{"auth": 2, "id": "9", "email": "999"},
385
+ # {"auth": 2, "id": "10", "email": "10"},
386
+ # {"id": "11", "auth": 1, "email": "11"},
387
+ # {"auth": 2, "id": "12", "email": "12"}]))
388
+
389
+ # 更新案例
390
+ # sql = "UPDATE user_copy1 SET status = '2', auth = 1 WHERE id = 2;"
391
+ # print(mysql_db.update(sql))
392
+
393
+ # 更新 无需拼接sql案例
394
+ # print(mysql_db.update_smart("user_copy1", {"email": "123", "status": 4}, "id=22"))
395
+
396
+ # 查询案例
397
+ # print(mysql_db.find("select * from user_copy1 where id=11",1,True))
@@ -10,7 +10,7 @@
10
10
  # --------------------------------------------------------------------------------------------------
11
11
  import requests, time, random
12
12
  from xtn_tools_pro.db.RedisDB import RedisDBPro
13
- from xtn_tools_pro.tools_time import get_time_now_timestamp, get_time_now_day59_timestamp
13
+ from xtn_tools_pro.utils.time_utils import get_time_now_timestamp, get_time_now_day59_timestamp
14
14
 
15
15
  import warnings
16
16
  from urllib3.exceptions import InsecureRequestWarning
xtn_tools_pro/tools.py CHANGED
@@ -8,125 +8,15 @@
8
8
  # --------------------------------------------------------------------------------------------------
9
9
  # 2024/4/17 xiatn V00.01.000 新建
10
10
  # --------------------------------------------------------------------------------------------------
11
- import hashlib, json, math
12
- from urllib.parse import urlencode
13
11
 
14
12
 
15
- def get_md5_32(s, is_upper=False):
13
+ def split_image(img):
16
14
  """
17
- 获取文本的md5值 32位
18
- :param s: 文本
19
- :param is_upper: 是否转大写 默认False
15
+ 切割图片
16
+ :param img:
20
17
  :return:
21
18
  """
22
- # s.encode()#变成bytes类型才能加密
23
- m = hashlib.md5(s.encode()) # 长度是32
24
- if is_upper:
25
- return m.hexdigest().upper()
26
- return m.hexdigest()
27
-
28
-
29
- def get_md5_16(s, is_upper=False):
30
- """
31
- 获取文本的md5值 16位
32
- :param s: 文本
33
- :param is_upper: 是否转大写 默认False
34
- :return:
35
- """
36
- result = get_md5_32(s, is_upper)
37
- return result[8:24]
38
-
39
-
40
- def get_binary_content_md5_32(content, is_upper=False):
41
- """
42
- 二进制内容md5 例如图片
43
- :param content: 二进制内容
44
- :param is_upper: 是否转大写 默认False
45
- :return:
46
- """
47
- md5_hash = hashlib.md5(content)
48
- md5_hexdigest = md5_hash.hexdigest()
49
- if is_upper:
50
- return md5_hexdigest.upper()
51
- return md5_hexdigest
52
-
53
-
54
- def get_binary_content_md5_16(content, is_upper=False):
55
- """
56
- 二进制内容md5 例如图片
57
- :param content: 二进制内容
58
- :param is_upper: 是否转大写 默认False
59
- :return:
60
- """
61
- result = get_binary_content_md5_32(content, is_upper)
62
- return result[8:24]
63
-
64
-
65
- def get_file_md5_32(file_path, is_upper=False):
66
- """
67
- 获取文件md5值
68
- :param file_path: 文件路径
69
- :param is_upper: 是否转大写 默认False
70
- :return:
71
- """
72
- with open(file_path, 'rb') as file:
73
- data = file.read()
74
- md5_hash = hashlib.md5(data).hexdigest()
75
- if is_upper:
76
- return md5_hash.upper()
77
- return md5_hash
78
-
79
-
80
- def get_file_md5_16(file_path, is_upper=False):
81
- """
82
- 获取文件md5值
83
- :param file_path: 文件路径
84
- :param is_upper: 是否转大写 默认False
85
- :return:
86
- """
87
- result = get_file_md5_32(file_path, is_upper)
88
- return result[8:24]
89
-
90
-
91
- def get_str_to_json(str_json):
92
- """
93
- 字符串类型的json格式 转 json
94
- :param str_json: 字符串json
95
- :return:
96
- """
97
- try:
98
- new_str_json = str_json.replace("'", '"'). \
99
- replace("None", "null").replace("True", "true"). \
100
- replace("False", "false")
101
- return json.loads(new_str_json)
102
- except Exception as e:
103
- return {}
104
-
105
-
106
- def get_build_url_with_params(url, params):
107
- """
108
- 传入url和params拼接完整的url ->效果 https://wwww.xxxx.com/?xxx1=1&xxx2=2
109
- :param url:
110
- :param params:
111
- :return:
112
- """
113
- encoded_params = urlencode(params)
114
- full_url = url + "?" + encoded_params
115
- return full_url
116
-
117
-
118
- def get_calculate_total_page(total, limit):
119
- """
120
- 根据total和limit计算出一共有多少页
121
- :param total:
122
- :param limit:
123
- :return:
124
- """
125
- if limit <= 0:
126
- return 0
127
- # 根据总条数和limit计算总页数
128
- total_pages = math.ceil(total / limit)
129
- return total_pages
19
+ pass
130
20
 
131
21
 
132
22
  if __name__ == '__main__':
@@ -38,4 +38,4 @@ def get_file_check_filename(file_name):
38
38
  if __name__ == '__main__':
39
39
  pass
40
40
  print(get_file_extension('file/2024-04-19/BOSCH GEX 125-1A/125-1AE砂磨机操作说明书:[1]_jingyan.txt'))
41
- print(get_check_filename('file/2024-04-19/BOSCH GEX 125-1A/125-1AE砂磨机操作说明书:[1]_jingyan.txt'))
41
+ print(get_file_check_filename('file/2024-04-19/BOSCH GEX 125-1A/125-1AE砂磨机操作说明书:[1]_jingyan.txt'))
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ # 说明:
5
+ # 程序说明xxxxxxxxxxxxxxxxxxx
6
+ # History:
7
+ # Date Author Version Modification
8
+ # --------------------------------------------------------------------------------------------------
9
+ # 2024/5/12 xiatn V00.01.000 新建
10
+ # --------------------------------------------------------------------------------------------------
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ # 说明:
5
+ # 加解密、编解码
6
+ # History:
7
+ # Date Author Version Modification
8
+ # --------------------------------------------------------------------------------------------------
9
+ # 2024/5/13 xiatn V00.01.000 新建
10
+ # --------------------------------------------------------------------------------------------------
11
+ import hashlib
12
+
13
+
14
+ def get_md5_32(s, is_upper=False):
15
+ """
16
+ 获取文本的md5值 32位
17
+ :param s: 文本
18
+ :param is_upper: 是否转大写 默认False
19
+ :return:
20
+ """
21
+ # s.encode()#变成bytes类型才能加密
22
+ m = hashlib.md5(s.encode()) # 长度是32
23
+ if is_upper:
24
+ return m.hexdigest().upper()
25
+ return m.hexdigest()
26
+
27
+
28
+ def get_md5_16(s, is_upper=False):
29
+ """
30
+ 获取文本的md5值 16位
31
+ :param s: 文本
32
+ :param is_upper: 是否转大写 默认False
33
+ :return:
34
+ """
35
+ result = get_md5_32(s, is_upper)
36
+ return result[8:24]
37
+
38
+
39
+ def get_binary_content_md5_32(content, is_upper=False):
40
+ """
41
+ 二进制内容md5 例如图片
42
+ :param content: 二进制内容
43
+ :param is_upper: 是否转大写 默认False
44
+ :return:
45
+ """
46
+ md5_hash = hashlib.md5(content)
47
+ md5_hexdigest = md5_hash.hexdigest()
48
+ if is_upper:
49
+ return md5_hexdigest.upper()
50
+ return md5_hexdigest
51
+
52
+
53
+ def get_binary_content_md5_16(content, is_upper=False):
54
+ """
55
+ 二进制内容md5 例如图片
56
+ :param content: 二进制内容
57
+ :param is_upper: 是否转大写 默认False
58
+ :return:
59
+ """
60
+ result = get_binary_content_md5_32(content, is_upper)
61
+ return result[8:24]
62
+
63
+
64
+ def get_file_md5_32(file_path, is_upper=False):
65
+ """
66
+ 获取文件md5值
67
+ :param file_path: 文件路径
68
+ :param is_upper: 是否转大写 默认False
69
+ :return:
70
+ """
71
+ with open(file_path, 'rb') as file:
72
+ data = file.read()
73
+ md5_hash = hashlib.md5(data).hexdigest()
74
+ if is_upper:
75
+ return md5_hash.upper()
76
+ return md5_hash
77
+
78
+
79
+ def get_file_md5_16(file_path, is_upper=False):
80
+ """
81
+ 获取文件md5值
82
+ :param file_path: 文件路径
83
+ :param is_upper: 是否转大写 默认False
84
+ :return:
85
+ """
86
+ result = get_file_md5_32(file_path, is_upper)
87
+ return result[8:24]
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ # 说明:
5
+ # 文件
6
+ # History:
7
+ # Date Author Version Modification
8
+ # --------------------------------------------------------------------------------------------------
9
+ # 2024/5/13 xiatn V00.01.000 新建
10
+ # --------------------------------------------------------------------------------------------------
11
+ import os
12
+ import re
13
+
14
+
15
+ def get_file_extension(file_name):
16
+ """
17
+ 根据文件名获取文件扩展名/后缀名
18
+ :param file_name: 文件名称
19
+ :return:
20
+ """
21
+ _, file_extension = os.path.splitext(file_name)
22
+ return file_extension
23
+
24
+
25
+ def get_file_check_filename(file_name):
26
+ """
27
+ 传入文件名返回一个合法的文件名 会替换掉一些特殊符号 常用于爬虫写文件时文件名中带有特殊符号的情况...
28
+ :param filename: 文件名
29
+ :return:
30
+ """
31
+ file_extension = get_file_extension(file_name)
32
+ # 删除非法字符
33
+ sanitized_filename = re.sub(r'[\/:*?"<>|]', '', file_name)
34
+ max_length = 255 # 操作系统限制文件名的最大长度为255个
35
+ sanitized_filename = sanitized_filename[:max_length]
36
+ return sanitized_filename
37
+
38
+
39
+ if __name__ == '__main__':
40
+ pass
41
+ print(get_file_extension('file/2024-04-19/BOSCH GEX 125-1A/125-1AE砂磨机操作说明书:[1]_jingyan.txt'))
42
+ print(get_file_check_filename('file/2024-04-19/BOSCH GEX 125-1A/125-1AE砂磨机操作说明书:[1]_jingyan.txt'))