3 字符串、切片、二进制列和正则表达式
字符串
形式
字符串一般被单引号''
(可以内嵌双引号)、双引号""
(可以内嵌单引号)、三重引号(多行,与多行注释的格式一致)包围,并使用\
作为转义字符。
letters = 'cnblogs' letters = ''' I couldn't wait for you to come clear the cupboards But now you're going to leave with nothing but a sign Another evening I'll be sitting reading in between your lines Because I miss you all the time So, get away ''' # 实际上,多行字符串可以直接表示为: letters = 'I couldn\'t wait for you to come clear the cupboards' 'But now you\'re going to leave with nothing but a sign' 'Another evening I\'ll be sitting reading in between your lines' 'Because I miss you all the time' 'So, get away'
切片
用法
包括字符串、列表、元组在内的多个有序可迭代数据类型都支持切片运算。iters[i:j:k]
表示可迭代对象下标从i
到j - 1
的步长为k
的子列(:k
可以省略,默认为),即遵循左闭右开的原则。
i
、j
和k
可以是一切整数,即使其小于,或者大于len(iters)
。同时,i
可以大于等于j
。考虑时:
- 当时,切片为空。
- 当时,切片为空。
- 当时,切片的有效右边界为
len(iters)
。 - 当或为负数时,对应表示的下标将从可迭代对象的右侧开始计数,例如
iters[-1]
表示最后一个元素,iters[-2]
表示倒数第二个元素。 - 当省略时,默认
i = 0
;当省略时,默认j = len(iters)
。
letters = 'python' arrays = [1, 2, 3, 4, 5] print(letters[1:2]) # y print(letters[3:8]) # hon,不会报错,但是右边界为6。 print(letters[2:2]) # ``,不会报错,但输出为空。 print(letters[7:10]) # ``,不会报错,但输出为空。 print(arrays[:]) # [1, 2, 3, 4, 5],可以作为复制对象的方式。 print(arrays[1:]) # [2, 3, 4, 5] print(arrays[:-1]) # [1, 2, 3, 4] print(arrays[-4:-2]) # [2, 3] print(arrays[:-9]) # []
当时,表示倒序的步长。
arrays = [1, 2, 3, 4, 5] print(arrays[::-1]) # [5, 4, 3, 2, 1],可以作为获取倒序对象的方式。
注意点
切片是对原对象的一次“浅拷贝”,与直接赋值不同(直接将对象的头地址提供给新变量),切片将某一段值“复制”下来并申请新的内存空间来存储。因此在切片上修改非引用对象不会导致原对象的修改,在原对象上修改非引用对象也不会导致已有的切片的值。
既然切片是浅拷贝,则说明切片仅仅复制了父对象,对于父对象下的一干子对象都没有复制:
arrays = [1, 2, 3, 4] lines = ['abcde', arrays, 17, 18] # lines父对象中包含了arrays子对象。 result = lines[1:] # 根据浅拷贝的定义,切片与原对象对arrays的引用仍然是同一个。 print(result) # [[1, 2, 3, 4], 17, 18] # 对切片中非引用对象的修改只修改了切片,没有修改到原对象。 result.append(19) print(lines) # ['abcde', [1, 2, 3, 4], 17, 18] print(result) # [[1, 2, 3, 4], 17, 18, 19] # 对切片中引用对象的修改涉及到了子对象arrays,浅拷贝并没有拷贝arrays。 arrays.append(5) print(lines) # ['abcde', [1, 2, 3, 4, 5], 17, 18],导致了原对象的修改。 print(result) # [[1, 2, 3, 4, 5], 17, 18, 19]
运算
字符串的运算支持加法和乘法,表示“连接”和“重复”。
letters1, letters2 = 'abcde', 'fgh' print(letters1 + letters2) # abcdefgh print(letters1 * 2) # abcdeabcde
此外有r
表达式,表示不做任何处理的字符串:
letters1, letters2, letters3 = r'a\b\c\naa', 'a\b\c\naa', 'a\\b\\c\\naa' print(letters1) # a\b\c\naa # letters2被转义:\b->退格,\n->换行,同时由于\c无法转义会抛出警告。 print(letters2) # \c[换行]aa print(letters3) # a\b\c\naa
方法
见Python文档。
格式化
Python现版本提供3种常用的字符串格式化方法。
通过%实现格式化
`'%s%%%d' % ('CyberPunk', 2077)` # CyberPunk%2077
通过%?
的形式来实现占位符,并以元组的方式顺序表示被格式化的字符串。常见的%?
有:
表示 | 含义 |
---|---|
%s |
字符串 |
%d |
整数 |
%u |
无符号整数 |
%o |
无符号八进制数 |
%x |
无符号十六进制数 |
%f |
浮点数 |
%e |
科学计数法表示的浮点数 |
%p |
十六进制格式的地址 |
%% |
% |
通过.format()方法实现格式化
str.format(*args, **kwargs) ''' 执行字符串格式化操作。 调用此方法的字符串可以包含字符串字面值或者以{}括起来的替换域。每个替换域可以包含一个位置参数 的数字索引,或者一个关键字参数的名称。 :return: 字符串副本中每个替换域都会被替换为对应参数的字符串值。 ''' # 使用: # 以下全部输出CyberPunk 2077。 'Cyber{} {}'.format('Punk', 2077) 'Cyber{1} {0}'.format(2077, 'Punk') 'Cyber{name} {year}'.format(name = 'Punk', year = 2077) # 此外,可以使用不定长参数的形式向.format()中传递参数。 'Cyber{} {}'.format(*('Punk', 2077))
通过
.format()
实现数字的格式化:
# 含符号位保留2位小数 '{:.2f}'.format(3.1415926) # 3.14 # 左边补x,宽度为4 '{:x>4d}'.format(314) # x314 # 右边补x,宽度为4 '{:x<4d}'.format(159) # 159x # {:,}采用逗号,分隔 '{:,}'.format(31415926) # 31,415,926 # {:.n%}n位小数百分比计数 '{:.2%}'.format(0.314) # 31.40% # {:.ne}n位小数指数计数 '{:.2e}'.format(314159) # 3.14e+05 # {:>nd}n位右对齐、{:<nd}n位左对齐、{:^nd}居中 '{:>10d}'.format(314) # 314 '{:<10d}'.format(314) # 314 '{:^10d}'.format(314) # 314
通过F表达式格式化字符串
F
表达式形如f''
,在引号中包含被花括号包围的变量名称。
arrays = ['Punk', 2077] print(f'Cyber{arrays[0]} {arrays[1]}') # CyberPunk 2077 print(f'{arrays[1] + 1 = }') # arrays[1] + 1 = 2078,通过=拼接运算结果。 # 通过:?来格式化小数。 number = 3.14159 f'{number:.2f}' # 保留两位小数。 f'{number:10.2f}' # 宽度为10,精度为2,靠右对齐。 f'{number:010.2f}' # 宽度为10,左侧补0,精度为2。
二进制列bytes
bytes
是不可变的二进制列,通常用于处理图像、音视频以及网络编程中。作为字符串类型的高级数据类型,bytes
类型的数据支持切片、拼接、查找、替换等许多操作和方法,但是bytes
类型中的元素是从到的整数值,而非Unicode字符,通常表示为b''
。
一般,通过bytes()
函数可以将其他类型的对象转化为bytes
类型:
byte_letters = bytes('Python', encoding = 'utf-8') # 指明编码方式是UTF-8,参数可选,默认为UTF-8。
除此之外,通过encode()
和decode()
函数可以将字符串编码成二进制列或者将二进制列解码成字符串。
str.encode(encoding = 'utf-8', errors = 'strict') ''' 返回编码为bytes的字符串。 :param encoding: 编码规则,可选参数,默认UTF-8。 :param errors: 处理编码错误的方式,可选参数,默认值'strict'会引发UnicodeError。 ''' bytes.decode(encoding = 'utf-8', errors = 'strict') ''' 返回解码为str的字节串。 '''
由于
bytes
的内容是整数值,因此做相等比较时需要将待比较值转化为数字:
letters = b'Python' print(letters[0] == ord('P))
正则表达式
标准的正则表达式使用反斜杠字符\
表示需要转义的特殊字符。按照此规定,如果在字符串中要匹配'\\'
,用户在正则表达式中就需要写成'\\\\'
,但是每个\
在Python中必须被表示为\\
。因此在Python中,需要使用未作处理的字符串来表示正则表达式:r''
。
正则表达式的部分特殊字符
字符串
-
.
:默认匹配除去\n
以外的任意字符。如果设定了
flag = S
,则将匹配任意字符。 -
^
:匹配字符串开头;在M
模式下也匹配换行后的首个符号。 -
$
:匹配字符串尾或者在字符串尾的换行符的前一个字符,在M
模式下也会匹配换行符之前的符号。python\njython\n
中搜索.ython$
,一般会匹配jython
;但是在M
模式下,可以匹配到python
。
在python\n
中匹配$
会找到2个匹配:(换行符之前, 字符串末尾)。
重复次数
-
*
:其前的正则式匹配到任意次重复,并尽量多的匹配字符串。 -
+
:其前的正则式匹配到任意次重复,并尽量多的匹配字符串。 -
?
:其前的正则式匹配到次重复,并尽量多的匹配字符串。*
、+
、?
都采用了贪心思想,尽可能多的匹配字符。例如<.py><.pyc>
按照<.*>
来匹配将匹配整个字符串。因此在上述个数量限定符之后在紧跟着?
*,将按照非贪婪的方式来匹配:*?
、+?
、??
。 -
{m}
:其前的正则式指定匹配次重复;少于则匹配失败。python{6}
将匹配pythonnnnnn
,但是无法匹配pythonnnnn
。 -
{m,n}
:其前的正则式进行到次尽量多的匹配。默认,即m
和n
可以省略。{m,n}?
:{m,n}
的非贪心版本。
组合
-
[]
:表示字符集合。特殊字符(不包括转义字符)在
[]
中将失去特殊意义:例如[p*]
将匹配p
或*
;[.\n]
将匹配.
或\n
。[aho]
:匹配a
或h
或o
。[a-zA-Z0-9]
:匹配大小写英文字母和数字。[^]
:通过^
符号取反。当^
在起始位置时,正则表达式将匹配不在集合中的字符。例如[^a-z]
将匹配不是小写英文字母的字符;[^^]
将匹配不是^
的字符;[\^]
将匹配\
或^
;[^]
是非法的,将导致re.error
。
-
|
:或运算符。 -
()
:正则表达式组合,匹配括号内的任意正则表达式,并标识出组合的开始和结尾。匹配完成提供内容的获取方式。(?)
:扩展标记法。(?P<name>…)
:组合匹配到了子字符串可通过符号分组名称name
来访问。
注意:一次正则表达式匹配行为中,贪心模式下重复匹配的格式串对应的结果会被覆盖。
# b = 2。 # a = 1的结果被覆盖。 print(findall(r'([a-zA-Z]+ = \d+)+', 'a = 1b = 2')) # ['a = 1', 'b = 2']。 print(findall(r'([a-zA-Z]+ = \d+)', 'a = 1b = 2')) # 声明命名param和data。 result = search(r'((?P<param>[a-zA-Z]+) = (?P<data>\d+))', 'a = 1b = 2') # a 1。 # search()函数仅匹配第一个匹配项。 print(result.group('param'), result.group('data'))
转义字符
-
\number
:匹配数字代表的组合()
的内容,表示子串的重复,组合编号从开始。import re # \1匹配第1个组合匹配成功的内容。 # [('hijk4', 'lmnopqr7')]。 print(re.findall(r'([a-z]+[0-4])([a-z]+[5-9])\1', 'abcd4efg3hijk4lmnopqr7hijk4')) # []。 print(re.findall(r'([a-z]+[0-4])([a-z]+[5-9])\1', 'abcd4efg3hijk4lmnopqr7hijk3')) # \2匹配第2个组合匹配成功的内容。 # [('hijk4', 'lmnopqr7')]。 print(re.findall(r'([a-z]+[0-4])([a-z]+[5-9])\2', 'abcd4efg3hijk4lmnopqr7lmnopqr7')) # []。 print(re.findall(r'([a-z]+[0-4])([a-z]+[5-9])\2', 'abcd4efg3hijk4lmnopqr7lmnopqrst9')) # [('hijk4', 'lmnopqr7'), ('op2', 'rstuvw6')]。 print(re.findall(r'([a-z]+[0-4])([a-z]+[5-9])\1', 'abcd4efg3hijk4lmnopqr7hijk4op2rstuvw6op2')) \number
只能用于匹配前个组合。如果number
的第一个数位是,或者number
是三个八进制数,它将不被看作是一个组合,而是八进制的数字值。- 在
[]
字符集合内,任何数字转义都被看作是字符。
-
\A
:只匹配字符串开始。
\Z
:只匹配字符串结尾。 -
\b
:匹配在单词开始或结尾的空字符串,即\w
和\W
之间的边界,或是\w
和字符串开始或结尾之间的边界。r'\bpython\b'
匹配'python'
、'(python)'
、'java python c'
,但是不匹配thepython
。\B
:匹配不在单词的开始或结尾的空字符串,与\b
相反。 -
\d
:一般表示[0-9]
。
\D
:一般表示[^0-9]
,与\d
相反。 -
\s
:一般表示[ \t\n\r\f\v]
,即Unicode空白字符。
\S
:一般表示[^ \t\n\r\f\v]
,与\s
相反。 -
\w
:一般表示[a-zA-Z0-9_]
。
\W
:一般表示[^a-zA-Z0-9_]
,与\W
相反。 -
其他:正则表达式的用法。
正则表达式标志
A
:使\w
、\W
、\b
、\B
、\d
、\D
、\s
和\S
执行仅限ASCII
匹配而不是完整的Unicode
匹配。仅对str
模式有意义,而被bytes
模式忽略。I
:忽视大小写的匹配。M
:模式字符'^'
将匹配字符串的开始和每一行的开头(紧随在换行符之后);而模式字符'$'
将匹配字符串的末尾和每一行的末尾(紧接在换行符之前)。S
:使'.'
特殊字符匹配任意字符,包括换行符。- 其他:正则表达式标志。
正则表达式函数
-
compile(pattern, flags = 0) ''' 编译模式串。 :param pattern: 模式串。 :param flags: 标志。 ''' 当正则表达式可以复用的时候,推荐先使用
compile()
将其编译成Pattern
类型。 -
search(pattern, string, flags = 0) ''' 扫描字符串string查找正则表达式pattern产生匹配的第一个位置,并返回相应的Match对象。 :return: 成功查找的Match对象;失败则返回None。 ''' -
match(pattern, string, flags = 0) ''' 如果string开头的零个或多个字符与正则表达式pattern匹配,则返回相应的Match。 :return: 成功查找的Match对象;失败则返回None。 注意:即便是
M
模式,match()
也只匹配字符串的开始位置,而不匹配每行开始。 -
fullmatch(pattern, string, flags = 0) ''' 如果整个string与正则表达式pattern匹配,则返回相应的Match。 ''' -
split(pattern, string, maxsplit = 0, flags = 0) ''' 用pattern分开string。如果在pattern中捕获到括号,那么所有的组里的文字也会包含在列表里。 :param maxsplit: 最多进行maxsplit次分隔,剩下的字符全部返回到列表的最后一个元素。 :return: 分割后的字符串列表。 :rtype: list。 ''' # ['abcd', 'efg', 'h', 'ijkl', 'mnop', 'qr', '']。 print(split(r'[0-9]+', 'abcd123efg456h78ijkl9mnop012qr566')) # ['abcd', 'efg', 'h', 'ijkl9mnop012qr566']。 print(split(r'[0-9]+', 'abcd123efg456h78ijkl9mnop012qr566'), 3) -
findall(pattern, string, flags = 0) ''' 返回pattern在string中的所有非重叠顺序匹配(包括空匹配)。 :rtype: list[str] | list[tuple[str]]。 ''' re.findall()
的返回结果取决于模式中组合的数量:- 如果模式中没有组合,则返回与整个模式匹配的字符串列表
list[str]
。 - 如果模式中有且仅有一个组合,返回与该模式组合匹配的字符串列表
list[str]
。 - 如果模式中有多个组合,返回与这些组合匹配的字符串元组列表
list[tuple[str]]
。
import re letters = 'abcdefg123hijk456\n\n\t78lmn' # ['abcdefg', 'hijk', 'lmn']。 print(re.findall(r'[a-z]+', letters)) # ['abcdefg', 'hijk']。 # 按照[a-z]+[1-9]的规则来匹配字符串,但是返回只考虑组合中的[a-z]+。 print(re.findall(r'([a-z]+)[1-9]', letters)) # [('abcdefg', '123'), ('hijk', '456')]。 print(re.findall(r'([a-z]+)([1-9]+)', letters)) # [('abcdefg123', 'abcdefg'), ('hijk456', 'hijk')]。 print(re.findall(r'(([a-z]+)[1-9]+)', letters)) - 如果模式中没有组合,则返回与整个模式匹配的字符串列表
-
finditer(pattern, string, flags = 0) ''' 返回pattern在string中的所有非重叠顺序匹配(包括空匹配)。 :rtype: iter[Match]。 ''' -
sub(pattern, repl, string, count = 0, flags = 0) ''' :type repl: str | func。 :param count: 最大替换次数,默认0表示全部替换。 :return: 使用repl替换在string最左边非重叠出现的pattern获得的字符串; 如果样式没有找到,则不加改变地返回string。 ''' repl
参数可以是字符串(包括正则表达式)或函数:-
如果
repl
是字符串(包括正则表达式),则其中任何反斜杠转义序列都会被处理。即
\n
会被转换为换行符;\r
会被转换为一个回车符;未知的ASCII
字符转义序列被当作错误来处理;其他未知转义序列例如\&
会保持原样;\number
将用样式中第number组所匹配到的子字符串来替换。import re # % function name: # main # repl参数中的\n、\t被正常处理,\1引用main。 print( re.sub(r'def\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\(\s*\):', r'% function name: \n\t\1', 'def main():' ) ) -
如果
repl
是函数(不能使用re.escape()
函数),则它针对每次pattern
的非重叠出现的情况被调用。该函数接受单个Match
参数,并返回替换字符串。
-
-
escape(pattern) ''' 转义pattern中的特殊字符,比如字符串中包含某些诸如'*'、'~'、`.`等正则表达式元字符。 :return: 转义pattern中特殊字符后的字符串。 :rtypr: str。 ''' 该函数不能被用于
re.sub()
和re.subn()
的替换字符串。# abcdefghijklmnopqrstuvwxyz0123456789!\#\$%\&'\*\+\-\.\^_`\|\~:。 print(re.escape(string.ascii_lowercase + string.digits + '!#$%&'*+-.^_`|~:')) -
purge() ''' 清除正则表达式缓存。 '''
正则表达式模式对象
class Pattern: ''' 由compile()返回的已编译正则表达式对象。 ''' def search(self, string, pos = 0, endpos = sys.maxsize): ''' :param pos: 字符串中开始搜索的位置索引,但是实现效果与string[pos:]不完全相等,例如'^'样式字符匹配字符串真正的开头和换行符后面的第一个字符,但不会匹配pos规定的开始位置。 :param endpos: 限定了字符串搜索的结束;如果endpos < pos,则无匹配。 :rtype: Match | None。 ''' # sample = re.compile(#TODO) # sample.search(string, 0, 100)等价于sample.search(string[:100], 0) pass def match(self, string, pos = 0, endpos = sys.maxsize): ''' :param pos: 字符串中开始搜索的位置索引,但是实现效果与string[pos:]不完全相等,例如'^'样式字符匹配字符串真正的开头和换行符后面的第一个字符,但不会匹配pos规定的开始位置。 :param endpos: 限定了字符串搜索的结束;如果endpos < pos,则无匹配。 :rtype: Match | None。 ''' pass def fullmatch(self, string, pos = 0, endpos = sys.maxsize): ''' :param pos: 字符串中开始搜索的位置索引,但是实现效果与string[pos:]不完全相等,例如'^'样式字符匹配字符串真正的开头和换行符后面的第一个字符,但不会匹配pos规定的开始位置。 :param endpos: 限定了字符串搜索的结束;如果endpos < pos,则无匹配。 :rtype: Match | None。 ''' pass def split(self, string, maxsplit = 0): pass def findall(self, string, pos = 0, endpos = sys.maxsize): pass def finditer(self, string, pos = 0, endpos = sys.maxsize): pass def sub(self, repl, string, count = 0): pass
正则表达式匹配对象
class Match: ''' 由成功的match()和search()所返回的匹配对象。 ''' def expand(self, template): ''' 获取通过在字符串template上执行反斜杠替换所获得的字符串。 转义符例如'\n'将被转换为适当的字符,而数字反向引用'\1'、'\2'和命名反向 引用'\g<1>'、'\g<name>'将被替换为相应分组的内容。 :type template: str。 :rtype: str。 ''' pass def group(self, [group1, ...]): ''' 获取匹配后的子组合。 如果只有一个参数,结果是一个字符串; 如果有多个参数,结果是一个元组(每个参数对应一个项); 如果没有参数,第一个组合默认0(整个匹配都被返回)。 :rtype: str | tuple。 ''' pass def groups(self): ''' 返回一个元组,包含所有匹配的子组,在样式中出现的从1到任意多的组合。 一般等效为通过group()方法读取了所有合法参数的返回值组合得到的元组。 ''' def groupdict(self): ''' 返回一个字典,包含所有的命名子组合,不包含任何未命名子组合。 ''' def start(self, [group]): ''' 返回group匹配到的字串的开始标号(闭区间)。group默认0(整个匹配的子串)。 如果group存在,但未产生匹配,就返回-1。 ''' def end(self, [group]): ''' 返回group匹配到的字串的结束标号(开区间)。group默认0(整个匹配的子串)。 如果group存在,但未产生匹配,就返回-1。 ''' def span(self, [group]): ''' 对于匹配result,返回二元组(result.start(group), result.end(group))。group默认为0,即整个匹配。 如果group没有在这个匹配中,则返回(-1, -1)。 '''
group()方法
- 如果一个组合参数值为,相应的返回值就是整个匹配字符串;
- 如果一个组合参数值属于范围,结果就是相应的括号组字符串;
- 如果一个组合参数值是负数,或者大于样式中定义的组数,将引发一个
IndexError
异常; - 如果一个组合包含在样式的一部分,并被匹配多次,将返回最后一个匹配。
result = search(r'([a-zA-Z][a-zA-Z0-9]+):(([a-zA-Z]+) = (\d+))+', 'main:a = 1b = 2') # main:a = 1b = 2。 print(result.group()) # main。 print(result.group(1)) # b = 2。 # 如果一个组合包含在样式的一部分,并被匹配多次,将返回最后一个匹配。 print(result.group(2)) # ('main', 'b')。 print(result.group(1, 3)) # ('main:a = 1b = 2', 'main', 'b = 2', 'b', '2')。 print(result.group(0, 1, 2, 3, 4)) # IndexError: no such group。 print(result.group(0, 1, 2, 3, 4, 5)) # search()只匹配第一个匹配项。 result = search(r'((?P<param>[a-zA-Z]+) = (?P<data>\d+))', 'a = 1b = 2') # a = 1。 print(result.group()) # a 1。 print(result.group('param'), result.group('data'))
groups()和groupdict()方法
# search()只匹配第一个匹配项。 result = re.search(r'((\?)(?P<param>[a-zA-Z]+) = (?P<data>\d+))', '?a = 1?b = 2') # ('?a = 1', '?', 'a', '1')。 print(result.groups()) # {'param': 'a', 'data': '1'}。 print(result.groupdict())
start()、end()和span()方法
# search()只匹配第一个匹配项。 result = re.search(r'((\?)(?P<param>[a-zA-Z]+) = (?P<data>\d+))', '?a = 1?b = 2') # 0。 print(result.start()) # 6。 # 开区间。 print(result.end()) # (0, 6)。 # 左闭右开区间。 print(result.span())
对于一个匹配对象result
和一个组合batch
,组合batch
(等价于result.group(batch)
)产生的匹配是result.string[result.start(batch):result.end(batch)]
。
注意:如果batch
匹配一个空字符串,将有result.start(batch) == result.end(batch)
。
result = re.search(r'[a-z]+(\d?)', 'python') # 0 6 (0, 6)。 # group = 0表示匹配了整个字串。 print(result.start(0), result.end(0), result.span(0)) # 6 6 (6, 6)。 print(result.start(1), result.end(1), result.span(1)) # IndexError: no such group。 print(result.start(2), result.end(2), result.span(2))
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY