hutool-python 1.0.0__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.
- hutool/__init__.py +174 -0
- hutool/cache/__init__.py +7 -0
- hutool/cache/cache_util.py +47 -0
- hutool/cache/fifo_cache.py +87 -0
- hutool/cache/lfu_cache.py +129 -0
- hutool/cache/lru_cache.py +93 -0
- hutool/cache/timed_cache.py +115 -0
- hutool/captcha/__init__.py +3 -0
- hutool/captcha/captcha_util.py +215 -0
- hutool/core/__init__.py +23 -0
- hutool/core/_base.py +61 -0
- hutool/core/bean.py +214 -0
- hutool/core/codec.py +111 -0
- hutool/core/coll.py +635 -0
- hutool/core/date.py +1024 -0
- hutool/core/exceptions.py +66 -0
- hutool/core/io/__init__.py +0 -0
- hutool/core/io/data_size_util.py +79 -0
- hutool/core/io/file_name_util.py +111 -0
- hutool/core/io/file_util.py +650 -0
- hutool/core/io/io_util.py +133 -0
- hutool/core/io/path_util.py +247 -0
- hutool/core/io/resource_util.py +137 -0
- hutool/core/map.py +933 -0
- hutool/core/math_util.py +105 -0
- hutool/core/net.py +288 -0
- hutool/core/text/__init__.py +0 -0
- hutool/core/text/csv_util.py +54 -0
- hutool/core/text/str_builder.py +224 -0
- hutool/core/text/unicode_util.py +58 -0
- hutool/core/tree.py +242 -0
- hutool/core/util/__init__.py +63 -0
- hutool/core/util/array_util.py +503 -0
- hutool/core/util/boolean_util.py +124 -0
- hutool/core/util/charset_util.py +60 -0
- hutool/core/util/class_util.py +136 -0
- hutool/core/util/coordinate_util.py +186 -0
- hutool/core/util/credit_code_util.py +110 -0
- hutool/core/util/desensitized_util.py +194 -0
- hutool/core/util/enum_util.py +94 -0
- hutool/core/util/escape_util.py +97 -0
- hutool/core/util/hash_util.py +243 -0
- hutool/core/util/hex_util.py +140 -0
- hutool/core/util/id_util.py +147 -0
- hutool/core/util/idcard_util.py +300 -0
- hutool/core/util/number_util.py +720 -0
- hutool/core/util/object_util.py +294 -0
- hutool/core/util/page_util.py +61 -0
- hutool/core/util/phone_util.py +140 -0
- hutool/core/util/random_util.py +112 -0
- hutool/core/util/re_util.py +231 -0
- hutool/core/util/reflect_util.py +135 -0
- hutool/core/util/runtime_util.py +89 -0
- hutool/core/util/str_util.py +2320 -0
- hutool/core/util/system_util.py +62 -0
- hutool/core/util/url_util.py +232 -0
- hutool/core/util/version_util.py +41 -0
- hutool/core/util/xml_util.py +158 -0
- hutool/core/util/zip_util.py +126 -0
- hutool/cron/__init__.py +4 -0
- hutool/cron/cron_pattern.py +123 -0
- hutool/cron/cron_util.py +115 -0
- hutool/crypto/__init__.py +5 -0
- hutool/crypto/digest_util.py +167 -0
- hutool/crypto/secure_util.py +311 -0
- hutool/crypto/sign_util.py +74 -0
- hutool/dfa/__init__.py +3 -0
- hutool/dfa/sensitive_util.py +114 -0
- hutool/extra/__init__.py +6 -0
- hutool/extra/emoji_util.py +90 -0
- hutool/extra/pinyin_util.py +44 -0
- hutool/extra/qr_code_util.py +58 -0
- hutool/extra/template_util.py +41 -0
- hutool/http/__init__.py +6 -0
- hutool/http/html_util.py +88 -0
- hutool/http/http_request.py +188 -0
- hutool/http/http_response.py +139 -0
- hutool/http/http_util.py +237 -0
- hutool/json_util.py +251 -0
- hutool/jwt_util.py +57 -0
- hutool/setting/__init__.py +5 -0
- hutool/setting/props_util.py +79 -0
- hutool/setting/setting_util.py +80 -0
- hutool/setting/yaml_util.py +45 -0
- hutool_python-1.0.0.dist-info/LICENSE +127 -0
- hutool_python-1.0.0.dist-info/METADATA +438 -0
- hutool_python-1.0.0.dist-info/RECORD +89 -0
- hutool_python-1.0.0.dist-info/WHEEL +5 -0
- hutool_python-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,2320 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Java Hutool StrUtil 的 Python 移植版。
|
|
3
|
+
|
|
4
|
+
包含字符和字符串工具类:CharPool、CharUtil、CharSequenceUtil、StrPool、StrUtil。
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import array
|
|
8
|
+
import codecs
|
|
9
|
+
import re
|
|
10
|
+
from difflib import SequenceMatcher
|
|
11
|
+
from typing import Any, Callable, List, Optional, Union
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"CharPool",
|
|
15
|
+
"CharSequenceUtil",
|
|
16
|
+
"CharUtil",
|
|
17
|
+
"StrPool",
|
|
18
|
+
"StrUtil",
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# ---------------------------------------------------------------------------
|
|
23
|
+
# CharPool
|
|
24
|
+
# ---------------------------------------------------------------------------
|
|
25
|
+
class CharPool:
|
|
26
|
+
"""常用字符常量。"""
|
|
27
|
+
|
|
28
|
+
# 空字符串 ''
|
|
29
|
+
EMPTY: str = ""
|
|
30
|
+
# 字符常量:空格符 ' '
|
|
31
|
+
SPACE: str = " "
|
|
32
|
+
# 字符常量:制表符 '\t'
|
|
33
|
+
TAB: str = "\t"
|
|
34
|
+
# 字符常量:点 '.'
|
|
35
|
+
DOT: str = "."
|
|
36
|
+
# 字符常量:斜杠 '/'
|
|
37
|
+
SLASH: str = "/"
|
|
38
|
+
# 字符常量:反斜杠 '\\'
|
|
39
|
+
BACKSLASH: str = "\\"
|
|
40
|
+
# 字符常量:回车符 '\r'
|
|
41
|
+
CR: str = "\r"
|
|
42
|
+
# 字符常量:换行符 '\n'
|
|
43
|
+
LF: str = "\n"
|
|
44
|
+
# 字符常量:减号(连接符) '-'
|
|
45
|
+
DASHED: str = "-"
|
|
46
|
+
# 字符常量:下划线 '_'
|
|
47
|
+
UNDERLINE: str = "_"
|
|
48
|
+
# 字符常量:逗号 ','
|
|
49
|
+
COMMA: str = ","
|
|
50
|
+
# 字符常量:花括号(左) '{'
|
|
51
|
+
DELIM_START: str = "{"
|
|
52
|
+
# 字符常量:花括号(右) '}'
|
|
53
|
+
DELIM_END: str = "}"
|
|
54
|
+
# 字符常量:中括号(左) '['
|
|
55
|
+
BRACKET_START: str = "["
|
|
56
|
+
# 字符常量:中括号(右) ']'
|
|
57
|
+
BRACKET_END: str = "]"
|
|
58
|
+
# 字符常量:双引号 '"'
|
|
59
|
+
DOUBLE_QUOTES: str = '"'
|
|
60
|
+
# 字符常量:单引号 '\''
|
|
61
|
+
SINGLE_QUOTE: str = "'"
|
|
62
|
+
# 字符常量:与 '&'
|
|
63
|
+
AMP: str = "&"
|
|
64
|
+
# 字符常量:冒号 ':'
|
|
65
|
+
COLON: str = ":"
|
|
66
|
+
# 字符常量:艾特 '@'
|
|
67
|
+
AT: str = "@"
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
# ---------------------------------------------------------------------------
|
|
71
|
+
# CharUtil
|
|
72
|
+
# ---------------------------------------------------------------------------
|
|
73
|
+
class CharUtil(CharPool):
|
|
74
|
+
"""字符工具类。"""
|
|
75
|
+
|
|
76
|
+
@staticmethod
|
|
77
|
+
def is_ascii(ch: str) -> bool:
|
|
78
|
+
"""
|
|
79
|
+
判断字符是否为ASCII字符(0~127)。
|
|
80
|
+
|
|
81
|
+
:param ch: 待检查的字符
|
|
82
|
+
:return: 是否为ASCII字符
|
|
83
|
+
"""
|
|
84
|
+
return ord(ch) < 128
|
|
85
|
+
|
|
86
|
+
@staticmethod
|
|
87
|
+
def is_ascii_printable(ch: str) -> bool:
|
|
88
|
+
"""
|
|
89
|
+
判断字符是否为可打印ASCII字符(32~126)。
|
|
90
|
+
|
|
91
|
+
:param ch: 待检查的字符
|
|
92
|
+
:return: 是否为可打印ASCII字符
|
|
93
|
+
"""
|
|
94
|
+
return 32 <= ord(ch) < 127
|
|
95
|
+
|
|
96
|
+
@staticmethod
|
|
97
|
+
def is_ascii_control(ch: str) -> bool:
|
|
98
|
+
"""
|
|
99
|
+
判断字符是否为ASCII控制字符(0~31和127)。
|
|
100
|
+
|
|
101
|
+
:param ch: 待检查的字符
|
|
102
|
+
:return: 是否为控制字符
|
|
103
|
+
"""
|
|
104
|
+
ord_ch = ord(ch)
|
|
105
|
+
return ord_ch < 32 or ord_ch == 127
|
|
106
|
+
|
|
107
|
+
@staticmethod
|
|
108
|
+
def is_letter(ch: str) -> bool:
|
|
109
|
+
"""
|
|
110
|
+
判断字符是否为字母(A~Z或a~z)。
|
|
111
|
+
|
|
112
|
+
:param ch: 待检查的字符
|
|
113
|
+
:return: 是否为字母
|
|
114
|
+
"""
|
|
115
|
+
return CharUtil.is_letter_upper(ch) or CharUtil.is_letter_lower(ch)
|
|
116
|
+
|
|
117
|
+
@staticmethod
|
|
118
|
+
def is_letter_upper(ch: str) -> bool:
|
|
119
|
+
"""
|
|
120
|
+
判断字符是否为大写字母(A~Z)。
|
|
121
|
+
|
|
122
|
+
:param ch: 待检查的字符
|
|
123
|
+
:return: 是否为大写字母
|
|
124
|
+
"""
|
|
125
|
+
return "A" <= ch <= "Z"
|
|
126
|
+
|
|
127
|
+
@staticmethod
|
|
128
|
+
def is_letter_lower(ch: str) -> bool:
|
|
129
|
+
"""
|
|
130
|
+
判断字符是否为小写字母(a~z)。
|
|
131
|
+
|
|
132
|
+
:param ch: 待检查的字符
|
|
133
|
+
:return: 是否为小写字母
|
|
134
|
+
"""
|
|
135
|
+
return "a" <= ch <= "z"
|
|
136
|
+
|
|
137
|
+
@staticmethod
|
|
138
|
+
def is_number(ch: str) -> bool:
|
|
139
|
+
"""
|
|
140
|
+
判断字符是否为数字(0~9)。
|
|
141
|
+
|
|
142
|
+
:param ch: 待检查的字符
|
|
143
|
+
:return: 是否为数字
|
|
144
|
+
"""
|
|
145
|
+
return "0" <= ch <= "9"
|
|
146
|
+
|
|
147
|
+
@staticmethod
|
|
148
|
+
def is_hex_char(ch: str) -> bool:
|
|
149
|
+
"""
|
|
150
|
+
判断字符是否为十六进制字符(0~9, a~f, A~F)。
|
|
151
|
+
|
|
152
|
+
:param ch: 待检查的字符
|
|
153
|
+
:return: 是否为十六进制字符
|
|
154
|
+
"""
|
|
155
|
+
return CharUtil.is_number(ch) or ("a" <= ch <= "f") or ("A" <= ch <= "F")
|
|
156
|
+
|
|
157
|
+
@staticmethod
|
|
158
|
+
def is_letter_or_number(ch: str) -> bool:
|
|
159
|
+
"""
|
|
160
|
+
判断字符是否为字母或数字(A~Z, a~z, 0~9)。
|
|
161
|
+
|
|
162
|
+
:param ch: 待检查的字符
|
|
163
|
+
:return: 是否为字母或数字
|
|
164
|
+
"""
|
|
165
|
+
return CharUtil.is_letter(ch) or CharUtil.is_number(ch)
|
|
166
|
+
|
|
167
|
+
@staticmethod
|
|
168
|
+
def is_blank_char(ch: str) -> bool:
|
|
169
|
+
"""
|
|
170
|
+
判断字符是否为空白字符。
|
|
171
|
+
空白字符包括空格、制表符、全角空格和不间断空格。
|
|
172
|
+
|
|
173
|
+
:param ch: 待检查的字符
|
|
174
|
+
:return: 是否为空白字符
|
|
175
|
+
"""
|
|
176
|
+
return ch.isspace() or ch == "" or ch == "" or ch == "\x00" or ch == "ㅤ" or ch == "⠀" or ch == ""
|
|
177
|
+
|
|
178
|
+
@staticmethod
|
|
179
|
+
def is_emoji(ch: str) -> bool:
|
|
180
|
+
"""
|
|
181
|
+
判断字符是否为表情符号。
|
|
182
|
+
|
|
183
|
+
:param ch: 待检查的字符
|
|
184
|
+
:return: 是否为表情符号
|
|
185
|
+
"""
|
|
186
|
+
ord_ch = ord(ch)
|
|
187
|
+
return not (
|
|
188
|
+
ord_ch == 0x0
|
|
189
|
+
or ord_ch == 0x9
|
|
190
|
+
or ord_ch == 0xA
|
|
191
|
+
or ord_ch == 0xD
|
|
192
|
+
or 0x20 <= ord_ch <= 0xD7FF
|
|
193
|
+
or 0xE000 <= ord_ch <= 0xFFFD
|
|
194
|
+
or 0x100000 <= ord_ch <= 0x10FFFF
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
@staticmethod
|
|
198
|
+
def is_file_separator(ch: str) -> bool:
|
|
199
|
+
"""
|
|
200
|
+
判断字符是否为文件分隔符('/' 或 '\\')。
|
|
201
|
+
|
|
202
|
+
:param ch: 待检查的字符
|
|
203
|
+
:return: 是否为文件分隔符
|
|
204
|
+
"""
|
|
205
|
+
return ch == CharPool.SLASH or ch == CharPool.BACKSLASH
|
|
206
|
+
|
|
207
|
+
@staticmethod
|
|
208
|
+
def equals(ch1: str, ch2: str, case_insensitive: bool) -> bool:
|
|
209
|
+
"""
|
|
210
|
+
比较两个字符是否相等。
|
|
211
|
+
|
|
212
|
+
:param ch1: 第一个字符
|
|
213
|
+
:param ch2: 第二个字符
|
|
214
|
+
:param case_insensitive: 是否忽略大小写
|
|
215
|
+
:return: 两个字符是否相等
|
|
216
|
+
"""
|
|
217
|
+
if case_insensitive:
|
|
218
|
+
return ch1.lower() == ch2.lower()
|
|
219
|
+
return ch1 == ch2
|
|
220
|
+
|
|
221
|
+
@staticmethod
|
|
222
|
+
def digit16(ch: str) -> str:
|
|
223
|
+
"""
|
|
224
|
+
获取字符的十六进制值。
|
|
225
|
+
|
|
226
|
+
:param ch: 待转换的字符
|
|
227
|
+
:return: 十六进制字符串表示
|
|
228
|
+
"""
|
|
229
|
+
return hex(ord(ch))
|
|
230
|
+
|
|
231
|
+
@staticmethod
|
|
232
|
+
def to_close_char(ch: str) -> str:
|
|
233
|
+
"""
|
|
234
|
+
将字母或数字转换为带圈字符形式。
|
|
235
|
+
|
|
236
|
+
Examples::
|
|
237
|
+
|
|
238
|
+
'1' -> '①' (①)
|
|
239
|
+
'A' -> 'Ⓐ' (Ⓐ)
|
|
240
|
+
'a' -> 'ⓐ' (ⓐ)
|
|
241
|
+
|
|
242
|
+
如果字符不支持转换,则返回原字符。
|
|
243
|
+
|
|
244
|
+
:param ch: 待转换的字符
|
|
245
|
+
:return: 带圈字符,不支持时返回原字符
|
|
246
|
+
"""
|
|
247
|
+
result = ord(ch)
|
|
248
|
+
if "1" <= ch <= "9":
|
|
249
|
+
result = ord("①") + result - ord("1")
|
|
250
|
+
elif "A" <= ch <= "Z":
|
|
251
|
+
result = ord("Ⓐ") + result - ord("A")
|
|
252
|
+
elif "a" <= ch <= "z":
|
|
253
|
+
result = ord("ⓐ") + result - ord("a")
|
|
254
|
+
return chr(result)
|
|
255
|
+
|
|
256
|
+
@staticmethod
|
|
257
|
+
def to_close_by_number(number: int) -> str:
|
|
258
|
+
"""
|
|
259
|
+
将数字(1~20)转换为带圈数字形式。
|
|
260
|
+
|
|
261
|
+
Examples::
|
|
262
|
+
|
|
263
|
+
1 -> '①' (①)
|
|
264
|
+
12 -> '⑫' (⑫)
|
|
265
|
+
20 -> '⑳' (⑳)
|
|
266
|
+
|
|
267
|
+
:param number: 待转换的数字(必须在1~20范围内)
|
|
268
|
+
:return: 带圈数字字符
|
|
269
|
+
:raises ValueError: 数字不在[1, 20]范围内时抛出异常
|
|
270
|
+
"""
|
|
271
|
+
if number < 1 or number > 20:
|
|
272
|
+
raise ValueError("Number must be [1-20]")
|
|
273
|
+
return chr(ord("①") + number - 1)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
# ---------------------------------------------------------------------------
|
|
277
|
+
# CharSequenceUtil
|
|
278
|
+
# ---------------------------------------------------------------------------
|
|
279
|
+
class CharSequenceUtil:
|
|
280
|
+
"""
|
|
281
|
+
CharSequence 工具类,提供常用的字符串操作。
|
|
282
|
+
"""
|
|
283
|
+
|
|
284
|
+
# ------------------------------------------------------------------
|
|
285
|
+
# Blank / empty checks
|
|
286
|
+
# ------------------------------------------------------------------
|
|
287
|
+
|
|
288
|
+
@staticmethod
|
|
289
|
+
def is_blank(string: Optional[str]) -> bool:
|
|
290
|
+
"""
|
|
291
|
+
判断字符串是否为空白。当字符串为 None、空字符串(``""``)
|
|
292
|
+
或全部由空白字符组成时,视为空白。
|
|
293
|
+
|
|
294
|
+
:param string: 待检查的字符串
|
|
295
|
+
:return: 是否为空白字符串
|
|
296
|
+
"""
|
|
297
|
+
if CharSequenceUtil.is_empty(string):
|
|
298
|
+
return True
|
|
299
|
+
for char in string:
|
|
300
|
+
if not CharUtil.is_blank_char(char):
|
|
301
|
+
return False
|
|
302
|
+
return True
|
|
303
|
+
|
|
304
|
+
@staticmethod
|
|
305
|
+
def is_not_blank(string: Optional[str]) -> bool:
|
|
306
|
+
"""
|
|
307
|
+
判断字符串是否不为空白(与 :meth:`is_blank` 相反)。
|
|
308
|
+
|
|
309
|
+
:param string: 待检查的字符串
|
|
310
|
+
:return: 是否不为空白字符串
|
|
311
|
+
"""
|
|
312
|
+
return not CharSequenceUtil.is_blank(string)
|
|
313
|
+
|
|
314
|
+
@staticmethod
|
|
315
|
+
def has_blank(*strings: Optional[str]) -> bool:
|
|
316
|
+
"""
|
|
317
|
+
判断给定的多个字符串中是否存在空白字符串。
|
|
318
|
+
当 *strings* 为空或其中任一元素为空白时返回 True。
|
|
319
|
+
|
|
320
|
+
:param strings: 一个或多个待检查的字符串
|
|
321
|
+
:return: 是否存在空白字符串
|
|
322
|
+
"""
|
|
323
|
+
if not strings:
|
|
324
|
+
return True
|
|
325
|
+
for string in strings:
|
|
326
|
+
if CharSequenceUtil.is_blank(string):
|
|
327
|
+
return True
|
|
328
|
+
return False
|
|
329
|
+
|
|
330
|
+
@staticmethod
|
|
331
|
+
def is_all_blank(*strings: Optional[str]) -> bool:
|
|
332
|
+
"""
|
|
333
|
+
判断给定的多个字符串是否全部为空白。
|
|
334
|
+
当 *strings* 为空或所有元素均为空白时返回 True。
|
|
335
|
+
|
|
336
|
+
:param strings: 一个或多个待检查的字符串
|
|
337
|
+
:return: 是否全部为空白字符串
|
|
338
|
+
"""
|
|
339
|
+
if not strings:
|
|
340
|
+
return True
|
|
341
|
+
for string in strings:
|
|
342
|
+
if CharSequenceUtil.is_not_blank(string):
|
|
343
|
+
return False
|
|
344
|
+
return True
|
|
345
|
+
|
|
346
|
+
@staticmethod
|
|
347
|
+
def is_empty(string: Optional[str]) -> bool:
|
|
348
|
+
"""
|
|
349
|
+
判断字符串是否为空(None 或 ``""``)。
|
|
350
|
+
|
|
351
|
+
:param string: 待检查的字符串
|
|
352
|
+
:return: 是否为 None 或空字符串
|
|
353
|
+
"""
|
|
354
|
+
return string is None or len(string) == 0
|
|
355
|
+
|
|
356
|
+
@staticmethod
|
|
357
|
+
def is_not_empty(string: Optional[str]) -> bool:
|
|
358
|
+
"""
|
|
359
|
+
判断字符串是否不为空(与 :meth:`is_empty` 相反)。
|
|
360
|
+
|
|
361
|
+
:param string: 待检查的字符串
|
|
362
|
+
:return: 是否不为 None 且不为空
|
|
363
|
+
"""
|
|
364
|
+
return not CharSequenceUtil.is_empty(string)
|
|
365
|
+
|
|
366
|
+
@staticmethod
|
|
367
|
+
def empty_if_none(string: Optional[str]) -> str:
|
|
368
|
+
"""
|
|
369
|
+
如果 *string* 为 None 则返回空字符串,否则返回原字符串。
|
|
370
|
+
|
|
371
|
+
:param string: 待检查的字符串
|
|
372
|
+
:return: 原字符串或 ``""``
|
|
373
|
+
"""
|
|
374
|
+
return CharSequenceUtil.none_to_empty(string)
|
|
375
|
+
|
|
376
|
+
@staticmethod
|
|
377
|
+
def none_to_empty(string: Optional[str]) -> str:
|
|
378
|
+
"""
|
|
379
|
+
将 None 转换为空字符串。
|
|
380
|
+
|
|
381
|
+
:param string: 待转换的字符串
|
|
382
|
+
:return: *string* 为 None 时返回 ``""``,否则返回原字符串
|
|
383
|
+
"""
|
|
384
|
+
return CharSequenceUtil.none_to_default(string, CharPool.EMPTY)
|
|
385
|
+
|
|
386
|
+
@staticmethod
|
|
387
|
+
def none_to_default(string: Optional[str], default: str) -> str:
|
|
388
|
+
"""
|
|
389
|
+
如果 *string* 为 None 则返回 *default*,否则返回 str(*string*)。
|
|
390
|
+
|
|
391
|
+
:param string: 待转换的字符串
|
|
392
|
+
:param default: 默认值
|
|
393
|
+
:return: 字符串本身或默认值
|
|
394
|
+
"""
|
|
395
|
+
return default if string is None else str(string)
|
|
396
|
+
|
|
397
|
+
@staticmethod
|
|
398
|
+
def empty_to_default(string: Optional[str], default: str) -> str:
|
|
399
|
+
"""
|
|
400
|
+
如果 *string* 为 None 或 ``""`` 则返回 *default*,否则返回 str(*string*)。
|
|
401
|
+
|
|
402
|
+
:param string: 待转换的字符串
|
|
403
|
+
:param default: 默认值
|
|
404
|
+
:return: 字符串本身或默认值
|
|
405
|
+
"""
|
|
406
|
+
return default if CharSequenceUtil.is_empty(string) else str(string)
|
|
407
|
+
|
|
408
|
+
@staticmethod
|
|
409
|
+
def blank_to_default(string: Optional[str], default: str) -> str:
|
|
410
|
+
"""
|
|
411
|
+
如果 *string* 为 None、空字符串或空白字符串则返回 *default*,否则返回 str(*string*)。
|
|
412
|
+
|
|
413
|
+
:param string: 待转换的字符串
|
|
414
|
+
:param default: 默认值
|
|
415
|
+
:return: 字符串本身或默认值
|
|
416
|
+
"""
|
|
417
|
+
return default if CharSequenceUtil.is_blank(string) else str(string)
|
|
418
|
+
|
|
419
|
+
@staticmethod
|
|
420
|
+
def empty_to_none(string: Optional[str]) -> Optional[str]:
|
|
421
|
+
"""
|
|
422
|
+
将空字符串转换为 None。
|
|
423
|
+
|
|
424
|
+
:param string: 待转换的字符串
|
|
425
|
+
:return: *string* 为空时返回 None,否则返回原字符串
|
|
426
|
+
"""
|
|
427
|
+
return None if CharSequenceUtil.is_empty(string) else str(string)
|
|
428
|
+
|
|
429
|
+
@staticmethod
|
|
430
|
+
def has_empty(*strings: Optional[str]) -> bool:
|
|
431
|
+
"""
|
|
432
|
+
判断给定的多个字符串中是否存在空字符串(None 或 ``""``)。
|
|
433
|
+
当 *strings* 为空或其中任一元素为空时返回 True。
|
|
434
|
+
|
|
435
|
+
:param strings: 一个或多个待检查的字符串
|
|
436
|
+
:return: 是否存在空字符串
|
|
437
|
+
"""
|
|
438
|
+
if not strings:
|
|
439
|
+
return True
|
|
440
|
+
for string in strings:
|
|
441
|
+
if CharSequenceUtil.is_empty(string):
|
|
442
|
+
return True
|
|
443
|
+
return False
|
|
444
|
+
|
|
445
|
+
@staticmethod
|
|
446
|
+
def is_all_empty(*strings: Optional[str]) -> bool:
|
|
447
|
+
"""
|
|
448
|
+
判断给定的多个字符串是否全部为空。
|
|
449
|
+
当 *strings* 为空或所有元素均为空时返回 True。
|
|
450
|
+
|
|
451
|
+
:param strings: 一个或多个待检查的字符串
|
|
452
|
+
:return: 是否全部为空字符串
|
|
453
|
+
"""
|
|
454
|
+
if not strings:
|
|
455
|
+
return True
|
|
456
|
+
for string in strings:
|
|
457
|
+
if CharSequenceUtil.is_not_empty(string):
|
|
458
|
+
return False
|
|
459
|
+
return True
|
|
460
|
+
|
|
461
|
+
@staticmethod
|
|
462
|
+
def is_all_not_empty(*strings: Optional[str]) -> bool:
|
|
463
|
+
"""
|
|
464
|
+
判断给定的多个字符串是否全部不为空。
|
|
465
|
+
|
|
466
|
+
:param strings: 一个或多个待检查的字符串
|
|
467
|
+
:return: 是否没有空字符串
|
|
468
|
+
"""
|
|
469
|
+
return not CharSequenceUtil.has_empty(*strings)
|
|
470
|
+
|
|
471
|
+
@staticmethod
|
|
472
|
+
def is_all_not_blank(*strings: Optional[str]) -> bool:
|
|
473
|
+
"""
|
|
474
|
+
判断给定的多个字符串是否全部不为空白。
|
|
475
|
+
|
|
476
|
+
:param strings: 一个或多个待检查的字符串
|
|
477
|
+
:return: 是否没有空白字符串
|
|
478
|
+
"""
|
|
479
|
+
return not CharSequenceUtil.has_blank(*strings)
|
|
480
|
+
|
|
481
|
+
@staticmethod
|
|
482
|
+
def is_none_or_undefined(string: Optional[str]) -> bool:
|
|
483
|
+
"""
|
|
484
|
+
判断 *string* 是否为 None、``""``、``"None"`` 或 ``"undefined"``。
|
|
485
|
+
|
|
486
|
+
:param string: 待检查的字符串
|
|
487
|
+
:return: 是否匹配哨兵值
|
|
488
|
+
"""
|
|
489
|
+
if string is None:
|
|
490
|
+
return True
|
|
491
|
+
return CharSequenceUtil.is_none_or_undefined_str(string)
|
|
492
|
+
|
|
493
|
+
@staticmethod
|
|
494
|
+
def is_empty_or_undefined(string: Optional[str]) -> bool:
|
|
495
|
+
"""
|
|
496
|
+
判断 *string* 是否为空或等于 ``"None"`` / ``"undefined"``。
|
|
497
|
+
|
|
498
|
+
:param string: 待检查的字符串
|
|
499
|
+
:return: 是否为空或哨兵值
|
|
500
|
+
"""
|
|
501
|
+
if CharSequenceUtil.is_empty(string):
|
|
502
|
+
return True
|
|
503
|
+
return CharSequenceUtil.is_none_or_undefined_str(string)
|
|
504
|
+
|
|
505
|
+
@staticmethod
|
|
506
|
+
def is_blank_or_undefined(string: Optional[str]) -> bool:
|
|
507
|
+
"""
|
|
508
|
+
判断 *string* 是否为空白或等于 ``"None"`` / ``"undefined"``。
|
|
509
|
+
|
|
510
|
+
:param string: 待检查的字符串
|
|
511
|
+
:return: 是否为空白或哨兵值
|
|
512
|
+
"""
|
|
513
|
+
if CharSequenceUtil.is_blank(string):
|
|
514
|
+
return True
|
|
515
|
+
return CharSequenceUtil.is_none_or_undefined_str(string)
|
|
516
|
+
|
|
517
|
+
@staticmethod
|
|
518
|
+
def is_none_or_undefined_str(string: str) -> bool:
|
|
519
|
+
"""
|
|
520
|
+
不做 None 检查,直接判断 *string* 去除空白后是否等于 ``"None"`` 或 ``"undefined"``。
|
|
521
|
+
|
|
522
|
+
:param string: 待检查的字符串
|
|
523
|
+
:return: 是否为 ``"None"`` 或 ``"undefined"``
|
|
524
|
+
"""
|
|
525
|
+
str_string = str(string).strip()
|
|
526
|
+
return str_string == StrPool.NONE or str_string == "undefined"
|
|
527
|
+
|
|
528
|
+
# ------------------------------------------------------------------
|
|
529
|
+
# Trim
|
|
530
|
+
# ------------------------------------------------------------------
|
|
531
|
+
|
|
532
|
+
@staticmethod
|
|
533
|
+
def trim(
|
|
534
|
+
string: Optional[str],
|
|
535
|
+
mode: int = 0,
|
|
536
|
+
predicate: Callable[[str], bool] = CharUtil.is_blank_char,
|
|
537
|
+
) -> Optional[str]:
|
|
538
|
+
"""
|
|
539
|
+
根据 *predicate* 从字符串的头部和/或尾部裁剪字符。
|
|
540
|
+
|
|
541
|
+
:param string: 待裁剪的字符串
|
|
542
|
+
:param mode: ``-1`` = 仅裁剪头部,``0`` = 两端都裁剪,``1`` = 仅裁剪尾部
|
|
543
|
+
:param predicate: 返回 True 时表示该字符应被移除的函数
|
|
544
|
+
:return: 裁剪后的字符串,*string* 为 None 时返回 None
|
|
545
|
+
"""
|
|
546
|
+
result: Optional[str]
|
|
547
|
+
if string is None:
|
|
548
|
+
result = None
|
|
549
|
+
else:
|
|
550
|
+
length = len(string)
|
|
551
|
+
start = 0
|
|
552
|
+
end = length
|
|
553
|
+
if mode <= 0:
|
|
554
|
+
while start < end and predicate(string[start]):
|
|
555
|
+
start += 1
|
|
556
|
+
if mode >= 0:
|
|
557
|
+
while start < end and predicate(string[end - 1]):
|
|
558
|
+
end -= 1
|
|
559
|
+
if start > 0 or end < length:
|
|
560
|
+
result = str(string)[start:end]
|
|
561
|
+
else:
|
|
562
|
+
result = str(string)
|
|
563
|
+
return result
|
|
564
|
+
|
|
565
|
+
@staticmethod
|
|
566
|
+
def trim_to_empty(string: Optional[str]) -> str:
|
|
567
|
+
"""
|
|
568
|
+
裁剪两端空白。*string* 为 None 时返回 ``""``。
|
|
569
|
+
|
|
570
|
+
:param string: 待裁剪的字符串
|
|
571
|
+
:return: 裁剪后的字符串或 ``""``
|
|
572
|
+
"""
|
|
573
|
+
return CharPool.EMPTY if string is None else CharSequenceUtil.trim(string) # type: ignore[return-value]
|
|
574
|
+
|
|
575
|
+
@staticmethod
|
|
576
|
+
def trim_to_none(string: Optional[str]) -> Optional[str]:
|
|
577
|
+
"""
|
|
578
|
+
裁剪两端空白。*string* 为 None 或结果为空字符串时返回 None。
|
|
579
|
+
|
|
580
|
+
:param string: 待裁剪的字符串
|
|
581
|
+
:return: 裁剪后的字符串或 None
|
|
582
|
+
"""
|
|
583
|
+
trim_str = CharSequenceUtil.trim(string)
|
|
584
|
+
return None if trim_str == CharPool.EMPTY else trim_str
|
|
585
|
+
|
|
586
|
+
@staticmethod
|
|
587
|
+
def trim_start(string: Optional[str]) -> Optional[str]:
|
|
588
|
+
"""
|
|
589
|
+
仅裁剪字符串头部的空白。
|
|
590
|
+
|
|
591
|
+
:param string: 待裁剪的字符串
|
|
592
|
+
:return: 裁剪后的字符串,*string* 为 None 时返回 None
|
|
593
|
+
"""
|
|
594
|
+
return CharSequenceUtil.trim(string, mode=-1)
|
|
595
|
+
|
|
596
|
+
@staticmethod
|
|
597
|
+
def trim_end(string: Optional[str]) -> Optional[str]:
|
|
598
|
+
"""
|
|
599
|
+
仅裁剪字符串尾部的空白。
|
|
600
|
+
|
|
601
|
+
:param string: 待裁剪的字符串
|
|
602
|
+
:return: 裁剪后的字符串,*string* 为 None 时返回 None
|
|
603
|
+
"""
|
|
604
|
+
return CharSequenceUtil.trim(string, mode=1)
|
|
605
|
+
|
|
606
|
+
# ------------------------------------------------------------------
|
|
607
|
+
# Starts / ends with
|
|
608
|
+
# ------------------------------------------------------------------
|
|
609
|
+
|
|
610
|
+
@staticmethod
|
|
611
|
+
def start_with(
|
|
612
|
+
string: Optional[str],
|
|
613
|
+
prefix: Optional[str],
|
|
614
|
+
ignore_case: bool = False,
|
|
615
|
+
ignore_equals: bool = False,
|
|
616
|
+
) -> bool:
|
|
617
|
+
"""
|
|
618
|
+
判断 *string* 是否以 *prefix* 开头。
|
|
619
|
+
|
|
620
|
+
:param string: 待检查的字符串
|
|
621
|
+
:param prefix: 前缀
|
|
622
|
+
:param ignore_case: 是否忽略大小写
|
|
623
|
+
:param ignore_equals: 字符串相等时是否返回 False
|
|
624
|
+
:return: 是否以指定前缀开头
|
|
625
|
+
"""
|
|
626
|
+
if string is None or prefix is None:
|
|
627
|
+
if ignore_equals:
|
|
628
|
+
return False
|
|
629
|
+
return string is None and prefix is None
|
|
630
|
+
|
|
631
|
+
if ignore_case:
|
|
632
|
+
is_start = string.lower().startswith(prefix.lower())
|
|
633
|
+
else:
|
|
634
|
+
is_start = string.startswith(prefix)
|
|
635
|
+
|
|
636
|
+
if is_start:
|
|
637
|
+
if ignore_equals:
|
|
638
|
+
return not CharSequenceUtil.equals(string, prefix, ignore_case)
|
|
639
|
+
return True
|
|
640
|
+
return False
|
|
641
|
+
|
|
642
|
+
@staticmethod
|
|
643
|
+
def start_with_ignore_equals(string: Optional[str], prefix: Optional[str]) -> bool:
|
|
644
|
+
"""
|
|
645
|
+
判断 *string* 是否以 *prefix* 开头且不相等。
|
|
646
|
+
|
|
647
|
+
:param string: 待检查的字符串
|
|
648
|
+
:param prefix: 前缀
|
|
649
|
+
:return: 是否以指定前缀开头且两者不同
|
|
650
|
+
"""
|
|
651
|
+
return CharSequenceUtil.start_with(string, prefix, ignore_case=False, ignore_equals=True)
|
|
652
|
+
|
|
653
|
+
@staticmethod
|
|
654
|
+
def start_with_ignore_case(string: Optional[str], prefix: Optional[str]) -> bool:
|
|
655
|
+
"""
|
|
656
|
+
判断 *string* 是否以 *prefix* 开头,忽略大小写。
|
|
657
|
+
|
|
658
|
+
:param string: 待检查的字符串
|
|
659
|
+
:param prefix: 前缀
|
|
660
|
+
:return: 是否以指定前缀开头(不区分大小写)
|
|
661
|
+
"""
|
|
662
|
+
return CharSequenceUtil.start_with(string, prefix, ignore_case=True)
|
|
663
|
+
|
|
664
|
+
@staticmethod
|
|
665
|
+
def start_with_any(string: Optional[str], *prefixes: Optional[str]) -> bool:
|
|
666
|
+
"""
|
|
667
|
+
判断 *string* 是否以给定的任一 *prefixes* 开头。
|
|
668
|
+
|
|
669
|
+
:param string: 待检查的字符串
|
|
670
|
+
:param prefixes: 前缀列表
|
|
671
|
+
:return: 是否匹配任一前缀
|
|
672
|
+
"""
|
|
673
|
+
if CharSequenceUtil.is_empty(string) or not prefixes:
|
|
674
|
+
return False
|
|
675
|
+
for prefix in prefixes:
|
|
676
|
+
if CharSequenceUtil.start_with(string, prefix, ignore_case=False):
|
|
677
|
+
return True
|
|
678
|
+
return False
|
|
679
|
+
|
|
680
|
+
@staticmethod
|
|
681
|
+
def start_with_any_ignore_case(string: Optional[str], *prefixes: Optional[str]) -> bool:
|
|
682
|
+
"""
|
|
683
|
+
判断 *string* 是否以给定的任一 *prefixes* 开头,忽略大小写。
|
|
684
|
+
|
|
685
|
+
:param string: 待检查的字符串
|
|
686
|
+
:param prefixes: 前缀列表
|
|
687
|
+
:return: 是否匹配任一前缀(不区分大小写)
|
|
688
|
+
"""
|
|
689
|
+
if CharSequenceUtil.is_empty(string) or not prefixes:
|
|
690
|
+
return False
|
|
691
|
+
for prefix in prefixes:
|
|
692
|
+
if CharSequenceUtil.start_with(string, prefix, ignore_case=True):
|
|
693
|
+
return True
|
|
694
|
+
return False
|
|
695
|
+
|
|
696
|
+
@staticmethod
|
|
697
|
+
def end_with(
|
|
698
|
+
string: Optional[str],
|
|
699
|
+
suffix: Optional[str],
|
|
700
|
+
ignore_case: bool = False,
|
|
701
|
+
ignore_equals: bool = False,
|
|
702
|
+
) -> bool:
|
|
703
|
+
"""
|
|
704
|
+
判断 *string* 是否以 *suffix* 结尾。
|
|
705
|
+
|
|
706
|
+
:param string: 待检查的字符串
|
|
707
|
+
:param suffix: 后缀
|
|
708
|
+
:param ignore_case: 是否忽略大小写
|
|
709
|
+
:param ignore_equals: 字符串相等时是否返回 False
|
|
710
|
+
:return: 是否以指定后缀结尾
|
|
711
|
+
"""
|
|
712
|
+
if string is None or suffix is None:
|
|
713
|
+
if ignore_equals:
|
|
714
|
+
return False
|
|
715
|
+
return string is None and suffix is None
|
|
716
|
+
|
|
717
|
+
if ignore_case:
|
|
718
|
+
is_end = string.lower().endswith(suffix.lower())
|
|
719
|
+
else:
|
|
720
|
+
is_end = string.endswith(suffix)
|
|
721
|
+
|
|
722
|
+
if is_end:
|
|
723
|
+
if ignore_equals:
|
|
724
|
+
return not CharSequenceUtil.equals(string, suffix, ignore_case)
|
|
725
|
+
return True
|
|
726
|
+
return False
|
|
727
|
+
|
|
728
|
+
@staticmethod
|
|
729
|
+
def end_with_ignore_equals(string: Optional[str], suffix: Optional[str]) -> bool:
|
|
730
|
+
"""
|
|
731
|
+
判断 *string* 是否以 *suffix* 结尾且不相等。
|
|
732
|
+
|
|
733
|
+
:param string: 待检查的字符串
|
|
734
|
+
:param suffix: 后缀
|
|
735
|
+
:return: 是否以指定后缀结尾且两者不同
|
|
736
|
+
"""
|
|
737
|
+
return CharSequenceUtil.end_with(string, suffix, ignore_case=False, ignore_equals=True)
|
|
738
|
+
|
|
739
|
+
@staticmethod
|
|
740
|
+
def end_with_ignore_case(string: Optional[str], prefix: Optional[str]) -> bool:
|
|
741
|
+
"""
|
|
742
|
+
判断 *string* 是否以 *prefix* 结尾,忽略大小写。
|
|
743
|
+
|
|
744
|
+
:param string: 待检查的字符串
|
|
745
|
+
:param prefix: 后缀
|
|
746
|
+
:return: 是否以指定后缀结尾(不区分大小写)
|
|
747
|
+
"""
|
|
748
|
+
return CharSequenceUtil.end_with(string, prefix, ignore_case=True)
|
|
749
|
+
|
|
750
|
+
@staticmethod
|
|
751
|
+
def end_with_any(string: Optional[str], *prefixes: Optional[str]) -> bool:
|
|
752
|
+
"""
|
|
753
|
+
判断 *string* 是否以给定的任一 *prefixes* 结尾。
|
|
754
|
+
|
|
755
|
+
:param string: 待检查的字符串
|
|
756
|
+
:param prefixes: 后缀列表
|
|
757
|
+
:return: 是否匹配任一后缀
|
|
758
|
+
"""
|
|
759
|
+
if CharSequenceUtil.is_empty(string) or not prefixes:
|
|
760
|
+
return False
|
|
761
|
+
for prefix in prefixes:
|
|
762
|
+
if CharSequenceUtil.end_with(string, prefix, ignore_case=False):
|
|
763
|
+
return True
|
|
764
|
+
return False
|
|
765
|
+
|
|
766
|
+
@staticmethod
|
|
767
|
+
def end_with_any_ignore_case(string: Optional[str], *prefixes: Optional[str]) -> bool:
|
|
768
|
+
"""
|
|
769
|
+
判断 *string* 是否以给定的任一 *prefixes* 结尾,忽略大小写。
|
|
770
|
+
|
|
771
|
+
:param string: 待检查的字符串
|
|
772
|
+
:param prefixes: 后缀列表
|
|
773
|
+
:return: 是否匹配任一后缀(不区分大小写)
|
|
774
|
+
"""
|
|
775
|
+
if CharSequenceUtil.is_empty(string) or not prefixes:
|
|
776
|
+
return False
|
|
777
|
+
for prefix in prefixes:
|
|
778
|
+
if CharSequenceUtil.end_with(string, prefix, ignore_case=True):
|
|
779
|
+
return True
|
|
780
|
+
return False
|
|
781
|
+
|
|
782
|
+
# ------------------------------------------------------------------
|
|
783
|
+
# Contains
|
|
784
|
+
# ------------------------------------------------------------------
|
|
785
|
+
|
|
786
|
+
@staticmethod
|
|
787
|
+
def contains(string: Optional[str], search: Optional[str]) -> bool:
|
|
788
|
+
"""
|
|
789
|
+
判断 *string* 是否包含 *search*。
|
|
790
|
+
|
|
791
|
+
:param string: 待搜索的字符串
|
|
792
|
+
:param search: 待查找的子串
|
|
793
|
+
:return: 是否包含该子串
|
|
794
|
+
"""
|
|
795
|
+
if string is None or search is None:
|
|
796
|
+
return False
|
|
797
|
+
return search in string
|
|
798
|
+
|
|
799
|
+
@staticmethod
|
|
800
|
+
def contains_any(string: Optional[str], *test_strings: Optional[str]) -> bool:
|
|
801
|
+
"""
|
|
802
|
+
判断 *string* 是否包含给定的任一子串。
|
|
803
|
+
|
|
804
|
+
:param string: 待搜索的字符串
|
|
805
|
+
:param test_strings: 待查找的子串列表
|
|
806
|
+
:return: 是否包含任一子串
|
|
807
|
+
"""
|
|
808
|
+
return CharSequenceUtil.get_contains_str(string, *test_strings) is not None
|
|
809
|
+
|
|
810
|
+
@staticmethod
|
|
811
|
+
def contains_all(string: Optional[str], *test_strings: Optional[str]) -> bool:
|
|
812
|
+
"""
|
|
813
|
+
判断 *string* 是否包含所有给定的子串。
|
|
814
|
+
|
|
815
|
+
:param string: 待搜索的字符串
|
|
816
|
+
:param test_strings: 待查找的子串列表
|
|
817
|
+
:return: 是否包含所有子串
|
|
818
|
+
"""
|
|
819
|
+
if CharSequenceUtil.is_blank(string) or not test_strings:
|
|
820
|
+
return False
|
|
821
|
+
for test_string in test_strings:
|
|
822
|
+
if not CharSequenceUtil.contains(string, test_string):
|
|
823
|
+
return False
|
|
824
|
+
return True
|
|
825
|
+
|
|
826
|
+
@staticmethod
|
|
827
|
+
def contains_blank(string: Optional[str]) -> bool:
|
|
828
|
+
"""
|
|
829
|
+
判断 *string* 是否包含空白字符。
|
|
830
|
+
*string* 为 None 或空字符串时返回 False。
|
|
831
|
+
|
|
832
|
+
:param string: 待检查的字符串
|
|
833
|
+
:return: 是否包含空白字符
|
|
834
|
+
"""
|
|
835
|
+
if CharSequenceUtil.is_empty(string):
|
|
836
|
+
return False
|
|
837
|
+
for char in string:
|
|
838
|
+
if CharUtil.is_blank_char(char):
|
|
839
|
+
return True
|
|
840
|
+
return False
|
|
841
|
+
|
|
842
|
+
@staticmethod
|
|
843
|
+
def get_contains_str(string: Optional[str], *test_strings: Optional[str]) -> Optional[str]:
|
|
844
|
+
"""
|
|
845
|
+
查找 *test_strings* 中第一个被 *string* 包含的子串。
|
|
846
|
+
|
|
847
|
+
:param string: 待搜索的字符串
|
|
848
|
+
:param test_strings: 待查找的子串列表
|
|
849
|
+
:return: 第一个匹配的子串,或 None
|
|
850
|
+
"""
|
|
851
|
+
if CharSequenceUtil.is_empty(string) or not test_strings:
|
|
852
|
+
return None
|
|
853
|
+
for test_string in test_strings:
|
|
854
|
+
if CharSequenceUtil.contains(string, test_string):
|
|
855
|
+
return test_string
|
|
856
|
+
return None
|
|
857
|
+
|
|
858
|
+
@staticmethod
|
|
859
|
+
def contains_ignore_case(string: Optional[str], test_string: Optional[str]) -> bool:
|
|
860
|
+
"""
|
|
861
|
+
判断 *string* 是否包含 *test_string*,忽略大小写。
|
|
862
|
+
两者均为 None 时返回 True。
|
|
863
|
+
|
|
864
|
+
:param string: 待搜索的字符串
|
|
865
|
+
:param test_string: 待查找的子串
|
|
866
|
+
:return: 是否包含该子串(不区分大小写)
|
|
867
|
+
"""
|
|
868
|
+
if string is None:
|
|
869
|
+
return test_string is None
|
|
870
|
+
return CharSequenceUtil.index_of(string, test_string, ignore_case=True) > -1
|
|
871
|
+
|
|
872
|
+
@staticmethod
|
|
873
|
+
def contains_any_ignore_case(string: Optional[str], *test_strings: Optional[str]) -> bool:
|
|
874
|
+
"""
|
|
875
|
+
判断 *string* 是否包含给定的任一子串,忽略大小写。
|
|
876
|
+
|
|
877
|
+
:param string: 待搜索的字符串
|
|
878
|
+
:param test_strings: 待查找的子串列表
|
|
879
|
+
:return: 是否包含任一子串(不区分大小写)
|
|
880
|
+
"""
|
|
881
|
+
return CharSequenceUtil.get_contains_str_ignore_case(string, *test_strings) is not None
|
|
882
|
+
|
|
883
|
+
@staticmethod
|
|
884
|
+
def get_contains_str_ignore_case(string: Optional[str], *test_strings: Optional[str]) -> Optional[str]:
|
|
885
|
+
"""
|
|
886
|
+
查找 *test_strings* 中第一个被 *string* 包含的子串,忽略大小写。
|
|
887
|
+
|
|
888
|
+
:param string: 待搜索的字符串
|
|
889
|
+
:param test_strings: 待查找的子串列表
|
|
890
|
+
:return: 第一个匹配的子串,或 None
|
|
891
|
+
"""
|
|
892
|
+
if CharSequenceUtil.is_empty(string) or not test_strings:
|
|
893
|
+
return None
|
|
894
|
+
for test_string in test_strings:
|
|
895
|
+
if CharSequenceUtil.contains_ignore_case(string, test_string):
|
|
896
|
+
return test_string
|
|
897
|
+
return None
|
|
898
|
+
|
|
899
|
+
# ------------------------------------------------------------------
|
|
900
|
+
# Index of
|
|
901
|
+
# ------------------------------------------------------------------
|
|
902
|
+
|
|
903
|
+
@staticmethod
|
|
904
|
+
def index_of(
|
|
905
|
+
text: Optional[str],
|
|
906
|
+
search_string: Optional[str],
|
|
907
|
+
start: Optional[int] = None,
|
|
908
|
+
end: Optional[int] = None,
|
|
909
|
+
ignore_case: bool = False,
|
|
910
|
+
) -> int:
|
|
911
|
+
"""
|
|
912
|
+
在 *text* 的可选范围 [start, end) 内查找 *search_string* 的索引。
|
|
913
|
+
|
|
914
|
+
:param text: 待搜索的字符串
|
|
915
|
+
:param search_string: 待查找的子串
|
|
916
|
+
:param start: 可选的起始位置
|
|
917
|
+
:param end: 可选的结束位置
|
|
918
|
+
:param ignore_case: 是否忽略大小写
|
|
919
|
+
:return: 索引值,未找到时返回 -1
|
|
920
|
+
"""
|
|
921
|
+
if CharSequenceUtil.is_empty(text) or CharSequenceUtil.is_empty(search_string):
|
|
922
|
+
if CharSequenceUtil.equals(text, search_string):
|
|
923
|
+
return 0
|
|
924
|
+
return -1
|
|
925
|
+
if ignore_case:
|
|
926
|
+
text = text.lower()
|
|
927
|
+
search_string = search_string.lower()
|
|
928
|
+
return text.find(search_string, start, end)
|
|
929
|
+
|
|
930
|
+
@staticmethod
|
|
931
|
+
def index_of_ignore_case(
|
|
932
|
+
text: Optional[str],
|
|
933
|
+
search_string: Optional[str],
|
|
934
|
+
start: Optional[int] = None,
|
|
935
|
+
end: Optional[int] = None,
|
|
936
|
+
) -> int:
|
|
937
|
+
"""
|
|
938
|
+
在 *text* 中查找 *search_string* 的索引,忽略大小写。
|
|
939
|
+
|
|
940
|
+
:param text: 待搜索的字符串
|
|
941
|
+
:param search_string: 待查找的子串
|
|
942
|
+
:param start: 可选的起始位置
|
|
943
|
+
:param end: 可选的结束位置
|
|
944
|
+
:return: 索引值,未找到时返回 -1
|
|
945
|
+
"""
|
|
946
|
+
return CharSequenceUtil.index_of(text, search_string, start=start, end=end, ignore_case=True)
|
|
947
|
+
|
|
948
|
+
@staticmethod
|
|
949
|
+
def last_index_of(
|
|
950
|
+
text: Optional[str],
|
|
951
|
+
search_string: Optional[str],
|
|
952
|
+
start: Optional[int] = None,
|
|
953
|
+
end: Optional[int] = None,
|
|
954
|
+
ignore_case: bool = False,
|
|
955
|
+
) -> int:
|
|
956
|
+
"""
|
|
957
|
+
在 *text* 的可选范围 [start, end) 内查找 *search_string* 最后一次出现的索引。
|
|
958
|
+
|
|
959
|
+
:param text: 待搜索的字符串
|
|
960
|
+
:param search_string: 待查找的子串
|
|
961
|
+
:param start: 可选的起始位置
|
|
962
|
+
:param end: 可选的结束位置
|
|
963
|
+
:param ignore_case: 是否忽略大小写
|
|
964
|
+
:return: 索引值,未找到时返回 -1
|
|
965
|
+
"""
|
|
966
|
+
if CharSequenceUtil.is_empty(text) or CharSequenceUtil.is_empty(search_string):
|
|
967
|
+
if CharSequenceUtil.equals(text, search_string):
|
|
968
|
+
return 0
|
|
969
|
+
return -1
|
|
970
|
+
if ignore_case:
|
|
971
|
+
text = text.lower()
|
|
972
|
+
search_string = search_string.lower()
|
|
973
|
+
return text.rfind(search_string, start, end)
|
|
974
|
+
|
|
975
|
+
@staticmethod
|
|
976
|
+
def last_index_of_ignore_case(
|
|
977
|
+
text: Optional[str],
|
|
978
|
+
search_string: Optional[str],
|
|
979
|
+
start: Optional[int] = None,
|
|
980
|
+
end: Optional[int] = None,
|
|
981
|
+
) -> int:
|
|
982
|
+
"""
|
|
983
|
+
在 *text* 中查找 *search_string* 最后一次出现的索引,忽略大小写。
|
|
984
|
+
|
|
985
|
+
:param text: 待搜索的字符串
|
|
986
|
+
:param search_string: 待查找的子串
|
|
987
|
+
:param start: 可选的起始位置
|
|
988
|
+
:param end: 可选的结束位置
|
|
989
|
+
:return: 索引值,未找到时返回 -1
|
|
990
|
+
"""
|
|
991
|
+
return CharSequenceUtil.last_index_of(text, search_string, start, end, ignore_case=True)
|
|
992
|
+
|
|
993
|
+
@staticmethod
|
|
994
|
+
def ordinal_index_of(text: Optional[str], search_string: Optional[str], ordinal: int) -> int:
|
|
995
|
+
"""
|
|
996
|
+
返回 *search_string* 在 *text* 中第 *ordinal* 次出现的索引。
|
|
997
|
+
|
|
998
|
+
:param text: 待搜索的字符串
|
|
999
|
+
:param search_string: 待查找的子串
|
|
1000
|
+
:param ordinal: 出现次数(从1开始)
|
|
1001
|
+
:return: 索引值,未找到时返回 -1
|
|
1002
|
+
"""
|
|
1003
|
+
if text is None or search_string is None or ordinal <= 0:
|
|
1004
|
+
return -1
|
|
1005
|
+
if len(search_string) == 0:
|
|
1006
|
+
return 0
|
|
1007
|
+
found = 0
|
|
1008
|
+
index = -1
|
|
1009
|
+
for _ in range(ordinal):
|
|
1010
|
+
index = CharSequenceUtil.index_of(text, search_string, start=found)
|
|
1011
|
+
if index < 0:
|
|
1012
|
+
return index
|
|
1013
|
+
found = index + 1
|
|
1014
|
+
return index
|
|
1015
|
+
|
|
1016
|
+
# ------------------------------------------------------------------
|
|
1017
|
+
# Remove
|
|
1018
|
+
# ------------------------------------------------------------------
|
|
1019
|
+
|
|
1020
|
+
@staticmethod
|
|
1021
|
+
def remove_all(string: Optional[str], string_to_remove: Optional[str]) -> Optional[str]:
|
|
1022
|
+
"""
|
|
1023
|
+
从 *string* 中移除所有 *string_to_remove* 的出现。
|
|
1024
|
+
|
|
1025
|
+
:param string: 源字符串
|
|
1026
|
+
:param string_to_remove: 待移除的子串
|
|
1027
|
+
:return: 移除后的字符串
|
|
1028
|
+
"""
|
|
1029
|
+
if CharSequenceUtil.is_empty(string) or CharSequenceUtil.is_empty(string_to_remove):
|
|
1030
|
+
return string
|
|
1031
|
+
return string.replace(string_to_remove, CharPool.EMPTY) # type: ignore[union-attr]
|
|
1032
|
+
|
|
1033
|
+
@staticmethod
|
|
1034
|
+
def remove_any(string: Optional[str], *strings_to_remove: Optional[str]) -> Optional[str]:
|
|
1035
|
+
"""
|
|
1036
|
+
从 *string* 中移除 *strings_to_remove* 中每个子串的所有出现。
|
|
1037
|
+
|
|
1038
|
+
:param string: 源字符串
|
|
1039
|
+
:param strings_to_remove: 待移除的子串列表
|
|
1040
|
+
:return: 移除后的字符串
|
|
1041
|
+
"""
|
|
1042
|
+
result = string
|
|
1043
|
+
if CharSequenceUtil.is_not_empty(string):
|
|
1044
|
+
for string_to_remove in strings_to_remove:
|
|
1045
|
+
result = CharSequenceUtil.remove_all(result, string_to_remove)
|
|
1046
|
+
return result
|
|
1047
|
+
|
|
1048
|
+
@staticmethod
|
|
1049
|
+
def remove_all_line_breaks(string: Optional[str]) -> Optional[str]:
|
|
1050
|
+
"""
|
|
1051
|
+
从 *string* 中移除所有换行符(``\\r`` 和 ``\\n``)。
|
|
1052
|
+
|
|
1053
|
+
:param string: 源字符串
|
|
1054
|
+
:return: 不含换行符的字符串
|
|
1055
|
+
"""
|
|
1056
|
+
return CharSequenceUtil.remove_any(string, CharPool.CR, CharPool.LF)
|
|
1057
|
+
|
|
1058
|
+
@staticmethod
|
|
1059
|
+
def remove_pre_and_lower_first(string: Optional[str], pre_or_length: Union[int, str]) -> Optional[str]:
|
|
1060
|
+
"""
|
|
1061
|
+
从 *string* 中移除前缀,并将剩余字符串的首字母小写。
|
|
1062
|
+
|
|
1063
|
+
:param string: 源字符串
|
|
1064
|
+
:param pre_or_length: 要移除的前缀字符串,或从头部移除的字符数
|
|
1065
|
+
:return: 处理后的字符串,*string* 为 None 时返回 None
|
|
1066
|
+
"""
|
|
1067
|
+
if isinstance(pre_or_length, str):
|
|
1068
|
+
return CharSequenceUtil.lower_first(CharSequenceUtil.remove_prefix(string, pre_or_length))
|
|
1069
|
+
if string is None:
|
|
1070
|
+
return None
|
|
1071
|
+
if len(string) > pre_or_length:
|
|
1072
|
+
first = string[pre_or_length].lower()
|
|
1073
|
+
if len(string) > pre_or_length + 1:
|
|
1074
|
+
start_index = pre_or_length + 1
|
|
1075
|
+
return first + string[start_index:]
|
|
1076
|
+
return first
|
|
1077
|
+
return string
|
|
1078
|
+
|
|
1079
|
+
@staticmethod
|
|
1080
|
+
def remove_prefix(string: Optional[str], prefix: Optional[str]) -> Optional[str]:
|
|
1081
|
+
"""
|
|
1082
|
+
如果 *string* 以 *prefix* 开头则移除该前缀。
|
|
1083
|
+
|
|
1084
|
+
:param string: 源字符串
|
|
1085
|
+
:param prefix: 待移除的前缀
|
|
1086
|
+
:return: 移除前缀后的字符串
|
|
1087
|
+
"""
|
|
1088
|
+
if CharSequenceUtil.is_empty(string) or CharSequenceUtil.is_empty(prefix):
|
|
1089
|
+
return string
|
|
1090
|
+
if CharSequenceUtil.start_with(string, prefix):
|
|
1091
|
+
return CharSequenceUtil.sub_suf(string, len(prefix)) # type: ignore[arg-type]
|
|
1092
|
+
return string
|
|
1093
|
+
|
|
1094
|
+
@staticmethod
|
|
1095
|
+
def remove_prefix_ignore_case(string: Optional[str], prefix: Optional[str]) -> Optional[str]:
|
|
1096
|
+
"""
|
|
1097
|
+
如果 *string* 以 *prefix* 开头则移除该前缀(忽略大小写)。
|
|
1098
|
+
|
|
1099
|
+
:param string: 源字符串
|
|
1100
|
+
:param prefix: 待移除的前缀
|
|
1101
|
+
:return: 移除前缀后的字符串
|
|
1102
|
+
"""
|
|
1103
|
+
if CharSequenceUtil.is_empty(string) or CharSequenceUtil.is_empty(prefix):
|
|
1104
|
+
return string
|
|
1105
|
+
if CharSequenceUtil.start_with_ignore_case(string, prefix):
|
|
1106
|
+
return CharSequenceUtil.sub_suf(string, len(prefix)) # type: ignore[arg-type]
|
|
1107
|
+
return string
|
|
1108
|
+
|
|
1109
|
+
@staticmethod
|
|
1110
|
+
def remove_suffix(string: Optional[str], suffix: Optional[str]) -> Optional[str]:
|
|
1111
|
+
"""
|
|
1112
|
+
如果 *string* 以 *suffix* 结尾则移除该后缀。
|
|
1113
|
+
|
|
1114
|
+
:param string: 源字符串
|
|
1115
|
+
:param suffix: 待移除的后缀
|
|
1116
|
+
:return: 移除后缀后的字符串
|
|
1117
|
+
"""
|
|
1118
|
+
if CharSequenceUtil.is_empty(string) or CharSequenceUtil.is_empty(suffix):
|
|
1119
|
+
return string
|
|
1120
|
+
if CharSequenceUtil.end_with(string, suffix):
|
|
1121
|
+
return CharSequenceUtil.sub_pre(string, len(suffix)) # type: ignore[arg-type]
|
|
1122
|
+
return string
|
|
1123
|
+
|
|
1124
|
+
@staticmethod
|
|
1125
|
+
def remove_suffix_and_lower_first(string: Optional[str], suffix: Optional[str]) -> Optional[str]:
|
|
1126
|
+
"""
|
|
1127
|
+
从 *string* 中移除后缀并将首字母小写。
|
|
1128
|
+
|
|
1129
|
+
:param string: 源字符串
|
|
1130
|
+
:param suffix: 待移除的后缀
|
|
1131
|
+
:return: 处理后的字符串
|
|
1132
|
+
"""
|
|
1133
|
+
return CharSequenceUtil.lower_first(CharSequenceUtil.remove_suffix(string, suffix))
|
|
1134
|
+
|
|
1135
|
+
@staticmethod
|
|
1136
|
+
def remove_suffix_ignore_case(string: Optional[str], suffix: Optional[str]) -> Optional[str]:
|
|
1137
|
+
"""
|
|
1138
|
+
如果 *string* 以 *suffix* 结尾则移除该后缀(忽略大小写)。
|
|
1139
|
+
|
|
1140
|
+
:param string: 源字符串
|
|
1141
|
+
:param suffix: 待移除的后缀
|
|
1142
|
+
:return: 移除后缀后的字符串
|
|
1143
|
+
"""
|
|
1144
|
+
if CharSequenceUtil.is_empty(string) or CharSequenceUtil.is_empty(suffix):
|
|
1145
|
+
return string
|
|
1146
|
+
if CharSequenceUtil.end_with_ignore_case(string, suffix):
|
|
1147
|
+
return CharSequenceUtil.sub_pre(string, len(suffix)) # type: ignore[arg-type]
|
|
1148
|
+
return string
|
|
1149
|
+
|
|
1150
|
+
# ------------------------------------------------------------------
|
|
1151
|
+
# Clean / strip
|
|
1152
|
+
# ------------------------------------------------------------------
|
|
1153
|
+
|
|
1154
|
+
@staticmethod
|
|
1155
|
+
def clean_blank(string: Optional[str]) -> Optional[str]:
|
|
1156
|
+
"""
|
|
1157
|
+
移除 *string* 中的所有空白字符。
|
|
1158
|
+
|
|
1159
|
+
:param string: 源字符串
|
|
1160
|
+
:return: 不含空白字符的字符串
|
|
1161
|
+
"""
|
|
1162
|
+
return CharSequenceUtil.filter(string, lambda char: not CharUtil.is_blank_char(char))
|
|
1163
|
+
|
|
1164
|
+
@staticmethod
|
|
1165
|
+
def strip(string: Optional[str], prefix: str, suffix: Optional[str] = None) -> Optional[str]:
|
|
1166
|
+
"""
|
|
1167
|
+
从 *string* 头部去除 *prefix*,尾部去除 *suffix*。
|
|
1168
|
+
若 *suffix* 为 None,则两端均去除 *prefix*。
|
|
1169
|
+
|
|
1170
|
+
:param string: 源字符串
|
|
1171
|
+
:param prefix: 待去除的前缀
|
|
1172
|
+
:param suffix: 待去除的后缀(默认为 *prefix*)
|
|
1173
|
+
:return: 去除后的字符串
|
|
1174
|
+
"""
|
|
1175
|
+
if suffix is None:
|
|
1176
|
+
if CharSequenceUtil.equals(string, prefix):
|
|
1177
|
+
return CharPool.EMPTY
|
|
1178
|
+
suffix = prefix
|
|
1179
|
+
|
|
1180
|
+
if CharSequenceUtil.is_empty(string):
|
|
1181
|
+
return string
|
|
1182
|
+
from_index = 0
|
|
1183
|
+
to_index = len(string)
|
|
1184
|
+
if CharSequenceUtil.start_with(string, prefix):
|
|
1185
|
+
from_index = len(prefix)
|
|
1186
|
+
if CharSequenceUtil.end_with(string, suffix):
|
|
1187
|
+
to_index -= len(suffix)
|
|
1188
|
+
return string[from_index:to_index]
|
|
1189
|
+
|
|
1190
|
+
@staticmethod
|
|
1191
|
+
def strip_ignore_case(string: Optional[str], prefix: str, suffix: Optional[str] = None) -> Optional[str]:
|
|
1192
|
+
"""
|
|
1193
|
+
从 *string* 头部去除 *prefix*,尾部去除 *suffix*,忽略大小写。
|
|
1194
|
+
|
|
1195
|
+
:param string: 源字符串
|
|
1196
|
+
:param prefix: 待去除的前缀
|
|
1197
|
+
:param suffix: 待去除的后缀(默认为 *prefix*)
|
|
1198
|
+
:return: 去除后的字符串
|
|
1199
|
+
"""
|
|
1200
|
+
if suffix is None:
|
|
1201
|
+
if CharSequenceUtil.equals_ignore_case(string, prefix):
|
|
1202
|
+
return CharPool.EMPTY
|
|
1203
|
+
suffix = prefix
|
|
1204
|
+
|
|
1205
|
+
if CharSequenceUtil.is_empty(string):
|
|
1206
|
+
return string
|
|
1207
|
+
from_index = 0
|
|
1208
|
+
to_index = len(string)
|
|
1209
|
+
if CharSequenceUtil.start_with_ignore_case(string, prefix):
|
|
1210
|
+
from_index = len(prefix)
|
|
1211
|
+
if CharSequenceUtil.end_with_ignore_case(string, suffix):
|
|
1212
|
+
to_index -= len(suffix)
|
|
1213
|
+
return string[from_index:to_index]
|
|
1214
|
+
|
|
1215
|
+
# ------------------------------------------------------------------
|
|
1216
|
+
# Add prefix / suffix if missing
|
|
1217
|
+
# ------------------------------------------------------------------
|
|
1218
|
+
|
|
1219
|
+
@staticmethod
|
|
1220
|
+
def add_prefix_if_not(string: Optional[str], prefix: Optional[str]) -> Optional[str]:
|
|
1221
|
+
"""
|
|
1222
|
+
如果 *string* 不以 *prefix* 开头则添加该前缀。
|
|
1223
|
+
|
|
1224
|
+
:param string: 源字符串
|
|
1225
|
+
:param prefix: 待添加的前缀
|
|
1226
|
+
:return: 确保有前缀的字符串
|
|
1227
|
+
"""
|
|
1228
|
+
return CharSequenceUtil.prepend_if_missing(string, prefix, prefix)
|
|
1229
|
+
|
|
1230
|
+
@staticmethod
|
|
1231
|
+
def add_suffix_if_not(string: Optional[str], suffix: Optional[str]) -> Optional[str]:
|
|
1232
|
+
"""
|
|
1233
|
+
如果 *string* 不以 *suffix* 结尾则添加该后缀。
|
|
1234
|
+
|
|
1235
|
+
:param string: 源字符串
|
|
1236
|
+
:param suffix: 待添加的后缀
|
|
1237
|
+
:return: 确保有后缀的字符串
|
|
1238
|
+
"""
|
|
1239
|
+
return CharSequenceUtil.append_if_missing(string, suffix, suffix)
|
|
1240
|
+
|
|
1241
|
+
# ------------------------------------------------------------------
|
|
1242
|
+
# Cut
|
|
1243
|
+
# ------------------------------------------------------------------
|
|
1244
|
+
|
|
1245
|
+
@staticmethod
|
|
1246
|
+
def cut(string: Optional[str], part_length: int) -> List[str]:
|
|
1247
|
+
"""
|
|
1248
|
+
将 *string* 按 *part_length* 个字符一组进行分割。
|
|
1249
|
+
|
|
1250
|
+
:param string: 源字符串
|
|
1251
|
+
:param part_length: 每组的长度
|
|
1252
|
+
:return: 分割后的字符串列表
|
|
1253
|
+
"""
|
|
1254
|
+
if string is None:
|
|
1255
|
+
return []
|
|
1256
|
+
length = len(string)
|
|
1257
|
+
if length < part_length:
|
|
1258
|
+
return [string]
|
|
1259
|
+
part = (length // part_length) if length % part_length == 0 else (length // part_length + 1)
|
|
1260
|
+
result: List[str] = []
|
|
1261
|
+
for i in range(part):
|
|
1262
|
+
start = i * part_length
|
|
1263
|
+
end = length if i == part - 1 else (part_length + i * part_length)
|
|
1264
|
+
result.append(string[start:end])
|
|
1265
|
+
return result
|
|
1266
|
+
|
|
1267
|
+
# ------------------------------------------------------------------
|
|
1268
|
+
# Equals
|
|
1269
|
+
# ------------------------------------------------------------------
|
|
1270
|
+
|
|
1271
|
+
@staticmethod
|
|
1272
|
+
def equals(string1: Optional[str], string2: Optional[str], ignore_case: bool = False) -> bool:
|
|
1273
|
+
"""
|
|
1274
|
+
比较两个字符串是否相等。
|
|
1275
|
+
|
|
1276
|
+
:param string1: 第一个字符串
|
|
1277
|
+
:param string2: 第二个字符串
|
|
1278
|
+
:param ignore_case: 是否忽略大小写
|
|
1279
|
+
:return: 两个字符串是否相等(或均为 None)
|
|
1280
|
+
"""
|
|
1281
|
+
if string1 is None:
|
|
1282
|
+
return string2 is None
|
|
1283
|
+
if string2 is None:
|
|
1284
|
+
return False
|
|
1285
|
+
if ignore_case:
|
|
1286
|
+
return string1.lower() == string2.lower()
|
|
1287
|
+
return string1 == string2
|
|
1288
|
+
|
|
1289
|
+
@staticmethod
|
|
1290
|
+
def equals_ignore_case(string1: Optional[str], string2: Optional[str]) -> bool:
|
|
1291
|
+
"""
|
|
1292
|
+
比较两个字符串是否相等,忽略大小写。
|
|
1293
|
+
|
|
1294
|
+
:param string1: 第一个字符串
|
|
1295
|
+
:param string2: 第二个字符串
|
|
1296
|
+
:return: 两个字符串是否相等(不区分大小写)
|
|
1297
|
+
"""
|
|
1298
|
+
return CharSequenceUtil.equals(string1, string2, ignore_case=True)
|
|
1299
|
+
|
|
1300
|
+
# ------------------------------------------------------------------
|
|
1301
|
+
# Append / prepend if missing
|
|
1302
|
+
# ------------------------------------------------------------------
|
|
1303
|
+
|
|
1304
|
+
@staticmethod
|
|
1305
|
+
def append_if_missing(
|
|
1306
|
+
string: Optional[str], suffix: Optional[str], *suffixes: Optional[str], ignore_case: bool = False
|
|
1307
|
+
) -> Optional[str]:
|
|
1308
|
+
"""
|
|
1309
|
+
如果 *string* 不以指定后缀结尾,则追加 *suffix*。
|
|
1310
|
+
|
|
1311
|
+
:param string: 源字符串
|
|
1312
|
+
:param suffix: 待追加的后缀
|
|
1313
|
+
:param suffixes: 额外的待检查后缀
|
|
1314
|
+
:param ignore_case: 检查时是否忽略大小写
|
|
1315
|
+
:return: 确保有后缀的字符串
|
|
1316
|
+
"""
|
|
1317
|
+
if (
|
|
1318
|
+
string is None
|
|
1319
|
+
or CharSequenceUtil.is_empty(suffix)
|
|
1320
|
+
or CharSequenceUtil.end_with(string, suffix, ignore_case=ignore_case)
|
|
1321
|
+
):
|
|
1322
|
+
return string
|
|
1323
|
+
if suffixes:
|
|
1324
|
+
for s in suffixes:
|
|
1325
|
+
if CharSequenceUtil.end_with(string, s, ignore_case):
|
|
1326
|
+
return string
|
|
1327
|
+
return string + suffix # type: ignore[operator]
|
|
1328
|
+
|
|
1329
|
+
@staticmethod
|
|
1330
|
+
def append_if_missing_ignore_case(
|
|
1331
|
+
string: Optional[str], suffix: Optional[str], *suffixes: Optional[str]
|
|
1332
|
+
) -> Optional[str]:
|
|
1333
|
+
"""
|
|
1334
|
+
如果 *string* 不以 *suffix* 结尾则追加(忽略大小写)。
|
|
1335
|
+
|
|
1336
|
+
:param string: 源字符串
|
|
1337
|
+
:param suffix: 待追加的后缀
|
|
1338
|
+
:param suffixes: 额外的待检查后缀
|
|
1339
|
+
:return: 确保有后缀的字符串
|
|
1340
|
+
"""
|
|
1341
|
+
return CharSequenceUtil.append_if_missing(string, suffix, *suffixes, ignore_case=True)
|
|
1342
|
+
|
|
1343
|
+
@staticmethod
|
|
1344
|
+
def prepend_if_missing(
|
|
1345
|
+
string: Optional[str], prefix: Optional[str], *prefixes: Optional[str], ignore_case: bool = False
|
|
1346
|
+
) -> Optional[str]:
|
|
1347
|
+
"""
|
|
1348
|
+
如果 *string* 不以指定前缀开头,则前置 *prefix*。
|
|
1349
|
+
|
|
1350
|
+
:param string: 源字符串
|
|
1351
|
+
:param prefix: 待前置的前缀
|
|
1352
|
+
:param prefixes: 额外的待检查前缀
|
|
1353
|
+
:param ignore_case: 检查时是否忽略大小写
|
|
1354
|
+
:return: 确保有前缀的字符串
|
|
1355
|
+
"""
|
|
1356
|
+
if (
|
|
1357
|
+
string is None
|
|
1358
|
+
or CharSequenceUtil.is_empty(prefix)
|
|
1359
|
+
or CharSequenceUtil.start_with(string, prefix, ignore_case=ignore_case)
|
|
1360
|
+
):
|
|
1361
|
+
return string
|
|
1362
|
+
if prefixes:
|
|
1363
|
+
for p in prefixes:
|
|
1364
|
+
if CharSequenceUtil.start_with(string, p, ignore_case):
|
|
1365
|
+
return string
|
|
1366
|
+
return prefix + string # type: ignore[operator]
|
|
1367
|
+
|
|
1368
|
+
@staticmethod
|
|
1369
|
+
def prepend_if_missing_ignore_case(
|
|
1370
|
+
string: Optional[str], prefix: Optional[str], *prefixes: Optional[str]
|
|
1371
|
+
) -> Optional[str]:
|
|
1372
|
+
"""
|
|
1373
|
+
如果 *string* 不以 *prefix* 开头则前置(忽略大小写)。
|
|
1374
|
+
|
|
1375
|
+
:param string: 源字符串
|
|
1376
|
+
:param prefix: 待前置的前缀
|
|
1377
|
+
:param prefixes: 额外的待检查前缀
|
|
1378
|
+
:return: 确保有前缀的字符串
|
|
1379
|
+
"""
|
|
1380
|
+
return CharSequenceUtil.prepend_if_missing(string, prefix, *prefixes, ignore_case=True)
|
|
1381
|
+
|
|
1382
|
+
# ------------------------------------------------------------------
|
|
1383
|
+
# Upper / lower first
|
|
1384
|
+
# ------------------------------------------------------------------
|
|
1385
|
+
|
|
1386
|
+
@staticmethod
|
|
1387
|
+
def upper_first_and_add_pre(string: Optional[str], pre_string: Optional[str]) -> Optional[str]:
|
|
1388
|
+
"""
|
|
1389
|
+
将 *string* 首字母大写并前置 *pre_string*。
|
|
1390
|
+
|
|
1391
|
+
:param string: 源字符串
|
|
1392
|
+
:param pre_string: 待前置的前缀
|
|
1393
|
+
:return: 处理后的字符串,任一参数为 None 时返回 None
|
|
1394
|
+
"""
|
|
1395
|
+
if string is None or pre_string is None:
|
|
1396
|
+
return None
|
|
1397
|
+
return pre_string + CharSequenceUtil.upper_first(string) # type: ignore[operator]
|
|
1398
|
+
|
|
1399
|
+
@staticmethod
|
|
1400
|
+
def upper_first(string: Optional[str]) -> Optional[str]:
|
|
1401
|
+
"""
|
|
1402
|
+
将 *string* 的首字母大写。
|
|
1403
|
+
|
|
1404
|
+
:param string: 源字符串
|
|
1405
|
+
:return: 首字母大写后的字符串
|
|
1406
|
+
"""
|
|
1407
|
+
if string is None:
|
|
1408
|
+
return None
|
|
1409
|
+
if len(string) > 0:
|
|
1410
|
+
first_char = string[0]
|
|
1411
|
+
if CharUtil.is_letter_lower(first_char):
|
|
1412
|
+
return first_char.upper() + CharSequenceUtil.sub_suf(string, 1)
|
|
1413
|
+
return string
|
|
1414
|
+
|
|
1415
|
+
@staticmethod
|
|
1416
|
+
def lower_first(string: Optional[str]) -> Optional[str]:
|
|
1417
|
+
"""
|
|
1418
|
+
将 *string* 的首字母小写。
|
|
1419
|
+
|
|
1420
|
+
:param string: 源字符串
|
|
1421
|
+
:return: 首字母小写后的字符串
|
|
1422
|
+
"""
|
|
1423
|
+
if string is None:
|
|
1424
|
+
return None
|
|
1425
|
+
if len(string) > 0:
|
|
1426
|
+
first_char = string[0]
|
|
1427
|
+
if CharUtil.is_letter_upper(first_char):
|
|
1428
|
+
return first_char.lower() + CharSequenceUtil.sub_suf(string, 1)
|
|
1429
|
+
return string
|
|
1430
|
+
|
|
1431
|
+
# ------------------------------------------------------------------
|
|
1432
|
+
# Filter
|
|
1433
|
+
# ------------------------------------------------------------------
|
|
1434
|
+
|
|
1435
|
+
@staticmethod
|
|
1436
|
+
def filter(string: Optional[str], filter_func: Callable[[str], bool]) -> Optional[str]:
|
|
1437
|
+
"""
|
|
1438
|
+
使用 *filter_func* 过滤 *string* 中的字符。仅保留
|
|
1439
|
+
*filter_func* 返回 True 的字符。
|
|
1440
|
+
|
|
1441
|
+
:param string: 源字符串
|
|
1442
|
+
:param filter_func: 过滤函数
|
|
1443
|
+
:return: 过滤后的字符串
|
|
1444
|
+
"""
|
|
1445
|
+
if string is None or filter_func is None:
|
|
1446
|
+
return string
|
|
1447
|
+
return "".join([char for char in string if filter_func(char)])
|
|
1448
|
+
|
|
1449
|
+
# ------------------------------------------------------------------
|
|
1450
|
+
# Substring helpers (used internally)
|
|
1451
|
+
# ------------------------------------------------------------------
|
|
1452
|
+
|
|
1453
|
+
@staticmethod
|
|
1454
|
+
def sub_suf(string: Optional[str], from_index: int) -> Optional[str]:
|
|
1455
|
+
"""
|
|
1456
|
+
返回从 *from_index* 到末尾的子串。
|
|
1457
|
+
|
|
1458
|
+
:param string: 源字符串
|
|
1459
|
+
:param from_index: 起始索引(包含)
|
|
1460
|
+
:return: 子串,*string* 为 None 时返回 None
|
|
1461
|
+
"""
|
|
1462
|
+
if string is None:
|
|
1463
|
+
return None
|
|
1464
|
+
if from_index < 0:
|
|
1465
|
+
from_index = 0
|
|
1466
|
+
if from_index >= len(string):
|
|
1467
|
+
return ""
|
|
1468
|
+
return string[from_index:]
|
|
1469
|
+
|
|
1470
|
+
@staticmethod
|
|
1471
|
+
def sub_pre(string: Optional[str], to_index: int) -> Optional[str]:
|
|
1472
|
+
"""
|
|
1473
|
+
返回从开头到末尾减去 *to_index* 个字符处的子串。
|
|
1474
|
+
|
|
1475
|
+
:param string: 源字符串
|
|
1476
|
+
:param to_index: 从末尾排除的字符数
|
|
1477
|
+
:return: 子串,*string* 为 None 时返回 None
|
|
1478
|
+
"""
|
|
1479
|
+
if string is None:
|
|
1480
|
+
return None
|
|
1481
|
+
length = len(string)
|
|
1482
|
+
end = length - to_index
|
|
1483
|
+
if end <= 0:
|
|
1484
|
+
return ""
|
|
1485
|
+
return string[:end]
|
|
1486
|
+
|
|
1487
|
+
# ======================================================================
|
|
1488
|
+
# NEW METHODS ported from Java Hutool
|
|
1489
|
+
# ======================================================================
|
|
1490
|
+
|
|
1491
|
+
# ------------------------------------------------------------------
|
|
1492
|
+
# Substring operations
|
|
1493
|
+
# ------------------------------------------------------------------
|
|
1494
|
+
|
|
1495
|
+
@staticmethod
|
|
1496
|
+
def sub(string: Optional[str], start: int, end: Optional[int] = None) -> Optional[str]:
|
|
1497
|
+
"""
|
|
1498
|
+
提取子串,支持从末尾开始计数的负索引(类似 Python 切片)。
|
|
1499
|
+
|
|
1500
|
+
:param string: 源字符串
|
|
1501
|
+
:param start: 起始索引(包含)。负值表示从末尾开始计数。
|
|
1502
|
+
:param end: 结束索引(不包含)。负值表示从末尾开始计数。
|
|
1503
|
+
为 None 时返回到字符串末尾。
|
|
1504
|
+
:return: 子串,*string* 为 None 时返回 None
|
|
1505
|
+
"""
|
|
1506
|
+
if string is None:
|
|
1507
|
+
return None
|
|
1508
|
+
length = len(string)
|
|
1509
|
+
if start < 0:
|
|
1510
|
+
start += length
|
|
1511
|
+
if start < 0:
|
|
1512
|
+
start = 0
|
|
1513
|
+
if end is None:
|
|
1514
|
+
end = length
|
|
1515
|
+
if end < 0:
|
|
1516
|
+
end += length
|
|
1517
|
+
if end < 0:
|
|
1518
|
+
end = 0
|
|
1519
|
+
if start >= end:
|
|
1520
|
+
return ""
|
|
1521
|
+
return string[start:end]
|
|
1522
|
+
|
|
1523
|
+
@staticmethod
|
|
1524
|
+
def sub_before(string: Optional[str], separator: str, is_last: bool = False) -> Optional[str]:
|
|
1525
|
+
"""
|
|
1526
|
+
返回第一个(或最后一个)*separator* 出现之前的子串。
|
|
1527
|
+
|
|
1528
|
+
未找到 *separator* 时返回整个字符串。
|
|
1529
|
+
|
|
1530
|
+
:param string: 源字符串
|
|
1531
|
+
:param separator: 分隔符字符串
|
|
1532
|
+
:param is_last: 是否使用最后一个分隔符
|
|
1533
|
+
:return: 分隔符之前的子串
|
|
1534
|
+
"""
|
|
1535
|
+
if CharSequenceUtil.is_empty(string):
|
|
1536
|
+
return string
|
|
1537
|
+
if CharSequenceUtil.is_empty(separator):
|
|
1538
|
+
return string
|
|
1539
|
+
if is_last:
|
|
1540
|
+
pos = string.rfind(separator) # type: ignore[union-attr]
|
|
1541
|
+
else:
|
|
1542
|
+
pos = string.find(separator) # type: ignore[union-attr]
|
|
1543
|
+
if pos < 0:
|
|
1544
|
+
return string
|
|
1545
|
+
return string[:pos] # type: ignore[index]
|
|
1546
|
+
|
|
1547
|
+
@staticmethod
|
|
1548
|
+
def sub_after(string: Optional[str], separator: str, is_last: bool = False) -> Optional[str]:
|
|
1549
|
+
"""
|
|
1550
|
+
返回第一个(或最后一个)*separator* 出现之后的子串。
|
|
1551
|
+
|
|
1552
|
+
未找到 *separator* 时返回整个字符串。
|
|
1553
|
+
|
|
1554
|
+
:param string: 源字符串
|
|
1555
|
+
:param separator: 分隔符字符串
|
|
1556
|
+
:param is_last: 是否使用最后一个分隔符
|
|
1557
|
+
:return: 分隔符之后的子串
|
|
1558
|
+
"""
|
|
1559
|
+
if CharSequenceUtil.is_empty(string):
|
|
1560
|
+
return string
|
|
1561
|
+
if CharSequenceUtil.is_empty(separator):
|
|
1562
|
+
return string
|
|
1563
|
+
if is_last:
|
|
1564
|
+
pos = string.rfind(separator) # type: ignore[union-attr]
|
|
1565
|
+
else:
|
|
1566
|
+
pos = string.find(separator) # type: ignore[union-attr]
|
|
1567
|
+
if pos < 0:
|
|
1568
|
+
return string
|
|
1569
|
+
return string[pos + len(separator) :] # type: ignore[index]
|
|
1570
|
+
|
|
1571
|
+
@staticmethod
|
|
1572
|
+
def sub_between(string: Optional[str], prefix: str, suffix: str) -> Optional[str]:
|
|
1573
|
+
"""
|
|
1574
|
+
返回 *string* 中第一个 *prefix* 之后、下一个 *suffix* 之前的文本。
|
|
1575
|
+
|
|
1576
|
+
*string* 为 None/空,或找不到 *prefix*/*suffix* 时返回 None。
|
|
1577
|
+
|
|
1578
|
+
:param string: 源字符串
|
|
1579
|
+
:param prefix: 前标记
|
|
1580
|
+
:param suffix: 后标记
|
|
1581
|
+
:return: *prefix* 和 *suffix* 之间的文本,或 None
|
|
1582
|
+
"""
|
|
1583
|
+
if CharSequenceUtil.is_empty(string) or CharSequenceUtil.is_empty(prefix) or CharSequenceUtil.is_empty(suffix):
|
|
1584
|
+
return None
|
|
1585
|
+
|
|
1586
|
+
start = string.find(prefix) # type: ignore[union-attr]
|
|
1587
|
+
if start < 0:
|
|
1588
|
+
return None
|
|
1589
|
+
start += len(prefix)
|
|
1590
|
+
end = string.find(suffix, start) # type: ignore[union-attr]
|
|
1591
|
+
if end < 0:
|
|
1592
|
+
return None
|
|
1593
|
+
return string[start:end] # type: ignore[index]
|
|
1594
|
+
|
|
1595
|
+
@staticmethod
|
|
1596
|
+
def sub_between_all(string: Optional[str], prefix: str, suffix: str) -> List[str]:
|
|
1597
|
+
"""
|
|
1598
|
+
返回 *string* 中所有 *prefix* 和 *suffix* 之间的不重叠子串。
|
|
1599
|
+
|
|
1600
|
+
:param string: 源字符串
|
|
1601
|
+
:param prefix: 前标记
|
|
1602
|
+
:param suffix: 后标记
|
|
1603
|
+
:return: *prefix* 和 *suffix* 之间的子串列表
|
|
1604
|
+
"""
|
|
1605
|
+
if CharSequenceUtil.is_empty(string) or CharSequenceUtil.is_empty(prefix) or CharSequenceUtil.is_empty(suffix):
|
|
1606
|
+
return []
|
|
1607
|
+
|
|
1608
|
+
result: List[str] = []
|
|
1609
|
+
remaining = string
|
|
1610
|
+
while True:
|
|
1611
|
+
piece = CharSequenceUtil.sub_between(remaining, prefix, suffix)
|
|
1612
|
+
if piece is None:
|
|
1613
|
+
break
|
|
1614
|
+
result.append(piece)
|
|
1615
|
+
# advance past this match
|
|
1616
|
+
idx = remaining.find(prefix + piece + suffix) # type: ignore[union-attr]
|
|
1617
|
+
if idx < 0:
|
|
1618
|
+
break
|
|
1619
|
+
remaining = remaining[idx + len(prefix) + len(piece) + len(suffix) :] # type: ignore[index]
|
|
1620
|
+
return result
|
|
1621
|
+
|
|
1622
|
+
# ------------------------------------------------------------------
|
|
1623
|
+
# Count
|
|
1624
|
+
# ------------------------------------------------------------------
|
|
1625
|
+
|
|
1626
|
+
@staticmethod
|
|
1627
|
+
def count(
|
|
1628
|
+
string: Optional[str],
|
|
1629
|
+
char_or_start: str,
|
|
1630
|
+
end: Optional[str] = None,
|
|
1631
|
+
) -> int:
|
|
1632
|
+
"""
|
|
1633
|
+
统计字符或子串的出现次数。
|
|
1634
|
+
|
|
1635
|
+
两种调用方式(与 Java Hutool 对齐):
|
|
1636
|
+
|
|
1637
|
+
- ``count(str, char)`` -- 统计 *char* 在 *str* 中出现的次数。
|
|
1638
|
+
- ``count(str, start, end)`` -- 统计 *str* 中 *start* 和 *end* 标记之间的子串数量。
|
|
1639
|
+
|
|
1640
|
+
:param string: 源字符串
|
|
1641
|
+
:param char_or_start: 待统计的字符/子串,或当提供 *end* 时作为起始标记
|
|
1642
|
+
:param end: 结束标记。提供时 *char_or_start* 视为起始标记
|
|
1643
|
+
:return: 统计次数
|
|
1644
|
+
"""
|
|
1645
|
+
if CharSequenceUtil.is_empty(string):
|
|
1646
|
+
return 0
|
|
1647
|
+
if end is not None:
|
|
1648
|
+
# count between markers
|
|
1649
|
+
count_val = 0
|
|
1650
|
+
remaining = string
|
|
1651
|
+
while True:
|
|
1652
|
+
sub_piece = CharSequenceUtil.sub_between(remaining, char_or_start, end)
|
|
1653
|
+
if sub_piece is None:
|
|
1654
|
+
break
|
|
1655
|
+
count_val += 1
|
|
1656
|
+
idx = remaining.find(char_or_start + sub_piece + end) # type: ignore[union-attr]
|
|
1657
|
+
if idx < 0:
|
|
1658
|
+
break
|
|
1659
|
+
remaining = remaining[idx + len(char_or_start) + len(sub_piece) + len(end) :] # type: ignore[index]
|
|
1660
|
+
return count_val
|
|
1661
|
+
return string.count(char_or_start) # type: ignore[union-attr]
|
|
1662
|
+
|
|
1663
|
+
# ------------------------------------------------------------------
|
|
1664
|
+
# Replace
|
|
1665
|
+
# ------------------------------------------------------------------
|
|
1666
|
+
|
|
1667
|
+
@staticmethod
|
|
1668
|
+
def replace(string: Optional[str], search_str: str, replacement: str, count: int = -1) -> Optional[str]:
|
|
1669
|
+
"""
|
|
1670
|
+
将 *string* 中的 *search_str* 替换为 *replacement*。
|
|
1671
|
+
|
|
1672
|
+
:param string: 源字符串
|
|
1673
|
+
:param search_str: 待查找的字符串
|
|
1674
|
+
:param replacement: 替换字符串
|
|
1675
|
+
:param count: 最大替换次数。``-1`` 表示全部替换(默认)
|
|
1676
|
+
:return: 替换后的字符串
|
|
1677
|
+
"""
|
|
1678
|
+
if CharSequenceUtil.is_empty(string) or CharSequenceUtil.is_empty(search_str):
|
|
1679
|
+
return string
|
|
1680
|
+
if count == -1:
|
|
1681
|
+
return string.replace(search_str, replacement) # type: ignore[union-attr]
|
|
1682
|
+
return string.replace(search_str, replacement, count) # type: ignore[union-attr]
|
|
1683
|
+
|
|
1684
|
+
@staticmethod
|
|
1685
|
+
def replace_chars(string: Optional[str], chars: str, replacement: str) -> Optional[str]:
|
|
1686
|
+
"""
|
|
1687
|
+
将 *string* 中出现在 *chars* 里的每个字符替换为 *replacement*。
|
|
1688
|
+
|
|
1689
|
+
:param string: 源字符串
|
|
1690
|
+
:param chars: 包含待替换字符的字符串
|
|
1691
|
+
:param replacement: 每个匹配字符的替换字符串
|
|
1692
|
+
:return: 替换后的字符串
|
|
1693
|
+
"""
|
|
1694
|
+
if CharSequenceUtil.is_empty(string):
|
|
1695
|
+
return string
|
|
1696
|
+
result = string
|
|
1697
|
+
for ch in chars:
|
|
1698
|
+
result = result.replace(ch, replacement) # type: ignore[union-attr]
|
|
1699
|
+
return result
|
|
1700
|
+
|
|
1701
|
+
@staticmethod
|
|
1702
|
+
def replace_first(string: Optional[str], regex: str, replacement: str) -> Optional[str]:
|
|
1703
|
+
"""
|
|
1704
|
+
替换 *string* 中 *regex* 的第一个匹配项为 *replacement*。
|
|
1705
|
+
|
|
1706
|
+
:param string: 源字符串
|
|
1707
|
+
:param regex: 正则表达式模式
|
|
1708
|
+
:param replacement: 替换字符串(可包含反向引用如 ``\\1``)
|
|
1709
|
+
:return: 替换第一个正则匹配后的字符串
|
|
1710
|
+
"""
|
|
1711
|
+
if CharSequenceUtil.is_empty(string):
|
|
1712
|
+
return string
|
|
1713
|
+
return re.sub(regex, replacement, string, count=1) # type: ignore[arg-type]
|
|
1714
|
+
|
|
1715
|
+
@staticmethod
|
|
1716
|
+
def replace_all(string: Optional[str], regex: str, replacement: str) -> Optional[str]:
|
|
1717
|
+
"""
|
|
1718
|
+
替换 *string* 中 *regex* 的所有匹配项为 *replacement*。
|
|
1719
|
+
|
|
1720
|
+
:param string: 源字符串
|
|
1721
|
+
:param regex: 正则表达式模式
|
|
1722
|
+
:param replacement: 替换字符串(可包含反向引用如 ``\\1``)
|
|
1723
|
+
:return: 替换所有正则匹配后的字符串
|
|
1724
|
+
"""
|
|
1725
|
+
if CharSequenceUtil.is_empty(string):
|
|
1726
|
+
return string
|
|
1727
|
+
return re.sub(regex, replacement, string) # type: ignore[arg-type]
|
|
1728
|
+
|
|
1729
|
+
# ------------------------------------------------------------------
|
|
1730
|
+
# Join
|
|
1731
|
+
# ------------------------------------------------------------------
|
|
1732
|
+
|
|
1733
|
+
@staticmethod
|
|
1734
|
+
def join(sep: str, *args: Any) -> str:
|
|
1735
|
+
"""
|
|
1736
|
+
将 *args* 用 *sep* 连接成一个字符串。
|
|
1737
|
+
|
|
1738
|
+
每个元素通过 ``str()`` 转换为字符串。None 值表示为 ``""``。
|
|
1739
|
+
|
|
1740
|
+
:param sep: 分隔符
|
|
1741
|
+
:param args: 待连接的值
|
|
1742
|
+
:return: 连接后的字符串
|
|
1743
|
+
"""
|
|
1744
|
+
parts = []
|
|
1745
|
+
for arg in args:
|
|
1746
|
+
if arg is None:
|
|
1747
|
+
parts.append("")
|
|
1748
|
+
elif isinstance(arg, (list, tuple)):
|
|
1749
|
+
for item in arg:
|
|
1750
|
+
parts.append("" if item is None else str(item))
|
|
1751
|
+
else:
|
|
1752
|
+
parts.append(str(arg))
|
|
1753
|
+
return sep.join(parts)
|
|
1754
|
+
|
|
1755
|
+
@staticmethod
|
|
1756
|
+
def join_array(array: list, conjunction: str) -> str:
|
|
1757
|
+
"""
|
|
1758
|
+
将 *array* 的元素用 *conjunction* 连接成一个字符串。
|
|
1759
|
+
|
|
1760
|
+
:param array: 待连接的值列表
|
|
1761
|
+
:param conjunction: 分隔符
|
|
1762
|
+
:return: 连接后的字符串
|
|
1763
|
+
"""
|
|
1764
|
+
if not array:
|
|
1765
|
+
return ""
|
|
1766
|
+
return conjunction.join(str(item) for item in array)
|
|
1767
|
+
|
|
1768
|
+
# ------------------------------------------------------------------
|
|
1769
|
+
# Split
|
|
1770
|
+
# ------------------------------------------------------------------
|
|
1771
|
+
|
|
1772
|
+
@staticmethod
|
|
1773
|
+
def split(
|
|
1774
|
+
string: Optional[str],
|
|
1775
|
+
separator: str,
|
|
1776
|
+
limit: int = -1,
|
|
1777
|
+
is_trim: bool = False,
|
|
1778
|
+
ignore_empty: bool = False,
|
|
1779
|
+
) -> List[str]:
|
|
1780
|
+
"""
|
|
1781
|
+
按 *separator* 分割 *string*,支持可选的裁剪和空元素移除。
|
|
1782
|
+
|
|
1783
|
+
:param string: 源字符串
|
|
1784
|
+
:param separator: 分隔符字符串
|
|
1785
|
+
:param limit: 最大分割份数(类似 ``str.split(sep, n)``)。``-1`` 表示不限制
|
|
1786
|
+
:param is_trim: 是否裁剪每个部分
|
|
1787
|
+
:param ignore_empty: 是否移除空部分
|
|
1788
|
+
:return: 分割后的字符串列表
|
|
1789
|
+
"""
|
|
1790
|
+
if CharSequenceUtil.is_empty(string):
|
|
1791
|
+
return []
|
|
1792
|
+
|
|
1793
|
+
if limit == -1:
|
|
1794
|
+
parts = string.split(separator) # type: ignore[union-attr]
|
|
1795
|
+
else:
|
|
1796
|
+
parts = string.split(separator, limit) # type: ignore[union-attr]
|
|
1797
|
+
|
|
1798
|
+
if is_trim:
|
|
1799
|
+
parts = [p.strip() for p in parts]
|
|
1800
|
+
if ignore_empty:
|
|
1801
|
+
parts = [p for p in parts if p != ""]
|
|
1802
|
+
return parts
|
|
1803
|
+
|
|
1804
|
+
# ------------------------------------------------------------------
|
|
1805
|
+
# Pad
|
|
1806
|
+
# ------------------------------------------------------------------
|
|
1807
|
+
|
|
1808
|
+
@staticmethod
|
|
1809
|
+
def pad(string: Optional[str], size: int, pad_str: str = " ", is_right: bool = True) -> str:
|
|
1810
|
+
"""
|
|
1811
|
+
使用 *pad_str* 将 *string* 填充到 *size* 个字符。
|
|
1812
|
+
|
|
1813
|
+
如果字符串已经至少 *size* 个字符,则原样返回。
|
|
1814
|
+
|
|
1815
|
+
:param string: 源字符串
|
|
1816
|
+
:param size: 目标长度
|
|
1817
|
+
:param pad_str: 填充字符串(默认 ``" "``)
|
|
1818
|
+
:param is_right: True = 右侧填充(左对齐),False = 左侧填充(右对齐)
|
|
1819
|
+
:return: 填充后的字符串
|
|
1820
|
+
"""
|
|
1821
|
+
if string is None:
|
|
1822
|
+
string = ""
|
|
1823
|
+
if len(pad_str) == 0:
|
|
1824
|
+
pad_str = " "
|
|
1825
|
+
if is_right:
|
|
1826
|
+
return string.ljust(size, pad_str)
|
|
1827
|
+
return string.rjust(size, pad_str)
|
|
1828
|
+
|
|
1829
|
+
@staticmethod
|
|
1830
|
+
def center(string: Optional[str], size: int, pad_str: str = " ") -> str:
|
|
1831
|
+
"""
|
|
1832
|
+
使用 *pad_str* 将 *string* 居中对齐到 *size* 个字符。
|
|
1833
|
+
|
|
1834
|
+
:param string: 源字符串
|
|
1835
|
+
:param size: 目标长度
|
|
1836
|
+
:param pad_str: 填充字符串(默认 ``" "``)
|
|
1837
|
+
:return: 居中对齐后的字符串
|
|
1838
|
+
"""
|
|
1839
|
+
if string is None:
|
|
1840
|
+
string = ""
|
|
1841
|
+
if len(pad_str) == 0:
|
|
1842
|
+
pad_str = " "
|
|
1843
|
+
return string.center(size, pad_str)
|
|
1844
|
+
|
|
1845
|
+
# ------------------------------------------------------------------
|
|
1846
|
+
# Repeat
|
|
1847
|
+
# ------------------------------------------------------------------
|
|
1848
|
+
|
|
1849
|
+
@staticmethod
|
|
1850
|
+
def repeat(string: Optional[str], count: int) -> str:
|
|
1851
|
+
"""
|
|
1852
|
+
将 *string* 重复 *count* 次。
|
|
1853
|
+
|
|
1854
|
+
:param string: 待重复的字符串
|
|
1855
|
+
:param count: 重复次数
|
|
1856
|
+
:return: 重复后的字符串
|
|
1857
|
+
"""
|
|
1858
|
+
if CharSequenceUtil.is_empty(string) or count <= 0:
|
|
1859
|
+
return ""
|
|
1860
|
+
return string * count # type: ignore[operator]
|
|
1861
|
+
|
|
1862
|
+
@staticmethod
|
|
1863
|
+
def repeat_and_join(string: Optional[str], count: int, conjunction: str) -> str:
|
|
1864
|
+
"""
|
|
1865
|
+
将 *string* 重复 *count* 次,每次重复之间用 *conjunction* 连接。
|
|
1866
|
+
|
|
1867
|
+
:param string: 待重复的字符串
|
|
1868
|
+
:param count: 重复次数
|
|
1869
|
+
:param conjunction: 重复之间的分隔符
|
|
1870
|
+
:return: 重复并连接后的字符串
|
|
1871
|
+
"""
|
|
1872
|
+
if CharSequenceUtil.is_empty(string) or count <= 0:
|
|
1873
|
+
return ""
|
|
1874
|
+
return conjunction.join([string] * count) # type: ignore[list-item]
|
|
1875
|
+
|
|
1876
|
+
# ------------------------------------------------------------------
|
|
1877
|
+
# Naming conversions
|
|
1878
|
+
# ------------------------------------------------------------------
|
|
1879
|
+
|
|
1880
|
+
@staticmethod
|
|
1881
|
+
def to_camel_case(string: Optional[str]) -> Optional[str]:
|
|
1882
|
+
"""
|
|
1883
|
+
将字符串转换为 camelCase(驼峰命名法)。
|
|
1884
|
+
|
|
1885
|
+
支持 ``snake_case``、``kebab-case`` 和空格分隔的输入。
|
|
1886
|
+
|
|
1887
|
+
Examples::
|
|
1888
|
+
|
|
1889
|
+
"hello_world" -> "helloWorld"
|
|
1890
|
+
"hello-world" -> "helloWorld"
|
|
1891
|
+
"HELLO_WORLD" -> "helloWorld"
|
|
1892
|
+
|
|
1893
|
+
:param string: 源字符串
|
|
1894
|
+
:return: 驼峰命名的字符串
|
|
1895
|
+
"""
|
|
1896
|
+
if CharSequenceUtil.is_empty(string):
|
|
1897
|
+
return string
|
|
1898
|
+
|
|
1899
|
+
# split on underscores, hyphens, and spaces
|
|
1900
|
+
words = re.split(r"[_\- ]+", string) # type: ignore[arg-type]
|
|
1901
|
+
if not words:
|
|
1902
|
+
return string
|
|
1903
|
+
|
|
1904
|
+
result = words[0].lower()
|
|
1905
|
+
for word in words[1:]:
|
|
1906
|
+
if word:
|
|
1907
|
+
result += word[0].upper() + word[1:].lower()
|
|
1908
|
+
return result
|
|
1909
|
+
|
|
1910
|
+
@staticmethod
|
|
1911
|
+
def to_snake_case(string: Optional[str]) -> Optional[str]:
|
|
1912
|
+
"""
|
|
1913
|
+
将字符串转换为 snake_case(蛇形命名法)。
|
|
1914
|
+
|
|
1915
|
+
支持 ``camelCase``、``PascalCase``、``kebab-case`` 和空格分隔的输入。
|
|
1916
|
+
|
|
1917
|
+
Examples::
|
|
1918
|
+
|
|
1919
|
+
"helloWorld" -> "hello_world"
|
|
1920
|
+
"HelloWorld" -> "hello_world"
|
|
1921
|
+
"hello-world" -> "hello_world"
|
|
1922
|
+
|
|
1923
|
+
:param string: 源字符串
|
|
1924
|
+
:return: 蛇形命名的字符串
|
|
1925
|
+
"""
|
|
1926
|
+
if CharSequenceUtil.is_empty(string):
|
|
1927
|
+
return string
|
|
1928
|
+
|
|
1929
|
+
# Replace hyphens and spaces with underscores
|
|
1930
|
+
s = re.sub(r"[\- ]+", "_", string) # type: ignore[arg-type]
|
|
1931
|
+
|
|
1932
|
+
# Insert underscore before uppercase letters preceded by lowercase
|
|
1933
|
+
s = re.sub(r"([a-z])([A-Z])", r"\1_\2", s)
|
|
1934
|
+
# Insert underscore between consecutive uppercase and following
|
|
1935
|
+
# lowercase (e.g. "HTMLParser" -> "HTML_Parser")
|
|
1936
|
+
s = re.sub(r"([A-Z]+)([A-Z][a-z])", r"\1_\2", s)
|
|
1937
|
+
|
|
1938
|
+
return s.lower()
|
|
1939
|
+
|
|
1940
|
+
@staticmethod
|
|
1941
|
+
def to_under_score_case(string: Optional[str]) -> Optional[str]:
|
|
1942
|
+
"""
|
|
1943
|
+
:meth:`to_snake_case` 的别名。
|
|
1944
|
+
|
|
1945
|
+
:param string: 源字符串
|
|
1946
|
+
:return: 蛇形命名的字符串
|
|
1947
|
+
"""
|
|
1948
|
+
return CharSequenceUtil.to_snake_case(string)
|
|
1949
|
+
|
|
1950
|
+
# ------------------------------------------------------------------
|
|
1951
|
+
# Similarity
|
|
1952
|
+
# ------------------------------------------------------------------
|
|
1953
|
+
|
|
1954
|
+
@staticmethod
|
|
1955
|
+
def similar(str1: Optional[str], str2: Optional[str], is_ignore_case: bool = False) -> float:
|
|
1956
|
+
"""
|
|
1957
|
+
使用 Python 的 ``difflib.SequenceMatcher`` 计算两个字符串的相似度。
|
|
1958
|
+
返回 [0.0, 1.0] 之间的浮点数。
|
|
1959
|
+
|
|
1960
|
+
:param str1: 第一个字符串
|
|
1961
|
+
:param str2: 第二个字符串
|
|
1962
|
+
:param is_ignore_case: 是否忽略大小写
|
|
1963
|
+
:return: 相似度比率(0.0 到 1.0)
|
|
1964
|
+
"""
|
|
1965
|
+
if str1 is None and str2 is None:
|
|
1966
|
+
return 1.0
|
|
1967
|
+
if str1 is None or str2 is None:
|
|
1968
|
+
return 0.0
|
|
1969
|
+
s1 = str1.lower() if is_ignore_case else str1
|
|
1970
|
+
s2 = str2.lower() if is_ignore_case else str2
|
|
1971
|
+
return SequenceMatcher(None, s1, s2).ratio()
|
|
1972
|
+
|
|
1973
|
+
# ------------------------------------------------------------------
|
|
1974
|
+
# Format with map
|
|
1975
|
+
# ------------------------------------------------------------------
|
|
1976
|
+
|
|
1977
|
+
@staticmethod
|
|
1978
|
+
def format_with_map(template: str, map_: dict, ignore_null: bool = False) -> str:
|
|
1979
|
+
"""
|
|
1980
|
+
通过将 ``{key}`` 占位符替换为 *map_* 中的值来格式化 *template*。
|
|
1981
|
+
|
|
1982
|
+
如果 *ignore_null* 为 False 且键不存在,占位符保持不变。
|
|
1983
|
+
如果 *ignore_null* 为 True,缺失的键将被替换为空字符串。
|
|
1984
|
+
|
|
1985
|
+
:param template: 包含 ``{key}`` 占位符的模板字符串
|
|
1986
|
+
:param map_: 映射键到替换值的字典
|
|
1987
|
+
:param ignore_null: 为 True 时,缺失的键替换为 ``""``
|
|
1988
|
+
:return: 格式化后的字符串
|
|
1989
|
+
"""
|
|
1990
|
+
|
|
1991
|
+
def _replacer(match: re.Match) -> str:
|
|
1992
|
+
key = match.group(1)
|
|
1993
|
+
if key in map_:
|
|
1994
|
+
return str(map_[key])
|
|
1995
|
+
if ignore_null:
|
|
1996
|
+
return ""
|
|
1997
|
+
return match.group(0)
|
|
1998
|
+
|
|
1999
|
+
return re.sub(r"\{(\w+)\}", _replacer, template)
|
|
2000
|
+
|
|
2001
|
+
# ------------------------------------------------------------------
|
|
2002
|
+
# Type checks
|
|
2003
|
+
# ------------------------------------------------------------------
|
|
2004
|
+
|
|
2005
|
+
@staticmethod
|
|
2006
|
+
def is_numeric(string: Optional[str]) -> bool:
|
|
2007
|
+
"""
|
|
2008
|
+
判断 *string* 是否全部由数字字符组成。
|
|
2009
|
+
允许前导 ``-`` 号(与 Java Hutool 的 ``StrUtil.isNumeric`` 对齐)。
|
|
2010
|
+
|
|
2011
|
+
:param string: 待检查的字符串
|
|
2012
|
+
:return: 是否为数字字符串
|
|
2013
|
+
"""
|
|
2014
|
+
if CharSequenceUtil.is_empty(string):
|
|
2015
|
+
return False
|
|
2016
|
+
s = string.lstrip("-") # type: ignore[union-attr]
|
|
2017
|
+
if len(s) == 0:
|
|
2018
|
+
return False
|
|
2019
|
+
return s.isdigit()
|
|
2020
|
+
|
|
2021
|
+
@staticmethod
|
|
2022
|
+
def is_number(string: Optional[str]) -> bool:
|
|
2023
|
+
"""
|
|
2024
|
+
判断 *string* 是否表示有效的数字(整数或浮点数)。
|
|
2025
|
+
|
|
2026
|
+
:param string: 待检查的字符串
|
|
2027
|
+
:return: 是否为有效数字
|
|
2028
|
+
"""
|
|
2029
|
+
if CharSequenceUtil.is_empty(string):
|
|
2030
|
+
return False
|
|
2031
|
+
try:
|
|
2032
|
+
float(string)
|
|
2033
|
+
return True
|
|
2034
|
+
except (ValueError, TypeError):
|
|
2035
|
+
return False
|
|
2036
|
+
|
|
2037
|
+
@staticmethod
|
|
2038
|
+
def is_alpha(string: Optional[str]) -> bool:
|
|
2039
|
+
"""
|
|
2040
|
+
判断 *string* 是否全部由字母组成。
|
|
2041
|
+
|
|
2042
|
+
:param string: 待检查的字符串
|
|
2043
|
+
:return: 是否全部为字母
|
|
2044
|
+
"""
|
|
2045
|
+
if CharSequenceUtil.is_empty(string):
|
|
2046
|
+
return False
|
|
2047
|
+
return string.isalpha() # type: ignore[union-attr]
|
|
2048
|
+
|
|
2049
|
+
@staticmethod
|
|
2050
|
+
def is_alpha_upper(string: Optional[str]) -> bool:
|
|
2051
|
+
"""
|
|
2052
|
+
判断 *string* 是否全部由大写字母组成。
|
|
2053
|
+
|
|
2054
|
+
:param string: 待检查的字符串
|
|
2055
|
+
:return: 是否全部为大写字母
|
|
2056
|
+
"""
|
|
2057
|
+
if CharSequenceUtil.is_empty(string):
|
|
2058
|
+
return False
|
|
2059
|
+
return string.isalpha() and string.isupper() # type: ignore[union-attr]
|
|
2060
|
+
|
|
2061
|
+
@staticmethod
|
|
2062
|
+
def is_alpha_lower(string: Optional[str]) -> bool:
|
|
2063
|
+
"""
|
|
2064
|
+
判断 *string* 是否全部由小写字母组成。
|
|
2065
|
+
|
|
2066
|
+
:param string: 待检查的字符串
|
|
2067
|
+
:return: 是否全部为小写字母
|
|
2068
|
+
"""
|
|
2069
|
+
if CharSequenceUtil.is_empty(string):
|
|
2070
|
+
return False
|
|
2071
|
+
return string.isalpha() and string.islower() # type: ignore[union-attr]
|
|
2072
|
+
|
|
2073
|
+
|
|
2074
|
+
# ---------------------------------------------------------------------------
|
|
2075
|
+
# StrPool
|
|
2076
|
+
# ---------------------------------------------------------------------------
|
|
2077
|
+
class StrPool(CharPool):
|
|
2078
|
+
"""常用字符串常量。"""
|
|
2079
|
+
|
|
2080
|
+
# 字符串常量:'None'
|
|
2081
|
+
NONE: str = "None"
|
|
2082
|
+
# 字符串常量:双点 "..",
|
|
2083
|
+
# 用途:作为指向上级文件夹的路径,如:"../path"
|
|
2084
|
+
DOUBLE_DOT: str = ".."
|
|
2085
|
+
# 字符串常量:Windows 换行 "\r\n"
|
|
2086
|
+
CRLF: str = "\r\n"
|
|
2087
|
+
# 字符串常量:HTML 不间断空格转义 " "
|
|
2088
|
+
HTML_NBSP: str = " "
|
|
2089
|
+
# 字符串常量:HTML And 符转义 "&"
|
|
2090
|
+
HTML_AMP: str = "&"
|
|
2091
|
+
# 字符串常量:HTML 双引号转义 """
|
|
2092
|
+
HTML_QUOTE: str = """
|
|
2093
|
+
# 字符串常量:HTML 单引号转义 "'"
|
|
2094
|
+
HTML_APOS: str = "'"
|
|
2095
|
+
# 字符串常量:HTML 小于号转义 "<"
|
|
2096
|
+
HTML_LT: str = "<"
|
|
2097
|
+
# 字符串常量:HTML 大于号转义 ">"
|
|
2098
|
+
HTML_GT: str = ">"
|
|
2099
|
+
# 字符串常量:空 JSON "{}"
|
|
2100
|
+
EMPTY_JSON: str = "{}"
|
|
2101
|
+
|
|
2102
|
+
|
|
2103
|
+
# ---------------------------------------------------------------------------
|
|
2104
|
+
# StrUtil
|
|
2105
|
+
# ---------------------------------------------------------------------------
|
|
2106
|
+
class StrUtil(CharSequenceUtil, StrPool):
|
|
2107
|
+
"""
|
|
2108
|
+
主字符串工具类。继承 :class:`CharSequenceUtil` 和 :class:`StrPool`,
|
|
2109
|
+
提供所有字符串操作的统一入口。
|
|
2110
|
+
"""
|
|
2111
|
+
|
|
2112
|
+
@staticmethod
|
|
2113
|
+
def is_blank_if_str(obj: Any) -> bool:
|
|
2114
|
+
"""
|
|
2115
|
+
如果 *obj* 是字符串,判断其是否为空白。非字符串对象返回 False(None 返回 True)。
|
|
2116
|
+
|
|
2117
|
+
:param obj: 待检查的对象
|
|
2118
|
+
:return: *obj* 为 None 或为空白字符串时返回 True
|
|
2119
|
+
"""
|
|
2120
|
+
if obj is None:
|
|
2121
|
+
return True
|
|
2122
|
+
elif isinstance(obj, str):
|
|
2123
|
+
return CharSequenceUtil.is_blank(obj)
|
|
2124
|
+
return False
|
|
2125
|
+
|
|
2126
|
+
@staticmethod
|
|
2127
|
+
def is_empty_if_str(obj: Any) -> bool:
|
|
2128
|
+
"""
|
|
2129
|
+
如果 *obj* 是字符串,判断其是否为空。非字符串对象返回 False(None 返回 True)。
|
|
2130
|
+
|
|
2131
|
+
:param obj: 待检查的对象
|
|
2132
|
+
:return: *obj* 为 None 或为空字符串时返回 True
|
|
2133
|
+
"""
|
|
2134
|
+
if obj is None:
|
|
2135
|
+
return True
|
|
2136
|
+
elif isinstance(obj, str):
|
|
2137
|
+
return CharSequenceUtil.is_empty(obj)
|
|
2138
|
+
return False
|
|
2139
|
+
|
|
2140
|
+
@staticmethod
|
|
2141
|
+
def trim(
|
|
2142
|
+
string: Union[List[str], str, None],
|
|
2143
|
+
mode: int = 0,
|
|
2144
|
+
predicate: Callable[[str], bool] = CharUtil.is_blank_char,
|
|
2145
|
+
) -> Union[str, None]:
|
|
2146
|
+
"""
|
|
2147
|
+
裁剪字符串,或就地裁剪字符串列表中的每个元素。
|
|
2148
|
+
|
|
2149
|
+
:param string: 字符串或字符串列表
|
|
2150
|
+
:param mode: ``-1`` = 仅裁剪头部,``0`` = 两端裁剪,``1`` = 仅裁剪尾部
|
|
2151
|
+
:param predicate: 返回 True 时表示该字符应被移除的函数
|
|
2152
|
+
:return: 裁剪后的字符串或 None
|
|
2153
|
+
"""
|
|
2154
|
+
if isinstance(string, str):
|
|
2155
|
+
return CharSequenceUtil.trim(string, mode=mode, predicate=predicate)
|
|
2156
|
+
|
|
2157
|
+
if string is None:
|
|
2158
|
+
return None
|
|
2159
|
+
for i in range(len(string)):
|
|
2160
|
+
s = string[i]
|
|
2161
|
+
if s is not None:
|
|
2162
|
+
string[i] = CharSequenceUtil.trim(s) # type: ignore[index]
|
|
2163
|
+
return None
|
|
2164
|
+
|
|
2165
|
+
@staticmethod
|
|
2166
|
+
def to_str(obj: Any, charset: str = "utf-8") -> Optional[str]:
|
|
2167
|
+
"""
|
|
2168
|
+
将 *obj* 转换为字符串。
|
|
2169
|
+
|
|
2170
|
+
``bytes``/``bytearray``/``array.array`` 使用 *charset* 解码。列表会递归转换。
|
|
2171
|
+
|
|
2172
|
+
:param obj: 待转换的对象
|
|
2173
|
+
:param charset: 字符编码
|
|
2174
|
+
:return: 字符串表示
|
|
2175
|
+
"""
|
|
2176
|
+
if obj is None:
|
|
2177
|
+
return None
|
|
2178
|
+
|
|
2179
|
+
if isinstance(obj, str):
|
|
2180
|
+
return obj
|
|
2181
|
+
elif isinstance(obj, (bytes, bytearray, array.array)):
|
|
2182
|
+
return obj.decode(charset)
|
|
2183
|
+
elif isinstance(obj, memoryview):
|
|
2184
|
+
return obj.tobytes().decode(charset)
|
|
2185
|
+
elif isinstance(obj, list):
|
|
2186
|
+
return str(StrUtil.to_str(item, charset=charset) for item in obj)
|
|
2187
|
+
return str(obj)
|
|
2188
|
+
|
|
2189
|
+
@staticmethod
|
|
2190
|
+
def to_str_or_none(obj: Any) -> Optional[str]:
|
|
2191
|
+
"""
|
|
2192
|
+
将 *obj* 转换为字符串,None 输入返回 None。
|
|
2193
|
+
|
|
2194
|
+
:param obj: 待转换的对象
|
|
2195
|
+
:return: 字符串表示或 None
|
|
2196
|
+
"""
|
|
2197
|
+
return None if obj is None else str(obj)
|
|
2198
|
+
|
|
2199
|
+
@staticmethod
|
|
2200
|
+
def reverse(string: Optional[str]) -> Optional[str]:
|
|
2201
|
+
"""
|
|
2202
|
+
反转 *string*。
|
|
2203
|
+
|
|
2204
|
+
:param string: 待反转的字符串
|
|
2205
|
+
:return: 反转后的字符串
|
|
2206
|
+
"""
|
|
2207
|
+
if CharSequenceUtil.is_empty(string):
|
|
2208
|
+
return string
|
|
2209
|
+
return string[::-1] # type: ignore[index]
|
|
2210
|
+
|
|
2211
|
+
@staticmethod
|
|
2212
|
+
def fill_before(string: Optional[str], filled_char: str, length: int) -> Optional[str]:
|
|
2213
|
+
"""
|
|
2214
|
+
使用 *filled_char* 将 *string* 左侧填充到 *length*。
|
|
2215
|
+
|
|
2216
|
+
:param string: 待填充的字符串
|
|
2217
|
+
:param filled_char: 单个填充字符
|
|
2218
|
+
:param length: 目标长度
|
|
2219
|
+
:return: 填充后的字符串
|
|
2220
|
+
"""
|
|
2221
|
+
return StrUtil.fill(string, filled_char, length, True)
|
|
2222
|
+
|
|
2223
|
+
@staticmethod
|
|
2224
|
+
def fill_after(string: Optional[str], filled_char: str, length: int) -> Optional[str]:
|
|
2225
|
+
"""
|
|
2226
|
+
使用 *filled_char* 将 *string* 右侧填充到 *length*。
|
|
2227
|
+
|
|
2228
|
+
:param string: 待填充的字符串
|
|
2229
|
+
:param filled_char: 单个填充字符
|
|
2230
|
+
:param length: 目标长度
|
|
2231
|
+
:return: 填充后的字符串
|
|
2232
|
+
"""
|
|
2233
|
+
return StrUtil.fill(string, filled_char, length, False)
|
|
2234
|
+
|
|
2235
|
+
@staticmethod
|
|
2236
|
+
def fill(string: Optional[str], filled_char: str, length: int, is_pre: bool) -> Optional[str]:
|
|
2237
|
+
"""
|
|
2238
|
+
使用 *filled_char* 将 *string* 填充到 *length*,
|
|
2239
|
+
可选择左侧填充(``is_pre=True``)或右侧填充(``is_pre=False``)。
|
|
2240
|
+
|
|
2241
|
+
:param string: 待填充的字符串
|
|
2242
|
+
:param filled_char: 单个填充字符
|
|
2243
|
+
:param length: 目标长度
|
|
2244
|
+
:param is_pre: 是否左侧填充
|
|
2245
|
+
:return: 填充后的字符串
|
|
2246
|
+
:raises TypeError: *filled_char* 不是单个字符时抛出异常
|
|
2247
|
+
"""
|
|
2248
|
+
if CharSequenceUtil.is_empty(filled_char):
|
|
2249
|
+
return string
|
|
2250
|
+
if len(filled_char) > 1:
|
|
2251
|
+
raise TypeError("The fill character must be exactly one character long")
|
|
2252
|
+
if string is None:
|
|
2253
|
+
return filled_char * length
|
|
2254
|
+
if is_pre:
|
|
2255
|
+
return string.rjust(length, filled_char)
|
|
2256
|
+
return string.ljust(length, filled_char)
|
|
2257
|
+
|
|
2258
|
+
@staticmethod
|
|
2259
|
+
def format(template: str, *args: Any, **kwargs: Any) -> str:
|
|
2260
|
+
"""
|
|
2261
|
+
使用 Python 的 ``str.format()`` 格式化 *template*。
|
|
2262
|
+
|
|
2263
|
+
:param template: 模板字符串
|
|
2264
|
+
:param args: 位置参数
|
|
2265
|
+
:param kwargs: 关键字参数
|
|
2266
|
+
:return: 格式化后的字符串
|
|
2267
|
+
"""
|
|
2268
|
+
return template.format(*args, **kwargs)
|
|
2269
|
+
|
|
2270
|
+
@staticmethod
|
|
2271
|
+
def truncate_utf8(string: Optional[str], max_bytes: int) -> Optional[str]:
|
|
2272
|
+
"""
|
|
2273
|
+
截断 *string* 使其 UTF-8 编码不超过 *max_bytes*。
|
|
2274
|
+
截断时会附加省略号(``...``)。
|
|
2275
|
+
|
|
2276
|
+
:param string: 待截断的字符串
|
|
2277
|
+
:param max_bytes: 最大字节长度
|
|
2278
|
+
:return: 截断后的字符串
|
|
2279
|
+
"""
|
|
2280
|
+
return StrUtil.truncate_by_byte_length(string, "utf-8", max_bytes, 4, True)
|
|
2281
|
+
|
|
2282
|
+
@staticmethod
|
|
2283
|
+
def truncate_by_byte_length(
|
|
2284
|
+
string: Optional[str],
|
|
2285
|
+
charset: str,
|
|
2286
|
+
max_bytes: int,
|
|
2287
|
+
factor: int,
|
|
2288
|
+
append_dots: bool,
|
|
2289
|
+
) -> Optional[str]:
|
|
2290
|
+
"""
|
|
2291
|
+
截断 *string* 使其编码字节长度不超过 *max_bytes*。
|
|
2292
|
+
|
|
2293
|
+
*factor* 是速度估计除数(编码中单个字符的最大字节长度)。
|
|
2294
|
+
|
|
2295
|
+
:param string: 待截断的字符串
|
|
2296
|
+
:param charset: 字符编码
|
|
2297
|
+
:param max_bytes: 最大字节长度
|
|
2298
|
+
:param factor: 速度估计除数
|
|
2299
|
+
:param append_dots: 截断后是否附加 ``...``
|
|
2300
|
+
:return: 截断后的字符串
|
|
2301
|
+
"""
|
|
2302
|
+
if string is None or len(string) * factor <= max_bytes:
|
|
2303
|
+
return string
|
|
2304
|
+
|
|
2305
|
+
sba = string.encode(charset)
|
|
2306
|
+
if len(sba) <= max_bytes:
|
|
2307
|
+
return string
|
|
2308
|
+
|
|
2309
|
+
limit_bytes = max_bytes
|
|
2310
|
+
if append_dots:
|
|
2311
|
+
limit_bytes -= len("...".encode(charset))
|
|
2312
|
+
|
|
2313
|
+
bb = sba[:limit_bytes]
|
|
2314
|
+
decoder = codecs.getincrementaldecoder(charset)(errors="ignore")
|
|
2315
|
+
result = decoder.decode(bb, final=True)
|
|
2316
|
+
|
|
2317
|
+
if append_dots:
|
|
2318
|
+
return result + "..."
|
|
2319
|
+
|
|
2320
|
+
return result
|