re-common 10.0.39__py3-none-any.whl → 10.0.41__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.
- re_common/baselibrary/__init__.py +4 -4
- re_common/baselibrary/baseabs/__init__.py +6 -6
- re_common/baselibrary/baseabs/baseabs.py +26 -26
- re_common/baselibrary/database/mbuilder.py +132 -132
- re_common/baselibrary/database/moudle.py +93 -93
- re_common/baselibrary/database/msqlite3.py +194 -194
- re_common/baselibrary/database/mysql.py +169 -169
- re_common/baselibrary/database/sql_factory.py +26 -26
- re_common/baselibrary/mthread/MThreadingRun.py +486 -486
- re_common/baselibrary/mthread/MThreadingRunEvent.py +349 -349
- re_common/baselibrary/mthread/__init__.py +2 -2
- re_common/baselibrary/mthread/mythreading.py +695 -695
- re_common/baselibrary/pakge_other/socks.py +404 -404
- re_common/baselibrary/readconfig/config_factory.py +18 -18
- re_common/baselibrary/readconfig/ini_config.py +317 -317
- re_common/baselibrary/readconfig/toml_config.py +49 -49
- re_common/baselibrary/temporary/envdata.py +36 -36
- re_common/baselibrary/tools/all_requests/aiohttp_request.py +118 -118
- re_common/baselibrary/tools/all_requests/httpx_requet.py +102 -102
- re_common/baselibrary/tools/all_requests/mrequest.py +412 -412
- re_common/baselibrary/tools/all_requests/requests_request.py +81 -81
- re_common/baselibrary/tools/batch_compre/bijiao_batch.py +31 -31
- re_common/baselibrary/tools/contrast_db3.py +123 -123
- re_common/baselibrary/tools/copy_file.py +39 -39
- re_common/baselibrary/tools/db3_2_sizedb3.py +102 -102
- re_common/baselibrary/tools/foreachgz.py +39 -39
- re_common/baselibrary/tools/get_attr.py +10 -10
- re_common/baselibrary/tools/image_to_pdf.py +61 -61
- re_common/baselibrary/tools/java_code_deal.py +139 -139
- re_common/baselibrary/tools/javacode.py +79 -79
- re_common/baselibrary/tools/mdb_db3.py +48 -48
- re_common/baselibrary/tools/merge_file.py +171 -171
- re_common/baselibrary/tools/merge_gz_file.py +165 -165
- re_common/baselibrary/tools/mhdfstools/down_hdfs_files.py +42 -42
- re_common/baselibrary/tools/mhdfstools/hdfst.py +42 -42
- re_common/baselibrary/tools/mhdfstools/up_hdfs_files.py +38 -38
- re_common/baselibrary/tools/mongo_tools.py +50 -50
- re_common/baselibrary/tools/move_file.py +170 -170
- re_common/baselibrary/tools/move_mongo/mongo_table_to_file.py +63 -63
- re_common/baselibrary/tools/move_mongo/move_mongo_table.py +354 -354
- re_common/baselibrary/tools/move_mongo/use_mttf.py +18 -18
- re_common/baselibrary/tools/move_mongo/use_mv.py +93 -93
- re_common/baselibrary/tools/mpandas/mpandasreadexcel.py +125 -125
- re_common/baselibrary/tools/mpandas/pandas_visualization.py +7 -7
- re_common/baselibrary/tools/myparsel.py +104 -104
- re_common/baselibrary/tools/rename_dir_file.py +37 -37
- re_common/baselibrary/tools/sequoiadb_utils.py +398 -398
- re_common/baselibrary/tools/split_line_to_many.py +25 -25
- re_common/baselibrary/tools/stringtodicts.py +33 -33
- re_common/baselibrary/tools/workwechant_bot.py +84 -84
- re_common/baselibrary/utils/baseaiohttp.py +296 -296
- re_common/baselibrary/utils/baseaiomysql.py +87 -87
- re_common/baselibrary/utils/baseallstep.py +191 -191
- re_common/baselibrary/utils/baseavro.py +19 -19
- re_common/baselibrary/utils/baseboto3.py +291 -291
- re_common/baselibrary/utils/basecsv.py +32 -32
- re_common/baselibrary/utils/basedict.py +133 -133
- re_common/baselibrary/utils/basedir.py +241 -241
- re_common/baselibrary/utils/baseencode.py +351 -351
- re_common/baselibrary/utils/baseencoding.py +28 -28
- re_common/baselibrary/utils/baseesdsl.py +86 -86
- re_common/baselibrary/utils/baseexcel.py +264 -264
- re_common/baselibrary/utils/baseexcept.py +109 -109
- re_common/baselibrary/utils/basefile.py +654 -654
- re_common/baselibrary/utils/baseftp.py +214 -214
- re_common/baselibrary/utils/basegzip.py +60 -60
- re_common/baselibrary/utils/basehdfs.py +135 -135
- re_common/baselibrary/utils/basehttpx.py +268 -268
- re_common/baselibrary/utils/baseip.py +87 -87
- re_common/baselibrary/utils/basejson.py +2 -2
- re_common/baselibrary/utils/baselist.py +32 -32
- re_common/baselibrary/utils/basemotor.py +190 -190
- re_common/baselibrary/utils/basemssql.py +98 -98
- re_common/baselibrary/utils/baseodbc.py +113 -113
- re_common/baselibrary/utils/basepandas.py +302 -302
- re_common/baselibrary/utils/basepeewee.py +11 -11
- re_common/baselibrary/utils/basepika.py +180 -180
- re_common/baselibrary/utils/basepydash.py +143 -143
- re_common/baselibrary/utils/basepymongo.py +230 -230
- re_common/baselibrary/utils/basequeue.py +22 -22
- re_common/baselibrary/utils/baserar.py +57 -57
- re_common/baselibrary/utils/baserequest.py +279 -279
- re_common/baselibrary/utils/baseset.py +8 -8
- re_common/baselibrary/utils/basesmb.py +403 -403
- re_common/baselibrary/utils/basestring.py +382 -382
- re_common/baselibrary/utils/basetime.py +320 -320
- re_common/baselibrary/utils/baseurl.py +121 -121
- re_common/baselibrary/utils/basezip.py +57 -57
- re_common/baselibrary/utils/core/__init__.py +7 -7
- re_common/baselibrary/utils/core/bottomutils.py +18 -18
- re_common/baselibrary/utils/core/mdeprecated.py +327 -327
- re_common/baselibrary/utils/core/mlamada.py +16 -16
- re_common/baselibrary/utils/core/msginfo.py +25 -25
- re_common/baselibrary/utils/core/requests_core.py +103 -103
- re_common/baselibrary/utils/fateadm.py +429 -429
- re_common/baselibrary/utils/importfun.py +123 -123
- re_common/baselibrary/utils/mfaker.py +57 -57
- re_common/baselibrary/utils/my_abc/__init__.py +3 -3
- re_common/baselibrary/utils/my_abc/better_abc.py +32 -32
- re_common/baselibrary/utils/mylogger.py +414 -414
- re_common/baselibrary/utils/myredisclient.py +861 -861
- re_common/baselibrary/utils/pipupgrade.py +21 -21
- re_common/baselibrary/utils/ringlist.py +85 -85
- re_common/baselibrary/utils/version_compare.py +36 -36
- re_common/baselibrary/utils/ydmhttp.py +126 -126
- re_common/facade/lazy_import.py +11 -11
- re_common/facade/loggerfacade.py +25 -25
- re_common/facade/mysqlfacade.py +467 -467
- re_common/facade/now.py +31 -31
- re_common/facade/sqlite3facade.py +257 -257
- re_common/facade/use/mq_use_facade.py +83 -83
- re_common/facade/use/proxy_use_facade.py +19 -19
- re_common/libtest/base_dict_test.py +19 -19
- re_common/libtest/baseavro_test.py +13 -13
- re_common/libtest/basefile_test.py +14 -14
- re_common/libtest/basemssql_test.py +77 -77
- re_common/libtest/baseodbc_test.py +7 -7
- re_common/libtest/basepandas_test.py +38 -38
- re_common/libtest/get_attr_test/get_attr_test_settings.py +14 -14
- re_common/libtest/get_attr_test/settings.py +54 -54
- re_common/libtest/idencode_test.py +53 -53
- re_common/libtest/iniconfig_test.py +35 -35
- re_common/libtest/ip_test.py +34 -34
- re_common/libtest/merge_file_test.py +20 -20
- re_common/libtest/mfaker_test.py +8 -8
- re_common/libtest/mm3_test.py +31 -31
- re_common/libtest/mylogger_test.py +88 -88
- re_common/libtest/myparsel_test.py +27 -27
- re_common/libtest/mysql_test.py +151 -151
- re_common/libtest/pymongo_test.py +21 -21
- re_common/libtest/split_test.py +11 -11
- re_common/libtest/sqlite3_merge_test.py +5 -5
- re_common/libtest/sqlite3_test.py +34 -34
- re_common/libtest/tomlconfig_test.py +30 -30
- re_common/libtest/use_tools_test/__init__.py +2 -2
- re_common/libtest/user/__init__.py +4 -4
- re_common/studio/__init__.py +4 -4
- re_common/studio/assignment_expressions.py +36 -36
- re_common/studio/mydash/test1.py +18 -18
- re_common/studio/pydashstudio/first.py +9 -9
- re_common/studio/streamlitstudio/first_app.py +65 -65
- re_common/studio/streamlitstudio/uber_pickups.py +23 -23
- re_common/studio/test.py +18 -18
- re_common/v2/baselibrary/business_utils/BusinessStringUtil.py +235 -220
- re_common/v2/baselibrary/business_utils/baseencodeid.py +100 -100
- re_common/v2/baselibrary/business_utils/full_doi_path.py +116 -116
- re_common/v2/baselibrary/business_utils/rel_tools.py +6 -6
- re_common/v2/baselibrary/decorators/utils.py +59 -59
- re_common/v2/baselibrary/helpers/search_packge/NearestNeighbors_test.py +105 -105
- re_common/v2/baselibrary/helpers/search_packge/fit_text_match.py +253 -253
- re_common/v2/baselibrary/helpers/search_packge/scikit_learn_text_matcher.py +260 -260
- re_common/v2/baselibrary/helpers/search_packge/test.py +1 -1
- re_common/v2/baselibrary/s3object/baseboto3.py +230 -230
- re_common/v2/baselibrary/tools/WeChatRobot.py +95 -95
- re_common/v2/baselibrary/tools/ac_ahocorasick.py +75 -75
- re_common/v2/baselibrary/tools/concurrency.py +35 -35
- re_common/v2/baselibrary/tools/data_processer/base.py +53 -53
- re_common/v2/baselibrary/tools/data_processer/data_processer.py +497 -508
- re_common/v2/baselibrary/tools/data_processer/data_reader.py +187 -187
- re_common/v2/baselibrary/tools/data_processer/data_writer.py +38 -38
- re_common/v2/baselibrary/tools/dict_tools.py +44 -44
- re_common/v2/baselibrary/tools/dolphinscheduler.py +187 -187
- re_common/v2/baselibrary/tools/hdfs_base_processor.py +204 -204
- re_common/v2/baselibrary/tools/hdfs_bulk_processor.py +67 -67
- re_common/v2/baselibrary/tools/hdfs_data_processer.py +338 -338
- re_common/v2/baselibrary/tools/hdfs_line_processor.py +74 -74
- re_common/v2/baselibrary/tools/list_tools.py +69 -69
- re_common/v2/baselibrary/tools/resume_tracker.py +94 -94
- re_common/v2/baselibrary/tools/search_hash_tools.py +54 -54
- re_common/v2/baselibrary/tools/text_matcher.py +326 -326
- re_common/v2/baselibrary/tools/tree_processor/__init__.py +0 -0
- re_common/v2/baselibrary/tools/tree_processor/builder.py +25 -0
- re_common/v2/baselibrary/tools/tree_processor/node.py +13 -0
- re_common/v2/baselibrary/tools/unionfind_tools.py +60 -60
- re_common/v2/baselibrary/utils/BusinessStringUtil.py +196 -196
- re_common/v2/baselibrary/utils/api_net_utils.py +270 -270
- re_common/v2/baselibrary/utils/author_smi.py +361 -361
- re_common/v2/baselibrary/utils/base_string_similarity.py +158 -158
- re_common/v2/baselibrary/utils/basedict.py +37 -37
- re_common/v2/baselibrary/utils/basehdfs.py +163 -163
- re_common/v2/baselibrary/utils/basepika.py +180 -180
- re_common/v2/baselibrary/utils/basetime.py +94 -77
- re_common/v2/baselibrary/utils/db.py +174 -156
- re_common/v2/baselibrary/utils/elasticsearch.py +46 -0
- re_common/v2/baselibrary/utils/json_cls.py +16 -16
- re_common/v2/baselibrary/utils/mq.py +83 -83
- re_common/v2/baselibrary/utils/n_ary_expression_tree.py +243 -243
- re_common/v2/baselibrary/utils/string_bool.py +187 -186
- re_common/v2/baselibrary/utils/string_clear.py +246 -246
- re_common/v2/baselibrary/utils/string_smi.py +18 -18
- re_common/v2/baselibrary/utils/stringutils.py +312 -271
- re_common/vip/base_step_process.py +11 -11
- re_common/vip/baseencodeid.py +90 -90
- re_common/vip/changetaskname.py +28 -28
- re_common/vip/core_var.py +24 -24
- re_common/vip/mmh3Hash.py +89 -89
- re_common/vip/proxy/allproxys.py +127 -127
- re_common/vip/proxy/allproxys_thread.py +159 -159
- re_common/vip/proxy/cnki_proxy.py +153 -153
- re_common/vip/proxy/kuaidaili.py +87 -87
- re_common/vip/proxy/proxy_all.py +113 -113
- re_common/vip/proxy/update_kuaidaili_0.py +42 -42
- re_common/vip/proxy/wanfang_proxy.py +152 -152
- re_common/vip/proxy/wp_proxy_all.py +181 -181
- re_common/vip/read_rawid_to_txt.py +91 -91
- re_common/vip/title/__init__.py +5 -5
- re_common/vip/title/transform/TransformBookTitleToZt.py +125 -125
- re_common/vip/title/transform/TransformConferenceTitleToZt.py +139 -139
- re_common/vip/title/transform/TransformCstadTitleToZt.py +195 -195
- re_common/vip/title/transform/TransformJournalTitleToZt.py +203 -203
- re_common/vip/title/transform/TransformPatentTitleToZt.py +132 -132
- re_common/vip/title/transform/TransformRegulationTitleToZt.py +114 -114
- re_common/vip/title/transform/TransformStandardTitleToZt.py +135 -135
- re_common/vip/title/transform/TransformThesisTitleToZt.py +135 -135
- re_common/vip/title/transform/__init__.py +10 -10
- {re_common-10.0.39.dist-info → re_common-10.0.41.dist-info}/LICENSE +201 -201
- {re_common-10.0.39.dist-info → re_common-10.0.41.dist-info}/METADATA +16 -16
- re_common-10.0.41.dist-info/RECORD +252 -0
- {re_common-10.0.39.dist-info → re_common-10.0.41.dist-info}/WHEEL +1 -1
- re_common-10.0.39.dist-info/RECORD +0 -248
- {re_common-10.0.39.dist-info → re_common-10.0.41.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
|