re-common 10.0.21__py3-none-any.whl → 10.0.22__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 (199) hide show
  1. re_common/baselibrary/__init__.py +4 -4
  2. re_common/baselibrary/baseabs/__init__.py +6 -6
  3. re_common/baselibrary/baseabs/baseabs.py +26 -26
  4. re_common/baselibrary/database/mbuilder.py +132 -132
  5. re_common/baselibrary/database/moudle.py +93 -93
  6. re_common/baselibrary/database/msqlite3.py +194 -194
  7. re_common/baselibrary/database/mysql.py +169 -169
  8. re_common/baselibrary/database/sql_factory.py +26 -26
  9. re_common/baselibrary/mthread/MThreadingRun.py +486 -486
  10. re_common/baselibrary/mthread/MThreadingRunEvent.py +349 -349
  11. re_common/baselibrary/mthread/__init__.py +2 -2
  12. re_common/baselibrary/mthread/mythreading.py +695 -695
  13. re_common/baselibrary/pakge_other/socks.py +404 -404
  14. re_common/baselibrary/readconfig/config_factory.py +18 -18
  15. re_common/baselibrary/readconfig/ini_config.py +317 -317
  16. re_common/baselibrary/readconfig/toml_config.py +49 -49
  17. re_common/baselibrary/temporary/envdata.py +36 -36
  18. re_common/baselibrary/tools/all_requests/aiohttp_request.py +118 -118
  19. re_common/baselibrary/tools/all_requests/httpx_requet.py +102 -102
  20. re_common/baselibrary/tools/all_requests/mrequest.py +412 -412
  21. re_common/baselibrary/tools/all_requests/requests_request.py +81 -81
  22. re_common/baselibrary/tools/batch_compre/bijiao_batch.py +31 -31
  23. re_common/baselibrary/tools/contrast_db3.py +123 -123
  24. re_common/baselibrary/tools/copy_file.py +39 -39
  25. re_common/baselibrary/tools/db3_2_sizedb3.py +102 -102
  26. re_common/baselibrary/tools/foreachgz.py +39 -39
  27. re_common/baselibrary/tools/get_attr.py +10 -10
  28. re_common/baselibrary/tools/image_to_pdf.py +61 -61
  29. re_common/baselibrary/tools/java_code_deal.py +139 -139
  30. re_common/baselibrary/tools/javacode.py +79 -79
  31. re_common/baselibrary/tools/mdb_db3.py +48 -48
  32. re_common/baselibrary/tools/merge_file.py +171 -171
  33. re_common/baselibrary/tools/merge_gz_file.py +165 -165
  34. re_common/baselibrary/tools/mhdfstools/down_hdfs_files.py +42 -42
  35. re_common/baselibrary/tools/mhdfstools/hdfst.py +42 -42
  36. re_common/baselibrary/tools/mhdfstools/up_hdfs_files.py +38 -38
  37. re_common/baselibrary/tools/mongo_tools.py +50 -50
  38. re_common/baselibrary/tools/move_file.py +170 -170
  39. re_common/baselibrary/tools/move_mongo/mongo_table_to_file.py +63 -63
  40. re_common/baselibrary/tools/move_mongo/move_mongo_table.py +354 -354
  41. re_common/baselibrary/tools/move_mongo/use_mttf.py +18 -18
  42. re_common/baselibrary/tools/move_mongo/use_mv.py +93 -93
  43. re_common/baselibrary/tools/mpandas/mpandasreadexcel.py +125 -125
  44. re_common/baselibrary/tools/mpandas/pandas_visualization.py +7 -7
  45. re_common/baselibrary/tools/myparsel.py +104 -104
  46. re_common/baselibrary/tools/rename_dir_file.py +37 -37
  47. re_common/baselibrary/tools/sequoiadb_utils.py +398 -398
  48. re_common/baselibrary/tools/split_line_to_many.py +25 -25
  49. re_common/baselibrary/tools/stringtodicts.py +33 -33
  50. re_common/baselibrary/tools/workwechant_bot.py +84 -84
  51. re_common/baselibrary/utils/baseaiohttp.py +296 -296
  52. re_common/baselibrary/utils/baseaiomysql.py +87 -87
  53. re_common/baselibrary/utils/baseallstep.py +191 -191
  54. re_common/baselibrary/utils/baseavro.py +19 -19
  55. re_common/baselibrary/utils/baseboto3.py +291 -291
  56. re_common/baselibrary/utils/basecsv.py +32 -32
  57. re_common/baselibrary/utils/basedict.py +133 -133
  58. re_common/baselibrary/utils/basedir.py +241 -241
  59. re_common/baselibrary/utils/baseencode.py +351 -351
  60. re_common/baselibrary/utils/baseencoding.py +28 -28
  61. re_common/baselibrary/utils/baseesdsl.py +86 -86
  62. re_common/baselibrary/utils/baseexcel.py +264 -264
  63. re_common/baselibrary/utils/baseexcept.py +109 -109
  64. re_common/baselibrary/utils/basefile.py +654 -654
  65. re_common/baselibrary/utils/baseftp.py +214 -214
  66. re_common/baselibrary/utils/basegzip.py +60 -60
  67. re_common/baselibrary/utils/basehdfs.py +135 -135
  68. re_common/baselibrary/utils/basehttpx.py +268 -268
  69. re_common/baselibrary/utils/baseip.py +87 -87
  70. re_common/baselibrary/utils/basejson.py +2 -2
  71. re_common/baselibrary/utils/baselist.py +32 -32
  72. re_common/baselibrary/utils/basemotor.py +190 -190
  73. re_common/baselibrary/utils/basemssql.py +98 -98
  74. re_common/baselibrary/utils/baseodbc.py +113 -113
  75. re_common/baselibrary/utils/basepandas.py +302 -302
  76. re_common/baselibrary/utils/basepeewee.py +11 -11
  77. re_common/baselibrary/utils/basepika.py +180 -180
  78. re_common/baselibrary/utils/basepydash.py +143 -143
  79. re_common/baselibrary/utils/basepymongo.py +230 -230
  80. re_common/baselibrary/utils/basequeue.py +22 -22
  81. re_common/baselibrary/utils/baserar.py +57 -57
  82. re_common/baselibrary/utils/baserequest.py +279 -279
  83. re_common/baselibrary/utils/baseset.py +8 -8
  84. re_common/baselibrary/utils/basesmb.py +403 -403
  85. re_common/baselibrary/utils/basestring.py +382 -382
  86. re_common/baselibrary/utils/basetime.py +320 -320
  87. re_common/baselibrary/utils/baseurl.py +121 -121
  88. re_common/baselibrary/utils/basezip.py +57 -57
  89. re_common/baselibrary/utils/core/__init__.py +7 -7
  90. re_common/baselibrary/utils/core/bottomutils.py +18 -18
  91. re_common/baselibrary/utils/core/mdeprecated.py +327 -327
  92. re_common/baselibrary/utils/core/mlamada.py +16 -16
  93. re_common/baselibrary/utils/core/msginfo.py +25 -25
  94. re_common/baselibrary/utils/core/requests_core.py +103 -103
  95. re_common/baselibrary/utils/fateadm.py +429 -429
  96. re_common/baselibrary/utils/importfun.py +123 -123
  97. re_common/baselibrary/utils/mfaker.py +57 -57
  98. re_common/baselibrary/utils/my_abc/__init__.py +3 -3
  99. re_common/baselibrary/utils/my_abc/better_abc.py +32 -32
  100. re_common/baselibrary/utils/mylogger.py +414 -414
  101. re_common/baselibrary/utils/myredisclient.py +861 -861
  102. re_common/baselibrary/utils/pipupgrade.py +21 -21
  103. re_common/baselibrary/utils/ringlist.py +85 -85
  104. re_common/baselibrary/utils/version_compare.py +36 -36
  105. re_common/baselibrary/utils/ydmhttp.py +126 -126
  106. re_common/facade/lazy_import.py +11 -11
  107. re_common/facade/loggerfacade.py +25 -25
  108. re_common/facade/mysqlfacade.py +467 -467
  109. re_common/facade/now.py +31 -31
  110. re_common/facade/sqlite3facade.py +257 -257
  111. re_common/facade/use/mq_use_facade.py +83 -83
  112. re_common/facade/use/proxy_use_facade.py +19 -19
  113. re_common/libtest/base_dict_test.py +19 -19
  114. re_common/libtest/baseavro_test.py +13 -13
  115. re_common/libtest/basefile_test.py +14 -14
  116. re_common/libtest/basemssql_test.py +77 -77
  117. re_common/libtest/baseodbc_test.py +7 -7
  118. re_common/libtest/basepandas_test.py +38 -38
  119. re_common/libtest/get_attr_test/get_attr_test_settings.py +14 -14
  120. re_common/libtest/get_attr_test/settings.py +54 -54
  121. re_common/libtest/idencode_test.py +53 -53
  122. re_common/libtest/iniconfig_test.py +35 -35
  123. re_common/libtest/ip_test.py +34 -34
  124. re_common/libtest/merge_file_test.py +20 -20
  125. re_common/libtest/mfaker_test.py +8 -8
  126. re_common/libtest/mm3_test.py +31 -31
  127. re_common/libtest/mylogger_test.py +88 -88
  128. re_common/libtest/myparsel_test.py +27 -27
  129. re_common/libtest/mysql_test.py +151 -151
  130. re_common/libtest/pymongo_test.py +21 -21
  131. re_common/libtest/split_test.py +11 -11
  132. re_common/libtest/sqlite3_merge_test.py +5 -5
  133. re_common/libtest/sqlite3_test.py +34 -34
  134. re_common/libtest/tomlconfig_test.py +30 -30
  135. re_common/libtest/use_tools_test/__init__.py +2 -2
  136. re_common/libtest/user/__init__.py +4 -4
  137. re_common/studio/__init__.py +4 -4
  138. re_common/studio/assignment_expressions.py +36 -36
  139. re_common/studio/mydash/test1.py +18 -18
  140. re_common/studio/pydashstudio/first.py +9 -9
  141. re_common/studio/streamlitstudio/first_app.py +65 -65
  142. re_common/studio/streamlitstudio/uber_pickups.py +23 -23
  143. re_common/studio/test.py +18 -18
  144. re_common/v2/baselibrary/decorators/utils.py +59 -59
  145. re_common/v2/baselibrary/s3object/baseboto3.py +230 -230
  146. re_common/v2/baselibrary/tools/WeChatRobot.py +79 -79
  147. re_common/v2/baselibrary/tools/ac_ahocorasick.py +75 -75
  148. re_common/v2/baselibrary/tools/dict_tools.py +37 -37
  149. re_common/v2/baselibrary/tools/dolphinscheduler.py +187 -187
  150. re_common/v2/baselibrary/tools/hdfs_data_processer.py +338 -338
  151. re_common/v2/baselibrary/tools/list_tools.py +65 -65
  152. re_common/v2/baselibrary/tools/search_hash_tools.py +54 -54
  153. re_common/v2/baselibrary/tools/text_matcher.py +326 -326
  154. re_common/v2/baselibrary/tools/unionfind_tools.py +60 -60
  155. re_common/v2/baselibrary/utils/BusinessStringUtil.py +196 -196
  156. re_common/v2/baselibrary/utils/author_smi.py +360 -360
  157. re_common/v2/baselibrary/utils/base_string_similarity.py +158 -158
  158. re_common/v2/baselibrary/utils/basedict.py +37 -37
  159. re_common/v2/baselibrary/utils/basehdfs.py +161 -161
  160. re_common/v2/baselibrary/utils/basepika.py +180 -180
  161. re_common/v2/baselibrary/utils/basetime.py +77 -77
  162. re_common/v2/baselibrary/utils/db.py +38 -38
  163. re_common/v2/baselibrary/utils/json_cls.py +16 -16
  164. re_common/v2/baselibrary/utils/mq.py +83 -83
  165. re_common/v2/baselibrary/utils/n_ary_expression_tree.py +243 -243
  166. re_common/v2/baselibrary/utils/string_bool.py +149 -149
  167. re_common/v2/baselibrary/utils/string_clear.py +204 -202
  168. re_common/v2/baselibrary/utils/string_smi.py +18 -18
  169. re_common/v2/baselibrary/utils/stringutils.py +213 -213
  170. re_common/vip/base_step_process.py +11 -11
  171. re_common/vip/baseencodeid.py +90 -90
  172. re_common/vip/changetaskname.py +28 -28
  173. re_common/vip/core_var.py +24 -24
  174. re_common/vip/mmh3Hash.py +89 -89
  175. re_common/vip/proxy/allproxys.py +127 -127
  176. re_common/vip/proxy/allproxys_thread.py +159 -159
  177. re_common/vip/proxy/cnki_proxy.py +153 -153
  178. re_common/vip/proxy/kuaidaili.py +87 -87
  179. re_common/vip/proxy/proxy_all.py +113 -113
  180. re_common/vip/proxy/update_kuaidaili_0.py +42 -42
  181. re_common/vip/proxy/wanfang_proxy.py +152 -152
  182. re_common/vip/proxy/wp_proxy_all.py +181 -181
  183. re_common/vip/read_rawid_to_txt.py +91 -91
  184. re_common/vip/title/__init__.py +5 -5
  185. re_common/vip/title/transform/TransformBookTitleToZt.py +125 -125
  186. re_common/vip/title/transform/TransformConferenceTitleToZt.py +139 -139
  187. re_common/vip/title/transform/TransformCstadTitleToZt.py +195 -195
  188. re_common/vip/title/transform/TransformJournalTitleToZt.py +203 -203
  189. re_common/vip/title/transform/TransformPatentTitleToZt.py +132 -132
  190. re_common/vip/title/transform/TransformRegulationTitleToZt.py +114 -114
  191. re_common/vip/title/transform/TransformStandardTitleToZt.py +135 -135
  192. re_common/vip/title/transform/TransformThesisTitleToZt.py +135 -135
  193. re_common/vip/title/transform/__init__.py +10 -10
  194. {re_common-10.0.21.dist-info → re_common-10.0.22.dist-info}/LICENSE +201 -201
  195. {re_common-10.0.21.dist-info → re_common-10.0.22.dist-info}/METADATA +16 -16
  196. re_common-10.0.22.dist-info/RECORD +227 -0
  197. {re_common-10.0.21.dist-info → re_common-10.0.22.dist-info}/WHEEL +1 -1
  198. re_common-10.0.21.dist-info/RECORD +0 -227
  199. {re_common-10.0.21.dist-info → re_common-10.0.22.dist-info}/top_level.txt +0 -0
@@ -1,654 +1,654 @@
1
- import _io
2
- import linecache
3
- import os
4
- import platform
5
- import random
6
- import re
7
- import shutil
8
- import time
9
- import traceback
10
-
11
- from .core.bottomutils import __os_sep__
12
-
13
-
14
- class BaseFile(_io.TextIOWrapper):
15
-
16
- def __init__(self, *args, **kwargs):
17
- super().__init__(*args, **kwargs)
18
-
19
- def seek(self, offset: int, whence: int = 0) -> int:
20
- """
21
- :param offset: 开始偏移量,也就是代表需要移动偏移的字节数。
22
- :param whence: 给offset参数一个定义,表示要从哪个位置开始偏移;0
23
- 代表从文件开头开始算起,1代表从当前位置开始算起,2代表从文件末尾算起
24
- :return: 返回的游标的位置
25
- """
26
- super().seek(offset, whence=whence)
27
- return super().tell()
28
-
29
- def truncate(self, size: int = None) -> int:
30
- """
31
- Python 文件 truncate() 方法用于截断文件并返回截断的字节长度。
32
-
33
- 指定长度的话,就从文件的开头开始截断指定长度,其余内容删除;
34
- 不指定长度的话,就从文件开头开始截断到当前位置,其余内容删除。
35
- 文件中的数据也会删除
36
- :param size:
37
- :return:
38
- """
39
- super().truncate(size)
40
- return super().tell()
41
-
42
- @classmethod
43
- def read_line(cls, file_path, encoding="utf-8"):
44
- """
45
- 按行读取文件
46
- 会一次性读取文件生成list 不适合大文件
47
- :param file_path:
48
- :param encoding:
49
- :return:
50
- """
51
- try:
52
- with open(file_path, "r", encoding=encoding) as f:
53
- for line in f.readlines():
54
- yield line.strip()
55
- except:
56
- print(traceback.format_exc())
57
-
58
- @classmethod
59
- def read_file_r_mode_yield(cls, filePath, encoding="utf-8"):
60
- """
61
- rb 模式比r 模式更快 但要通过编码格式转换才能使用 对于转移文件使用会更快
62
- :param filePath:
63
- :return:
64
- """
65
- with open(filePath, "r", encoding=encoding) as f:
66
- for fLine in f:
67
- yield fLine.strip()
68
-
69
- @classmethod
70
- def read_file_rb_mode_yield(cls, filePath):
71
- """
72
- rb 方式读取文件
73
- for line in f文件对象f视为一个迭代器,会自动的采用缓冲IO和内存管理,所以你不必担心大文件
74
- 参数为"rb"时的效率是"r"的6倍 建议rb
75
- 这里说下转str的方法 str(line, encoding='utf-8')
76
- :param filePath:
77
- :return: type bytes
78
- """
79
- with open(filePath, "rb") as f:
80
- for fLine in f:
81
- yield fLine
82
-
83
- @classmethod
84
- def read_file_rb_block(cls, filePath, BLOCK_SIZE=1024):
85
- """
86
- 按块读取文件
87
- :param filePath:
88
- :param BLOCK_SIZE:
89
- :return:
90
- """
91
- with open(filePath, 'rb') as f:
92
- while True:
93
- block = f.read(BLOCK_SIZE)
94
- if block:
95
- yield block
96
- else:
97
- return
98
-
99
- @classmethod
100
- def read_file_rb(cls, filePath: str) -> bytes:
101
- assert cls.is_file_exists(filePath), FileNotFoundError("文件不存在%s" % filePath)
102
- with open(filePath, mode='rb') as f:
103
- return f.read()
104
-
105
- @classmethod
106
- def bytes_takeout_utf8bom(cls, content: bytes):
107
- if content.startswith(b'\xef\xbb\xbf'): # 去掉 utf8 bom 头
108
- return content[3:]
109
- return content
110
-
111
- @classmethod
112
- def readfile_rb_takeout_utf8bom(cls, cfgFile: str) -> bytes:
113
- content = cls.read_file_rb(cfgFile)
114
- content = cls.bytes_takeout_utf8bom(content)
115
- return content
116
-
117
- @classmethod
118
- def get_new_path(cls, path, *paths):
119
- """
120
- 通过当前目录组合新的目录
121
- :return:
122
- """
123
- if platform.system() == "Windows":
124
- if (re.match("^[A-Za-z]:$", path)):
125
- path = path + "\\"
126
- path = os.path.join(path, *paths)
127
- return path
128
-
129
- @classmethod
130
- def check_create_dir(cls, path):
131
- """
132
- 检查和创建目录
133
- :param path:
134
- :return:
135
- """
136
- if os.path.exists(path):
137
- return True
138
- else:
139
- os.makedirs(path)
140
- return False
141
-
142
- @classmethod
143
- def single_write_file(cls, files, value, encoding="utf-8"):
144
- """
145
- 单次将内容写入文件
146
- 写入值到文件 每次写入会覆盖原来的文件 文件不存在会自动创建
147
- :param value:
148
- :param files:
149
- :return:
150
- """
151
- with open(files, 'w', encoding=encoding) as f:
152
- f.write(value)
153
-
154
- @classmethod
155
- def single_write_file_lines(cls, files, value_list, encoding="utf-8"):
156
- """
157
- 单次将内容写入文件
158
- 写入值到文件 每次写入会覆盖原来的文件 文件不存在会自动创建
159
- :param value:
160
- :param files:
161
- :return:
162
- """
163
- with open(files, 'w', encoding=encoding) as f:
164
- f.writelines(value_list)
165
-
166
- @classmethod
167
- def single_write_wb_file(cls, files, value):
168
- """
169
- 单次将内容写入文件
170
- 写入值到文件 每次写入会覆盖原来的文件 文件不存在会自动创建
171
- :param value:
172
- :param files:
173
- :return:
174
- """
175
- with open(files, 'wb') as f:
176
- f.write(value)
177
-
178
- @classmethod
179
- def single_write_ab_file(cls, files, value):
180
- """
181
- 单次将内容写入文件
182
- 写入值到文件 每次写入会追加原来的文件 文件不存在会自动创建
183
- :param value:
184
- :param files:
185
- :return:
186
- """
187
- with open(files, 'ab') as f:
188
- f.write(value)
189
-
190
- @classmethod
191
- def get_file_absolute(cls, file):
192
- """
193
- 请传入 __file__ 作为参数
194
- 获取一个文件的绝对路径
195
- :param __file__:
196
- :return:
197
- """
198
- return os.path.abspath(file)
199
-
200
- @classmethod
201
- def single_read_file(cls, filePath, encoding="utf-8", num=-1):
202
- """
203
- 读取文件中的所有值
204
- :param filePath:
205
- :param encoding:
206
- :param num 读取指定长度 可以用于解决无换行大文件问题 默认-1读取全部
207
- :return:
208
- """
209
- with open(filePath, encoding=encoding) as f:
210
- return f.read(num)
211
-
212
- @classmethod
213
- def single_read_rb_file(cls, filePath):
214
- """
215
- 读取文件中的所有值
216
- :param filePath:
217
- :param encoding:
218
- :return:
219
- """
220
- with open(filePath, "rb") as f:
221
- return f.read()
222
-
223
- @classmethod
224
- def get_file_row(cls, filePath, linenum):
225
- return linecache.getline(filePath, linenum)
226
-
227
- @classmethod
228
- def get_file_line_num(cls, filePath, encoding="utf-8"):
229
- """
230
- 获取文件行数
231
- :param filePath:
232
- :return:
233
- """
234
- count = 0
235
- for index, line in enumerate(open(filePath, 'r', encoding=encoding)):
236
- count += 1
237
- return count
238
-
239
- @classmethod
240
- def get_file_line_num2(cls, filename, block=1024 * 100):
241
- """
242
- 获取文件行数,本方法好处是可以指定block防止内存爆炸
243
- :param filename:
244
- :return:
245
- """
246
- count = 0
247
- with open(filename, 'r') as f:
248
- while True:
249
- buffer = f.read(block)
250
- if not buffer:
251
- break
252
- count += buffer.count('\n')
253
- return count
254
-
255
- @classmethod
256
- def single_add_file(cls, filePath, value, encoding="utf-8"):
257
- """
258
- 追加文件中的所有值
259
- 文件不存在创建
260
- :param filePath:
261
- :param encoding:
262
- :return:
263
- """
264
- with open(file=filePath, mode="a", encoding=encoding) as f:
265
- f.write(value)
266
-
267
- @classmethod
268
- def add_file_ab_mode(cls, filePath, value):
269
- """
270
- 追加文件中的所有值
271
- :param filePath:
272
- :param encoding:
273
- :return:
274
- """
275
- with open(file=filePath, mode="ab") as f:
276
- f.write(value)
277
-
278
- @classmethod
279
- def copy_file_to_file(cls, soufilePath, desfilepath):
280
- """
281
- shutil.copy 实现了copy文件
282
- :param soufilePath: 必须是一个文件
283
- :param desfilepath: 可以是一个文件 或者目录 如果是文件且文件名不同 可以达到重命名的效果
284
- 当我们想将文件copy成一个无后缀文件 且当前目录已经有一个同名文件夹时 会将文件copy到文件夹中
285
- 而不是一个无后缀文件中 这里要注意在大量文件和文件夹中使用时产生的歧义,且不能定义一个文件与文件夹
286
- 重名
287
- :return:
288
- """
289
- assert cls.is_file(soufilePath), FileNotFoundError(soufilePath + "这不是一个文件的路径")
290
- # 不存在进行创建目录
291
- if not os.path.exists(desfilepath):
292
- # 判断上级目录是否存在
293
- if not os.path.exists(os.path.split(desfilepath)[0]):
294
- os.makedirs(os.path.split(desfilepath)[0])
295
- # copy文件
296
- shutil.copyfile(soufilePath, desfilepath)
297
- # 判断copy后的文件是否存在
298
- if os.path.isfile(desfilepath):
299
- return True
300
- else:
301
- raise FileExistsError(desfilepath + "不存在,可能被copy到" + desfilepath + "目录中")
302
-
303
- @classmethod
304
- def get_image_filename_time(cls, filename):
305
- # 这是从文件名中获取时间 主要用于手机图片的命名格式有效
306
- li = filename.split(__os_sep__)
307
- if li[-1].find(__os_sep__) != -1:
308
- return ''
309
- if li[-1].find(os.extsep) != -1:
310
- li2 = li[-1].split(os.extsep)
311
- li3 = li2[-2].split("_")
312
- if len(li3) == 1:
313
- return li3[0]
314
- return li3[-2] + li3[-1]
315
- return ''
316
-
317
- @classmethod
318
- def get_time(cls, filename):
319
- """
320
- 获取文件时间,这个是获取文件的属性,不利于获取创造时间 因为每一次更改时间都会更新
321
- :param filename:
322
- :return:
323
- """
324
- ModifiedTime = time.localtime(os.stat(filename).st_mtime) # 文件访问时间
325
- y = time.strftime('%Y', ModifiedTime)
326
- m = time.strftime('%m', ModifiedTime)
327
- d = time.strftime('%d', ModifiedTime)
328
- H = time.strftime('%H', ModifiedTime)
329
- M = time.strftime('%M', ModifiedTime)
330
- import datetime
331
- d2 = datetime.datetime(int(y), int(m), int(d), int(H), int(M))
332
- return d2
333
-
334
- @classmethod
335
- def secure_filename(cls, filename):
336
- r"""Pass it a filename and it will return a secure version of it. This
337
- filename can then safely be stored on a regular file system and passed
338
- to :func:`os.path.join`. The filename returned is an ASCII only string
339
- for maximum portability.
340
-
341
- On windows systems the function also makes sure that the file is not
342
- named after one of the special device files.
343
-
344
- 经我改变 支持中文
345
-
346
- >>> secure_filename("My cool movie.mov")
347
- 'My_cool_movie.mov'
348
- >>> secure_filename("../../../etc/passwd")
349
- 'etc_passwd'
350
- >>> secure_filename(u'i contain cool \xfcml\xe4uts.txt')
351
- 'i_contain_cool_umlauts.txt'
352
-
353
- The function might return an empty filename. It's your responsibility
354
- to ensure that the filename is unique and that you generate random
355
- filename if the function returned an empty one.
356
-
357
- .. versionadded:: 0.5
358
-
359
- :param filename: the filename to secure
360
- """
361
- if isinstance(filename, text_type):
362
- # 此模块提供对Unicode字符数据库的访问,该字符数据库为所有Unicode字符定义字符属性。该数据库中的数据基于UnicodeData.txt
363
- # 可从ftp://ftp.unicode.org/公开获得的文件版本5.2.0 。
364
- from unicodedata import normalize
365
- # ignore参数会忽略无法编码的字符
366
- # normalize 为标准化底层编码 http://python3-cookbook.readthedocs.io/zh_CN/latest/c02/p09_normalize_unicode_text_to_regexp.html
367
- filename = normalize('NFKD', filename).encode('utf-8')
368
- if not PY2:
369
- filename = filename.decode('utf-8')
370
- for sep in os.path.sep, os.path.altsep:
371
- if sep:
372
- # 替换用后面的替换前面的
373
- filename = filename.replace(sep, ' ')
374
- filename = str(re.sub(':', '', '_'.join(
375
- filename.split()))).strip('._')
376
-
377
- # on nt a couple of special files are present in each folder. We
378
- # have to ensure that the target file is not such a filename. In
379
- # this case we prepend an underline
380
- if os.name == 'nt' and filename and \
381
- filename.split('.')[0].upper() in _windows_device_files:
382
- filename = '_' + filename
383
-
384
- return filename
385
-
386
- @classmethod
387
- def get_file_ext_name(cls, filename, double_ext=False):
388
- """
389
- 获取文件后缀
390
- os.extsep = "."
391
- :param filename: 传入文件名或者带路径的文件
392
- :param double_ext:
393
- :return:
394
- """
395
- li = filename.split(os.extsep) # 以.分割
396
- if len(li) <= 1: # 如果分割后只有一个 那么这没有文件后缀
397
- return ''
398
- else:
399
- if li[-1].find(__os_sep__) != -1: # 如果在最后一个中发现分割符 也没有后缀
400
- return ''
401
- if double_ext:
402
- if len(li) > 2: # 有可能后缀有多个组成 这里我们最多返回两个字符串以点链接形成的后缀
403
- if li[-2].find(__os_sep__) == -1:
404
- return '%s.%s' % (li[-2], li[-1])
405
- return li[-1]
406
-
407
- @classmethod
408
- def get_filename_not_extsep(cls, filename, end=None):
409
- """
410
- 获取文件名 去除后缀和路径
411
- :param filename:
412
- :return:
413
- """
414
- filename = cls.get_file_name(filename)
415
- if end is None:
416
- return os.extsep.join(filename.split(os.extsep)[:-1])
417
- else:
418
- if filename.endswith(end):
419
- return filename[:len(filename) - len(end) - 1]
420
- return os.extsep.join(filename.split(os.extsep)[:-1])
421
-
422
- @classmethod
423
- def get_file_name(cls, filename):
424
- """
425
- 得到文件名 去掉路径
426
- :param filename:
427
- :return:
428
- """
429
- # assert cls.is_file(filename), FileNotFoundError("传入的文件路径不正确")
430
- return os.path.split(filename)[-1]
431
- # 旧的方式 废弃
432
- # return filename.split(__os_sep__)[-1]
433
-
434
- @classmethod
435
- def new_title(cls, title):
436
- """
437
- 替换掉标题里的特殊符号 方便保存
438
- :param title:
439
- :return:
440
- """
441
- err_str = r"[\/\\\:\*\?\"\<\>\|]"
442
- new_title = re.sub(err_str, "_", title)
443
- return new_title
444
-
445
- @classmethod
446
- def is_file_exists(cls, pathdir):
447
- """
448
- 目录是否存在
449
- :param sPth:
450
- :return:
451
- """
452
- if os.path.exists(pathdir) and cls.is_file(pathdir):
453
- return True
454
- else:
455
- return False
456
-
457
- @classmethod
458
- def get_file_size(cls, filePath):
459
- assert os.path.isfile(filePath), FileNotFoundError("你给出的路径的文件不存在")
460
- return os.path.getsize(filePath)
461
-
462
- @classmethod
463
- def is_file(cls, spath):
464
- """
465
- 判断传入的路径是否是一个文件
466
- :param spath:
467
- :return:
468
- """
469
- if os.path.isfile(spath):
470
- return True
471
- return False
472
-
473
- @classmethod
474
- def remove_file(cls, filepath):
475
- """
476
- 删除文件
477
- :param filepath:
478
- :return:
479
- """
480
- os.remove(filepath)
481
-
482
- @classmethod
483
- def rename_file(cls, filepath, sourpath):
484
- os.rename(filepath, sourpath)
485
-
486
- @classmethod
487
- def get_create_time(cls, filepath):
488
- # 创建时间
489
- return os.path.getctime(filepath)
490
-
491
- @classmethod
492
- def get_update_time(cls, filepath):
493
- # 修改时间
494
- return os.path.getmtime(filepath)
495
-
496
- @classmethod
497
- def change_file(cls, filepath, sign='_', ending='big_json', size=2 * 1024 * 1024 * 1024):
498
- """
499
- :param filepath: 传入验证文件
500
- :param size: 文件大小限制(字节比较)(默认为2GB)
501
- :param sign: 文件切换后缀的标志 默认为"_"
502
- :param ending: 后缀名
503
- :return: 一个新的文件名
504
- 注意 首次传入的文件名不能包含sign标志位
505
- """
506
- from re_common.baselibrary.utils.basedir import BaseDir
507
- uppath = BaseDir.get_upper_dir(filepath, -1)
508
- if not cls.is_file_exists(filepath):
509
- return filepath
510
- else:
511
- if cls.get_file_size(filepath) >= size:
512
- import random
513
- # 无后缀文件名
514
- filename = cls.get_filename_not_extsep(filepath, end=ending)
515
- if sign in filename:
516
- path_list = filename.split(sign)
517
- if path_list[-1].isdigit() and len(path_list[-1]) == 5:
518
- # 原生文件名无数字干扰
519
- filename = filename[:-len(path_list[-1]) - 1]
520
- filename = filename + sign + repr(random.randrange(10000, 99999)) + "." + ending
521
-
522
- path = cls.get_new_path(uppath, filename)
523
- if cls.is_file_exists(path):
524
- return cls.change_file(path, sign=sign, ending=ending, size=size)
525
- return path
526
- else:
527
- return filepath
528
-
529
- @classmethod
530
- def get_new_filename(cls, filepath, sign='_', ending='big_json.gz'):
531
- # 如果传入的文件名在路径下不存在直接使用该文件名
532
- if not cls.is_file_exists(filepath):
533
- return filepath
534
- else:
535
- from re_common.baselibrary.utils.basedir import BaseDir
536
- # 获取路径
537
- uppath = BaseDir.get_upper_dir(filepath, -1)
538
- # 获取文件名但只会去除默认的一个后缀
539
- if sign in cls.get_filename_not_extsep(filepath):
540
- path_list = cls.get_file_name(filepath).split(sign)
541
- filename = path_list[0] + sign + repr(random.randrange(11111, 99999)) + "." + ending
542
- else:
543
- filename = cls.get_file_name(filepath)
544
- filename = filename.replace(("." + cls.get_file_ext_name(filepath)),
545
- (sign + repr(random.randrange(11111, 99999)) + "." + ending))
546
- path = cls.get_new_path(uppath, filename)
547
- if cls.is_file_exists(path):
548
- return cls.change_file(path, sign=sign, ending=ending)
549
- return path
550
-
551
- @classmethod
552
- def file_is_update_days(cls, filepath, days=3):
553
- """
554
- 判断文件是否在3天内 有更新
555
- True 3天内有更新 否则更新在3天以前
556
- :param days:
557
- :return:
558
- """
559
- if BaseFile.is_file_exists(filepath):
560
- ctime = os.path.getctime(filepath) # 创建时间
561
- mtime = os.path.getmtime(filepath) # 修改时间
562
- if ((time.time() - ctime) / 3600 / 24 < days) or ((time.time() - mtime) / 3600 / 24 < days):
563
- return True
564
- else:
565
- return False
566
- else:
567
- return False
568
-
569
- @classmethod
570
- def copy_file_to_dir(cls, soufilePath, desfilepath):
571
- """
572
- copy 文件到目录
573
- :param file:
574
- :param destdir: 目标目录
575
- :return:
576
- """
577
- assert cls.is_file(soufilePath), FileNotFoundError(soufilePath + "这不是一个文件的路径")
578
- # 不存在进行创建目录
579
- if not os.path.exists(desfilepath):
580
- # 判断上级目录是否存在
581
- if not os.path.exists(os.path.split(desfilepath)[0]):
582
- os.makedirs(os.path.split(desfilepath)[0])
583
- # copy文件
584
- shutil.copy(soufilePath, desfilepath)
585
-
586
- @classmethod
587
- def is_open(cls, file_name):
588
- """
589
- 文件是否被其他进程打开
590
- :param file_name:
591
- :return:
592
- """
593
- if "Windows" == platform.system():
594
- import win32file
595
- try:
596
- vHandle = win32file.CreateFile(file_name, win32file.GENERIC_READ, 0, None, win32file.OPEN_EXISTING,
597
- win32file.FILE_ATTRIBUTE_NORMAL, None)
598
- return int(vHandle) == win32file.INVALID_HANDLE_VALUE
599
- except:
600
- if "另一个程序正在使用此文件,进程无法访问。" in traceback.format_exc():
601
- return True
602
- else:
603
- return False
604
- finally:
605
- try:
606
- win32file.CloseHandle(vHandle)
607
- except:
608
- pass
609
- else:
610
- raise Exception("不是windows系统,请不要调用该函数判断文件是否被打开")
611
-
612
- @classmethod
613
- def read_end_line(cls, filepath, n, block=-1024):
614
- """
615
- 读取文件的最后几行
616
- 使用file.seek,从文件尾部指定字节读取(推荐)
617
- 不管最后一行是否有换行,都能识别到最后一行,如果最后一行为空行,会返回空行
618
- """
619
- with open(filepath, 'rb') as f:
620
- # 代表从最后开始
621
- f.seek(0, 2)
622
- # 就是可以打印出当前指针所在的位置
623
- filesize = f.tell()
624
- while True:
625
- if filesize >= abs(block):
626
- f.seek(block, 2)
627
- # 读取所有行,这样做就不适合读取特别大的行数了
628
- s = f.readlines()
629
- if len(s) > n:
630
- # 取指定行
631
- return [i.decode(encoding="utf-8").rstrip() for i in s[-n:]]
632
- else:
633
- # 如果不到block就加倍读取
634
- block *= 2
635
- else:
636
- block = -filesize
637
-
638
- @classmethod
639
- def read_end_line2(cls, filepath, n):
640
- """
641
- 获取最后几行 该方法相比上面方法更慢且更耗内存
642
- 注意: 使用该方法最后一行必须换行才能正确识别
643
- :param filepath:
644
- :param n:
645
- :return:
646
- """
647
- linecache.clearcache()
648
- line_count = cls.get_file_line_num2(filepath)
649
- line_count = line_count - n + 1
650
- for i in range(n):
651
- last_line = linecache.getline(filepath, line_count)
652
- line_count += 1
653
- last_line = last_line.rstrip()
654
- yield last_line
1
+ import _io
2
+ import linecache
3
+ import os
4
+ import platform
5
+ import random
6
+ import re
7
+ import shutil
8
+ import time
9
+ import traceback
10
+
11
+ from .core.bottomutils import __os_sep__
12
+
13
+
14
+ class BaseFile(_io.TextIOWrapper):
15
+
16
+ def __init__(self, *args, **kwargs):
17
+ super().__init__(*args, **kwargs)
18
+
19
+ def seek(self, offset: int, whence: int = 0) -> int:
20
+ """
21
+ :param offset: 开始偏移量,也就是代表需要移动偏移的字节数。
22
+ :param whence: 给offset参数一个定义,表示要从哪个位置开始偏移;0
23
+ 代表从文件开头开始算起,1代表从当前位置开始算起,2代表从文件末尾算起
24
+ :return: 返回的游标的位置
25
+ """
26
+ super().seek(offset, whence=whence)
27
+ return super().tell()
28
+
29
+ def truncate(self, size: int = None) -> int:
30
+ """
31
+ Python 文件 truncate() 方法用于截断文件并返回截断的字节长度。
32
+
33
+ 指定长度的话,就从文件的开头开始截断指定长度,其余内容删除;
34
+ 不指定长度的话,就从文件开头开始截断到当前位置,其余内容删除。
35
+ 文件中的数据也会删除
36
+ :param size:
37
+ :return:
38
+ """
39
+ super().truncate(size)
40
+ return super().tell()
41
+
42
+ @classmethod
43
+ def read_line(cls, file_path, encoding="utf-8"):
44
+ """
45
+ 按行读取文件
46
+ 会一次性读取文件生成list 不适合大文件
47
+ :param file_path:
48
+ :param encoding:
49
+ :return:
50
+ """
51
+ try:
52
+ with open(file_path, "r", encoding=encoding) as f:
53
+ for line in f.readlines():
54
+ yield line.strip()
55
+ except:
56
+ print(traceback.format_exc())
57
+
58
+ @classmethod
59
+ def read_file_r_mode_yield(cls, filePath, encoding="utf-8"):
60
+ """
61
+ rb 模式比r 模式更快 但要通过编码格式转换才能使用 对于转移文件使用会更快
62
+ :param filePath:
63
+ :return:
64
+ """
65
+ with open(filePath, "r", encoding=encoding) as f:
66
+ for fLine in f:
67
+ yield fLine.strip()
68
+
69
+ @classmethod
70
+ def read_file_rb_mode_yield(cls, filePath):
71
+ """
72
+ rb 方式读取文件
73
+ for line in f文件对象f视为一个迭代器,会自动的采用缓冲IO和内存管理,所以你不必担心大文件
74
+ 参数为"rb"时的效率是"r"的6倍 建议rb
75
+ 这里说下转str的方法 str(line, encoding='utf-8')
76
+ :param filePath:
77
+ :return: type bytes
78
+ """
79
+ with open(filePath, "rb") as f:
80
+ for fLine in f:
81
+ yield fLine
82
+
83
+ @classmethod
84
+ def read_file_rb_block(cls, filePath, BLOCK_SIZE=1024):
85
+ """
86
+ 按块读取文件
87
+ :param filePath:
88
+ :param BLOCK_SIZE:
89
+ :return:
90
+ """
91
+ with open(filePath, 'rb') as f:
92
+ while True:
93
+ block = f.read(BLOCK_SIZE)
94
+ if block:
95
+ yield block
96
+ else:
97
+ return
98
+
99
+ @classmethod
100
+ def read_file_rb(cls, filePath: str) -> bytes:
101
+ assert cls.is_file_exists(filePath), FileNotFoundError("文件不存在%s" % filePath)
102
+ with open(filePath, mode='rb') as f:
103
+ return f.read()
104
+
105
+ @classmethod
106
+ def bytes_takeout_utf8bom(cls, content: bytes):
107
+ if content.startswith(b'\xef\xbb\xbf'): # 去掉 utf8 bom 头
108
+ return content[3:]
109
+ return content
110
+
111
+ @classmethod
112
+ def readfile_rb_takeout_utf8bom(cls, cfgFile: str) -> bytes:
113
+ content = cls.read_file_rb(cfgFile)
114
+ content = cls.bytes_takeout_utf8bom(content)
115
+ return content
116
+
117
+ @classmethod
118
+ def get_new_path(cls, path, *paths):
119
+ """
120
+ 通过当前目录组合新的目录
121
+ :return:
122
+ """
123
+ if platform.system() == "Windows":
124
+ if (re.match("^[A-Za-z]:$", path)):
125
+ path = path + "\\"
126
+ path = os.path.join(path, *paths)
127
+ return path
128
+
129
+ @classmethod
130
+ def check_create_dir(cls, path):
131
+ """
132
+ 检查和创建目录
133
+ :param path:
134
+ :return:
135
+ """
136
+ if os.path.exists(path):
137
+ return True
138
+ else:
139
+ os.makedirs(path)
140
+ return False
141
+
142
+ @classmethod
143
+ def single_write_file(cls, files, value, encoding="utf-8"):
144
+ """
145
+ 单次将内容写入文件
146
+ 写入值到文件 每次写入会覆盖原来的文件 文件不存在会自动创建
147
+ :param value:
148
+ :param files:
149
+ :return:
150
+ """
151
+ with open(files, 'w', encoding=encoding) as f:
152
+ f.write(value)
153
+
154
+ @classmethod
155
+ def single_write_file_lines(cls, files, value_list, encoding="utf-8"):
156
+ """
157
+ 单次将内容写入文件
158
+ 写入值到文件 每次写入会覆盖原来的文件 文件不存在会自动创建
159
+ :param value:
160
+ :param files:
161
+ :return:
162
+ """
163
+ with open(files, 'w', encoding=encoding) as f:
164
+ f.writelines(value_list)
165
+
166
+ @classmethod
167
+ def single_write_wb_file(cls, files, value):
168
+ """
169
+ 单次将内容写入文件
170
+ 写入值到文件 每次写入会覆盖原来的文件 文件不存在会自动创建
171
+ :param value:
172
+ :param files:
173
+ :return:
174
+ """
175
+ with open(files, 'wb') as f:
176
+ f.write(value)
177
+
178
+ @classmethod
179
+ def single_write_ab_file(cls, files, value):
180
+ """
181
+ 单次将内容写入文件
182
+ 写入值到文件 每次写入会追加原来的文件 文件不存在会自动创建
183
+ :param value:
184
+ :param files:
185
+ :return:
186
+ """
187
+ with open(files, 'ab') as f:
188
+ f.write(value)
189
+
190
+ @classmethod
191
+ def get_file_absolute(cls, file):
192
+ """
193
+ 请传入 __file__ 作为参数
194
+ 获取一个文件的绝对路径
195
+ :param __file__:
196
+ :return:
197
+ """
198
+ return os.path.abspath(file)
199
+
200
+ @classmethod
201
+ def single_read_file(cls, filePath, encoding="utf-8", num=-1):
202
+ """
203
+ 读取文件中的所有值
204
+ :param filePath:
205
+ :param encoding:
206
+ :param num 读取指定长度 可以用于解决无换行大文件问题 默认-1读取全部
207
+ :return:
208
+ """
209
+ with open(filePath, encoding=encoding) as f:
210
+ return f.read(num)
211
+
212
+ @classmethod
213
+ def single_read_rb_file(cls, filePath):
214
+ """
215
+ 读取文件中的所有值
216
+ :param filePath:
217
+ :param encoding:
218
+ :return:
219
+ """
220
+ with open(filePath, "rb") as f:
221
+ return f.read()
222
+
223
+ @classmethod
224
+ def get_file_row(cls, filePath, linenum):
225
+ return linecache.getline(filePath, linenum)
226
+
227
+ @classmethod
228
+ def get_file_line_num(cls, filePath, encoding="utf-8"):
229
+ """
230
+ 获取文件行数
231
+ :param filePath:
232
+ :return:
233
+ """
234
+ count = 0
235
+ for index, line in enumerate(open(filePath, 'r', encoding=encoding)):
236
+ count += 1
237
+ return count
238
+
239
+ @classmethod
240
+ def get_file_line_num2(cls, filename, block=1024 * 100):
241
+ """
242
+ 获取文件行数,本方法好处是可以指定block防止内存爆炸
243
+ :param filename:
244
+ :return:
245
+ """
246
+ count = 0
247
+ with open(filename, 'r') as f:
248
+ while True:
249
+ buffer = f.read(block)
250
+ if not buffer:
251
+ break
252
+ count += buffer.count('\n')
253
+ return count
254
+
255
+ @classmethod
256
+ def single_add_file(cls, filePath, value, encoding="utf-8"):
257
+ """
258
+ 追加文件中的所有值
259
+ 文件不存在创建
260
+ :param filePath:
261
+ :param encoding:
262
+ :return:
263
+ """
264
+ with open(file=filePath, mode="a", encoding=encoding) as f:
265
+ f.write(value)
266
+
267
+ @classmethod
268
+ def add_file_ab_mode(cls, filePath, value):
269
+ """
270
+ 追加文件中的所有值
271
+ :param filePath:
272
+ :param encoding:
273
+ :return:
274
+ """
275
+ with open(file=filePath, mode="ab") as f:
276
+ f.write(value)
277
+
278
+ @classmethod
279
+ def copy_file_to_file(cls, soufilePath, desfilepath):
280
+ """
281
+ shutil.copy 实现了copy文件
282
+ :param soufilePath: 必须是一个文件
283
+ :param desfilepath: 可以是一个文件 或者目录 如果是文件且文件名不同 可以达到重命名的效果
284
+ 当我们想将文件copy成一个无后缀文件 且当前目录已经有一个同名文件夹时 会将文件copy到文件夹中
285
+ 而不是一个无后缀文件中 这里要注意在大量文件和文件夹中使用时产生的歧义,且不能定义一个文件与文件夹
286
+ 重名
287
+ :return:
288
+ """
289
+ assert cls.is_file(soufilePath), FileNotFoundError(soufilePath + "这不是一个文件的路径")
290
+ # 不存在进行创建目录
291
+ if not os.path.exists(desfilepath):
292
+ # 判断上级目录是否存在
293
+ if not os.path.exists(os.path.split(desfilepath)[0]):
294
+ os.makedirs(os.path.split(desfilepath)[0])
295
+ # copy文件
296
+ shutil.copyfile(soufilePath, desfilepath)
297
+ # 判断copy后的文件是否存在
298
+ if os.path.isfile(desfilepath):
299
+ return True
300
+ else:
301
+ raise FileExistsError(desfilepath + "不存在,可能被copy到" + desfilepath + "目录中")
302
+
303
+ @classmethod
304
+ def get_image_filename_time(cls, filename):
305
+ # 这是从文件名中获取时间 主要用于手机图片的命名格式有效
306
+ li = filename.split(__os_sep__)
307
+ if li[-1].find(__os_sep__) != -1:
308
+ return ''
309
+ if li[-1].find(os.extsep) != -1:
310
+ li2 = li[-1].split(os.extsep)
311
+ li3 = li2[-2].split("_")
312
+ if len(li3) == 1:
313
+ return li3[0]
314
+ return li3[-2] + li3[-1]
315
+ return ''
316
+
317
+ @classmethod
318
+ def get_time(cls, filename):
319
+ """
320
+ 获取文件时间,这个是获取文件的属性,不利于获取创造时间 因为每一次更改时间都会更新
321
+ :param filename:
322
+ :return:
323
+ """
324
+ ModifiedTime = time.localtime(os.stat(filename).st_mtime) # 文件访问时间
325
+ y = time.strftime('%Y', ModifiedTime)
326
+ m = time.strftime('%m', ModifiedTime)
327
+ d = time.strftime('%d', ModifiedTime)
328
+ H = time.strftime('%H', ModifiedTime)
329
+ M = time.strftime('%M', ModifiedTime)
330
+ import datetime
331
+ d2 = datetime.datetime(int(y), int(m), int(d), int(H), int(M))
332
+ return d2
333
+
334
+ @classmethod
335
+ def secure_filename(cls, filename):
336
+ r"""Pass it a filename and it will return a secure version of it. This
337
+ filename can then safely be stored on a regular file system and passed
338
+ to :func:`os.path.join`. The filename returned is an ASCII only string
339
+ for maximum portability.
340
+
341
+ On windows systems the function also makes sure that the file is not
342
+ named after one of the special device files.
343
+
344
+ 经我改变 支持中文
345
+
346
+ >>> secure_filename("My cool movie.mov")
347
+ 'My_cool_movie.mov'
348
+ >>> secure_filename("../../../etc/passwd")
349
+ 'etc_passwd'
350
+ >>> secure_filename(u'i contain cool \xfcml\xe4uts.txt')
351
+ 'i_contain_cool_umlauts.txt'
352
+
353
+ The function might return an empty filename. It's your responsibility
354
+ to ensure that the filename is unique and that you generate random
355
+ filename if the function returned an empty one.
356
+
357
+ .. versionadded:: 0.5
358
+
359
+ :param filename: the filename to secure
360
+ """
361
+ if isinstance(filename, text_type):
362
+ # 此模块提供对Unicode字符数据库的访问,该字符数据库为所有Unicode字符定义字符属性。该数据库中的数据基于UnicodeData.txt
363
+ # 可从ftp://ftp.unicode.org/公开获得的文件版本5.2.0 。
364
+ from unicodedata import normalize
365
+ # ignore参数会忽略无法编码的字符
366
+ # normalize 为标准化底层编码 http://python3-cookbook.readthedocs.io/zh_CN/latest/c02/p09_normalize_unicode_text_to_regexp.html
367
+ filename = normalize('NFKD', filename).encode('utf-8')
368
+ if not PY2:
369
+ filename = filename.decode('utf-8')
370
+ for sep in os.path.sep, os.path.altsep:
371
+ if sep:
372
+ # 替换用后面的替换前面的
373
+ filename = filename.replace(sep, ' ')
374
+ filename = str(re.sub(':', '', '_'.join(
375
+ filename.split()))).strip('._')
376
+
377
+ # on nt a couple of special files are present in each folder. We
378
+ # have to ensure that the target file is not such a filename. In
379
+ # this case we prepend an underline
380
+ if os.name == 'nt' and filename and \
381
+ filename.split('.')[0].upper() in _windows_device_files:
382
+ filename = '_' + filename
383
+
384
+ return filename
385
+
386
+ @classmethod
387
+ def get_file_ext_name(cls, filename, double_ext=False):
388
+ """
389
+ 获取文件后缀
390
+ os.extsep = "."
391
+ :param filename: 传入文件名或者带路径的文件
392
+ :param double_ext:
393
+ :return:
394
+ """
395
+ li = filename.split(os.extsep) # 以.分割
396
+ if len(li) <= 1: # 如果分割后只有一个 那么这没有文件后缀
397
+ return ''
398
+ else:
399
+ if li[-1].find(__os_sep__) != -1: # 如果在最后一个中发现分割符 也没有后缀
400
+ return ''
401
+ if double_ext:
402
+ if len(li) > 2: # 有可能后缀有多个组成 这里我们最多返回两个字符串以点链接形成的后缀
403
+ if li[-2].find(__os_sep__) == -1:
404
+ return '%s.%s' % (li[-2], li[-1])
405
+ return li[-1]
406
+
407
+ @classmethod
408
+ def get_filename_not_extsep(cls, filename, end=None):
409
+ """
410
+ 获取文件名 去除后缀和路径
411
+ :param filename:
412
+ :return:
413
+ """
414
+ filename = cls.get_file_name(filename)
415
+ if end is None:
416
+ return os.extsep.join(filename.split(os.extsep)[:-1])
417
+ else:
418
+ if filename.endswith(end):
419
+ return filename[:len(filename) - len(end) - 1]
420
+ return os.extsep.join(filename.split(os.extsep)[:-1])
421
+
422
+ @classmethod
423
+ def get_file_name(cls, filename):
424
+ """
425
+ 得到文件名 去掉路径
426
+ :param filename:
427
+ :return:
428
+ """
429
+ # assert cls.is_file(filename), FileNotFoundError("传入的文件路径不正确")
430
+ return os.path.split(filename)[-1]
431
+ # 旧的方式 废弃
432
+ # return filename.split(__os_sep__)[-1]
433
+
434
+ @classmethod
435
+ def new_title(cls, title):
436
+ """
437
+ 替换掉标题里的特殊符号 方便保存
438
+ :param title:
439
+ :return:
440
+ """
441
+ err_str = r"[\/\\\:\*\?\"\<\>\|]"
442
+ new_title = re.sub(err_str, "_", title)
443
+ return new_title
444
+
445
+ @classmethod
446
+ def is_file_exists(cls, pathdir):
447
+ """
448
+ 目录是否存在
449
+ :param sPth:
450
+ :return:
451
+ """
452
+ if os.path.exists(pathdir) and cls.is_file(pathdir):
453
+ return True
454
+ else:
455
+ return False
456
+
457
+ @classmethod
458
+ def get_file_size(cls, filePath):
459
+ assert os.path.isfile(filePath), FileNotFoundError("你给出的路径的文件不存在")
460
+ return os.path.getsize(filePath)
461
+
462
+ @classmethod
463
+ def is_file(cls, spath):
464
+ """
465
+ 判断传入的路径是否是一个文件
466
+ :param spath:
467
+ :return:
468
+ """
469
+ if os.path.isfile(spath):
470
+ return True
471
+ return False
472
+
473
+ @classmethod
474
+ def remove_file(cls, filepath):
475
+ """
476
+ 删除文件
477
+ :param filepath:
478
+ :return:
479
+ """
480
+ os.remove(filepath)
481
+
482
+ @classmethod
483
+ def rename_file(cls, filepath, sourpath):
484
+ os.rename(filepath, sourpath)
485
+
486
+ @classmethod
487
+ def get_create_time(cls, filepath):
488
+ # 创建时间
489
+ return os.path.getctime(filepath)
490
+
491
+ @classmethod
492
+ def get_update_time(cls, filepath):
493
+ # 修改时间
494
+ return os.path.getmtime(filepath)
495
+
496
+ @classmethod
497
+ def change_file(cls, filepath, sign='_', ending='big_json', size=2 * 1024 * 1024 * 1024):
498
+ """
499
+ :param filepath: 传入验证文件
500
+ :param size: 文件大小限制(字节比较)(默认为2GB)
501
+ :param sign: 文件切换后缀的标志 默认为"_"
502
+ :param ending: 后缀名
503
+ :return: 一个新的文件名
504
+ 注意 首次传入的文件名不能包含sign标志位
505
+ """
506
+ from re_common.baselibrary.utils.basedir import BaseDir
507
+ uppath = BaseDir.get_upper_dir(filepath, -1)
508
+ if not cls.is_file_exists(filepath):
509
+ return filepath
510
+ else:
511
+ if cls.get_file_size(filepath) >= size:
512
+ import random
513
+ # 无后缀文件名
514
+ filename = cls.get_filename_not_extsep(filepath, end=ending)
515
+ if sign in filename:
516
+ path_list = filename.split(sign)
517
+ if path_list[-1].isdigit() and len(path_list[-1]) == 5:
518
+ # 原生文件名无数字干扰
519
+ filename = filename[:-len(path_list[-1]) - 1]
520
+ filename = filename + sign + repr(random.randrange(10000, 99999)) + "." + ending
521
+
522
+ path = cls.get_new_path(uppath, filename)
523
+ if cls.is_file_exists(path):
524
+ return cls.change_file(path, sign=sign, ending=ending, size=size)
525
+ return path
526
+ else:
527
+ return filepath
528
+
529
+ @classmethod
530
+ def get_new_filename(cls, filepath, sign='_', ending='big_json.gz'):
531
+ # 如果传入的文件名在路径下不存在直接使用该文件名
532
+ if not cls.is_file_exists(filepath):
533
+ return filepath
534
+ else:
535
+ from re_common.baselibrary.utils.basedir import BaseDir
536
+ # 获取路径
537
+ uppath = BaseDir.get_upper_dir(filepath, -1)
538
+ # 获取文件名但只会去除默认的一个后缀
539
+ if sign in cls.get_filename_not_extsep(filepath):
540
+ path_list = cls.get_file_name(filepath).split(sign)
541
+ filename = path_list[0] + sign + repr(random.randrange(11111, 99999)) + "." + ending
542
+ else:
543
+ filename = cls.get_file_name(filepath)
544
+ filename = filename.replace(("." + cls.get_file_ext_name(filepath)),
545
+ (sign + repr(random.randrange(11111, 99999)) + "." + ending))
546
+ path = cls.get_new_path(uppath, filename)
547
+ if cls.is_file_exists(path):
548
+ return cls.change_file(path, sign=sign, ending=ending)
549
+ return path
550
+
551
+ @classmethod
552
+ def file_is_update_days(cls, filepath, days=3):
553
+ """
554
+ 判断文件是否在3天内 有更新
555
+ True 3天内有更新 否则更新在3天以前
556
+ :param days:
557
+ :return:
558
+ """
559
+ if BaseFile.is_file_exists(filepath):
560
+ ctime = os.path.getctime(filepath) # 创建时间
561
+ mtime = os.path.getmtime(filepath) # 修改时间
562
+ if ((time.time() - ctime) / 3600 / 24 < days) or ((time.time() - mtime) / 3600 / 24 < days):
563
+ return True
564
+ else:
565
+ return False
566
+ else:
567
+ return False
568
+
569
+ @classmethod
570
+ def copy_file_to_dir(cls, soufilePath, desfilepath):
571
+ """
572
+ copy 文件到目录
573
+ :param file:
574
+ :param destdir: 目标目录
575
+ :return:
576
+ """
577
+ assert cls.is_file(soufilePath), FileNotFoundError(soufilePath + "这不是一个文件的路径")
578
+ # 不存在进行创建目录
579
+ if not os.path.exists(desfilepath):
580
+ # 判断上级目录是否存在
581
+ if not os.path.exists(os.path.split(desfilepath)[0]):
582
+ os.makedirs(os.path.split(desfilepath)[0])
583
+ # copy文件
584
+ shutil.copy(soufilePath, desfilepath)
585
+
586
+ @classmethod
587
+ def is_open(cls, file_name):
588
+ """
589
+ 文件是否被其他进程打开
590
+ :param file_name:
591
+ :return:
592
+ """
593
+ if "Windows" == platform.system():
594
+ import win32file
595
+ try:
596
+ vHandle = win32file.CreateFile(file_name, win32file.GENERIC_READ, 0, None, win32file.OPEN_EXISTING,
597
+ win32file.FILE_ATTRIBUTE_NORMAL, None)
598
+ return int(vHandle) == win32file.INVALID_HANDLE_VALUE
599
+ except:
600
+ if "另一个程序正在使用此文件,进程无法访问。" in traceback.format_exc():
601
+ return True
602
+ else:
603
+ return False
604
+ finally:
605
+ try:
606
+ win32file.CloseHandle(vHandle)
607
+ except:
608
+ pass
609
+ else:
610
+ raise Exception("不是windows系统,请不要调用该函数判断文件是否被打开")
611
+
612
+ @classmethod
613
+ def read_end_line(cls, filepath, n, block=-1024):
614
+ """
615
+ 读取文件的最后几行
616
+ 使用file.seek,从文件尾部指定字节读取(推荐)
617
+ 不管最后一行是否有换行,都能识别到最后一行,如果最后一行为空行,会返回空行
618
+ """
619
+ with open(filepath, 'rb') as f:
620
+ # 代表从最后开始
621
+ f.seek(0, 2)
622
+ # 就是可以打印出当前指针所在的位置
623
+ filesize = f.tell()
624
+ while True:
625
+ if filesize >= abs(block):
626
+ f.seek(block, 2)
627
+ # 读取所有行,这样做就不适合读取特别大的行数了
628
+ s = f.readlines()
629
+ if len(s) > n:
630
+ # 取指定行
631
+ return [i.decode(encoding="utf-8").rstrip() for i in s[-n:]]
632
+ else:
633
+ # 如果不到block就加倍读取
634
+ block *= 2
635
+ else:
636
+ block = -filesize
637
+
638
+ @classmethod
639
+ def read_end_line2(cls, filepath, n):
640
+ """
641
+ 获取最后几行 该方法相比上面方法更慢且更耗内存
642
+ 注意: 使用该方法最后一行必须换行才能正确识别
643
+ :param filepath:
644
+ :param n:
645
+ :return:
646
+ """
647
+ linecache.clearcache()
648
+ line_count = cls.get_file_line_num2(filepath)
649
+ line_count = line_count - n + 1
650
+ for i in range(n):
651
+ last_line = linecache.getline(filepath, line_count)
652
+ line_count += 1
653
+ last_line = last_line.rstrip()
654
+ yield last_line