一、str对象

1. str对象的设计意图

(1)核心定义

str是定义在Index 或 Series上的属性,专门用于批量处理序列中每个元素的文本内容,内置了大量文本处理方法,是 Pandas 处理文本数据的核心入口。

(2)设计逻辑

  • 对标 Python 内置str模块:Pandas 的str对象大量复用了 Python 原生字符串方法的命名与功能,降低学习成本。
    • 原生 Python:str.upper('abcd') → 输出'ABCD'
    • Pandas 中:pd.Series(['abcd','efg','hi']).str.upper() → 批量对每个元素执行大写转换,输出 Series
  • 方法兼容性:Pandas 的 50 个str对象方法中,有 31 个与 Python 标准库str方法同名且功能完全一致,是批量处理文本序列的核心工具。

2. [] 索引器

str对象支持和 Python 原生字符串完全一致的[]索引与切片语法,实现对每个元素的字符级操作,同时自动处理越界问题。

(1)基础索引

  • 原生字符串:var = 'abcd'var[0]'a'
  • Pandas str对象:s = pd.Series(['abcd','efg','hi'])s.str[0]
    • 输出:0 a\n1 e\n2 h\ndtype: object
    • 特性:越界索引自动返回缺失值NaN,例如s.str[2]中,长度为 2 的'hi'会返回NaN

(2)切片操作

  • 原生字符串:var[-1:0:-2] → 从末尾到开头,步长 - 2,输出'db'
  • Pandas str对象:s.str[-1:0:-2] → 批量对每个元素执行相同切片
    • 输出:0 db\n1 g\n2 i\ndtype: object

3.string 类型

string类型是专门为字符串设计的存储类型,和数值型、category类型并列,实现更规范的文本数据管理。

(1)object vs string 差异

对比维度 object 类型 string 类型
索引逻辑 直接对元素本身做[]索引(列表取第 2 个元素、字典取 key 对应值) 先把整个元素转为字面字符串,再对字符串做字符级索引
空值支持 非 Nullable 类型,缺失值用np.nan,数值结果会转为float64 Nullable 类型,缺失值用<NA>,数值结果保持Int64(整数型)
布尔结果 缺失值会转为False,类型为bool 缺失值保持<NA>,类型为boolean(Nullable 布尔型)
适用场景 混合类型序列 纯文本序列(推荐)

(2)典型示例

(a)索引逻辑差异
# 混合类型object序列
s = pd.Series([{1: 'temp_1', 2: 'temp_2'}, ['a', 'b'], 0.5, 'my_string'])
# object类型str索引:按元素本身结构取
s.str[1]
# 输出:0    temp_1\n1      b\n2    NaN\n3      y\ndtype: object

# 转为string类型后:先转字符串再索引
s.astype('string').str[1]
# 输出:0    1\n1    '\n2    .\n3    y\ndtype: string
  • 差异原因:object直接对元素(字典 / 列表 / 数字)做索引;string先把元素转为字符串(如字典{1: 'temp_1'}转为"{1: 'temp_1', 2: 'temp_2'}"),再取第 1 个字符。
(b)空值处理差异
# 带缺失值的序列
s = pd.Series(['a', np.nan])

# object类型:len返回float64,缺失值为NaN
s.str.len()
# 输出:0    1.0\n1    NaN\ndtype: float64

# string类型:len返回Int64(Nullable整数),缺失值为<NA>
s.astype('string').str.len()
# 输出:0       1\n1    <NA>\ndtype: Int64

# 比较操作差异
s == 'a'
# 输出:0     True\n1    False\ndtype: bool

s.astype('string') == 'a'
# 输出:0    True\n1    <NA>\ndtype: boolean

注:

  1. 数值序列不能直接用 str 属性全数值的object/category序列,禁止直接调用.str,必须先通过astype('string')转为字符串类型,再执行文本操作:

    s = pd.Series([12, 345, 6789])
    # 报错:AttributeError
    # s.str[1]
    # 正确写法:先转string类型
    s.astype('string').str[1]
    # 输出:0    2\n1    4\n2    7\ndtype: string
    
  2. str 属性的使用前提序列中至少有一个可迭代对象(字符串、列表、字典等),否则无法调用.str属性。

  3. string 类型的推荐场景处理纯文本数据时,优先使用string类型,避免object类型的混合类型隐患,同时获得更规范的 Nullable 空值处理。

二.正则表达式基础

1. 一般字符的匹配

正则表达式(Regex)是一种按指定模式匹配字符串内容的工具,核心逻辑是「从左到右匹配」,常用于文本提取、替换、校验等场景。

  • 演示工具:Python re 模块的 findall 函数,作用是匹配所有符合模式、不重叠的子串,返回列表。
  • 基础示例:匹配字符串中的 Apple
    import re
    re.findall(r'Apple', 'Apple! This Is an Apple!')
    # 输出:['Apple', 'Apple']
    
  • 注意:正则表达式前加 r 表示原生字符串,避免转义符冲突,是 Python 中的最佳实践。

2.元字符基础

元字符是正则中具有特殊含义的符号,是构建匹配规则的基础,完整对照表如下:

元字符

描述

示例

运行结果/说明

.

匹配除换行符以外的任意字符

re.findall(r'.', 'abc')

['a', 'b', 'c']

[]

字符类,匹配方括号中包含的任意字符

re.findall(r'[ac]', 'abc')

['a','c']

[^ ]

否定字符类,匹配方括号中不包含的任意字符

re.findall(r'[^ac]', 'abc')

['b']

*

匹配前面的子表达式零次或多次(贪婪匹配)

re.findall(r'a*b', 'b aaab ab')

['b', 'aaab', 'ab']

+

匹配前面的子表达式一次或多次(贪婪匹配)

re.findall(r'a+b', 'b aaab ab')

['aaab', 'ab']

?

匹配前面的子表达式零次或一次;或用于开启非贪婪匹配

re.findall(r'a?b', 'b aaab ab a')

['b', 'b', 'ab', 'b', 'a']

{n,m}

花括号,匹配前面字符至少 n 次,不超过 m 次

re.findall(r'a{2,4}', 'aa aaa aaaa aaaaa')

['aa', 'aaa', 'aaaa', 'aaaa']

(xyz)

字符组(分组),按照确切的顺序匹配字符 xyz

re.findall(r'(abc)', 'abc 123')

['abc']

|

分支结构,匹配符号左边右边的子表达式(多选一)

re.findall(r'aaa|bbb', 'aaaabbbb')

['aaa','bbb']

\​

转义符,还原元字符原本的字面含义(如匹配.需写成\.

re.findall(r'a\\?|a\*', 'aa?a*a')

['a', 'a', 'a','a']

^

匹配行的开始

re.findall(r'^hello', 'hello world\nhello')

['hello']

$

匹配行的结束

re.findall(r'bye$', 'hello bye bye world')

['bye']

  1. 贪婪与非贪婪*+{n,m} 默认是贪婪匹配(尽可能长),加 ? 变为非贪婪(尽可能短)。
  2. 字符类的特殊规则[] 内的元字符会失去特殊含义(如 . 仅表示点号),- 可表示范围(如 [a-z] 匹配小写字母)。
  3. 转义符的使用:匹配 .*? 等元字符本身时,必须加 \ 转义(如 r'\.' 匹配点号)。
impore re
print(re.findall(r'.', 'abc'))  # ['.'] 任意字符
print(re.findall(r'[ac]', 'abc'))  # ['a','c'] 字符类
print(re.findall(r'[^ac]', 'abc'))  # ['b'] 否定字符类
print(re.findall(r'[ab]{2}', 'aaaabbbb'))  # ['aa','aa','bb','bb'] 次数匹配
print(re.findall(r'aaa|bbb', 'aaaabbbb'))  # ['aaa','bbb'] 分支匹配

3.简写字符集(常用快捷语法)

简写字符集是常用字符类的缩写,大幅简化正则写法,完整对照表如下:

简写 描述 等价写法
\w 匹配所有字母、数字、下划线 [a-zA-Z0-9_]
\W 匹配非字母、数字、下划线的字符 [^\w]
\d 匹配数字 [0-9]
\D 匹配非数字 [^\d]
\s 匹配空白字符(空格、制表符、换行等) [\t\n\f\r\p{Z}]
\S 匹配非空白字符 [^\s]
\B 匹配非单词边界(非空字符开头 / 结尾的位置,不匹配具体字符) -
import re
# 1. 匹配包含is的子串(\s匹配空白,.匹配任意字符)
re.findall(r'\sis', 'Apple! This Is an Apple!')
# 输出:['is', 'Is']

# 2. 匹配2个连续的\w(字母/数字/下划线)
re.findall(r'\w{2}', '09 8? 7w c_ 9q p@')
# 输出:['09', '7w', 'c_', '9q']

# 3. 匹配\w+\W\B(单词+非单词字符+非单词边界)
re.findall(r'\w\W\B', '09 8? 7w c_ 9q p@')
# 输出:['8?', 'p@']

# 4. 匹配空白+任意字符
re.findall(r'\s.', 'Constant dropping wears the stone.')
# 输出:[' d', ' w', ' t', ' s', ' t']

1. 匹配逻辑优先级

  • 字符组 () > 数量词 */+/?/{} > 分支 | > 锚点 ^/$
  • 分支 |短路匹配:从左到右匹配,一旦匹配成功就不再尝试右侧规则。

2. 常见使用误区

  • 忘记加 r 原生字符串:导致 \n\t 等被 Python 解析为转义字符,而非正则语法。
  • 混淆贪婪 / 非贪婪匹配:r'a.*b 会匹配最长的 a...br'a.*?b 匹配最短的。
  • 字符类中误用元字符:[.*] 仅匹配 .*,而非任意字符多次。

三、文本处理的五类操作

1. 拆分

(1)核心方法 str.split

  • 功能:按正则表达式拆分字符串序列,支持控制拆分次数、展开为多列。

  • 核心参数

    • pat:第一个参数,正则表达式分隔符
    • n:最大拆分次数(从左到右),默认 - 1(全部分拆)
    • expand:是否展开为 DataFrame 多列,默认False(返回列表 Series)
  • 示例演示

    import pandas as pd
    s = pd.Series(['上海市黄浦区方浜中路249号', '上海市宝山区密山路5号'])
    # 1. 全部分拆,返回列表Series
    print(s.str.split('[市区路]'))
    # 输出:
    # 0    [上海, 黄浦, 方浜中, 249号]
    # 1       [上海, 宝山, 密山, 5号]
    # dtype: object
    
    # 2. 限制拆分2次,展开为DataFrame
    print(s.str.split('[市区路]', n=2, expand=True))
    # 输出:
    #      0    1            2
    # 0  上海  黄浦  方浜中路249号
    # 1  上海  宝山    密山路5号
    

(2)相似方法 str.rsplit

  • 区别n参数从右到左限制拆分次数,和split方向相反。
  • 注意:当前版本rsplit存在 bug,不支持正则表达式分割,仅支持普通字符串分隔符。
    # 正则分隔符在rsplit中失效,直接返回原字符串
    s.str.rsplit('[市区路]', n=2, expand=True)
    

2.合并操作

(1)str.join:序列内元素合并

  • 功能:用指定连接符,将 Series 中每个元素的列表 / 可迭代对象拼接为字符串。
  • 规则:列表中出现非字符串元素(如数字),直接返回NaN
    s = pd.Series([['a','b'], [1, 'a'], [['a', 'b'], 'c']])
    print(s.str.join('-'))
    # 输出:
    # 0    a-b
    # 1    NaN
    # 2    NaN
    # dtype: object
    

(2)str.cat:多序列横向合并

  • 功能:合并两个 Series,按索引对齐拼接字符串。

  • 核心参数

    • sep:连接符
    • join:连接方式(left/right/outer/inner,默认left左连接)
    • na_rep:缺失值替代符号
  • 示例演示

    s1 = pd.Series(['a','b'])
    s2 = pd.Series(['cat','dog'])
    # 1. 默认左连接,索引对齐
    print(s1.str.cat(s2, sep='-'))
    # 输出:
    # 0    a-cat
    # 1    b-dog
    # dtype: object
    
    # 2. 外连接,用?填充缺失值
    s2.index = [1, 2]
    print(s1.str.cat(s2, sep='-', na_rep='?', join='outer'))
    # 输出:
    # 0    a-?
    # 1    b-cat
    # 2    ?-dog
    # dtype: object
    

3.匹配操作

(1)布尔型匹配(返回 True/False Series)

方法 功能 是否支持正则 核心示例
str.contains 判断字符串是否包含指定正则模式 支持 s.str.contains('\swat'):匹配包含空格 + at 的字符串
str.startswith 判断字符串是否以指定前缀开头 不支持 s.str.startswith('my'):匹配以 my 开头的字符串
str.endswith 判断字符串是否以指定后缀结尾 不支持 s.str.endswith('t'):匹配以 t 结尾的字符串
str.match 判断字符串起始处是否匹配正则 支持 `s.str.match('mh')`:匹配以 m/h 开头的字符串
s = pd.Series(['my cat', 'he is fat', 'railway station'])
print(s.str.contains('\swat'))  # 匹配空格+at
# 输出:0 True, 1 True, 2 False

print(s.str.startswith('my'))    # 匹配my开头
# 输出:0 True, 1 False, 2 False

print(s.str.endswith('t'))       # 匹配t结尾
# 输出:0 True, 1 True, 2 False

print(s.str.match('m|h'))        # 匹配m/h开头
# 输出:0 True, 1 True, 2 False
  • 等价写法:用str.contains+^/$实现startswith/endswith的正则版本

    # 等价于str.match('m|h')
    s.str.contains('^[m|h]')
    # 等价于反转后匹配结尾(s.str[::-1].str.match('ta[f|g]|n'))
    s.str.contains('[f|g]at|n$')
    

(2)索引型匹配(返回位置索引)

方法 功能 是否支持正则 示例
str.find 返回从左到右第一次匹配子串的索引,未找到返回 - 1 仅支持普通字符串 s.str.find('apple') → 11
str.rfind 返回从右到左第一次匹配子串的索引,未找到返回 - 1 仅支持普通字符串 s.str.rfind('apple') → 33
s = pd.Series(['This is an apple. That is not an apple.'])
print(s.str.find('apple'))   # 左起第一个apple的索引:11
print(s.str.rfind('apple'))  # 右起第一个apple的索引:33

4.替换

(1)正则替换

  • 关键区分str.replace(文本专用)和 DataFrame.replace(值替换)是完全不同的函数,文本替换必须用前者。

  • 核心参数

    • pat:正则表达式 / 待替换字符串
    • repl:替换的目标内容(支持字符串或自定义函数)
    • regex:是否启用正则,默认True(Pandas 1.2+)
  • 基础示例

    import pandas as pd
    s = pd.Series(['a_1_b','c_?'])
    # 替换数字\d或问号?为'new'
    s.str.replace('\d|\?', 'new', regex=True)
    
    ###
    0    a_new_b
    1      c_new
    dtype: object

(2)子组 + 自定义函数替换

当需要对不同部分做差异化替换时,用 ** 正则子组(圆括号)** 提取内容,再传入自定义函数处理。

  • group(k):代表匹配到的第k个子组(圆括号包裹的内容)

示例:中文地址转英文

s = pd.Series([
    '上海市黄浦区方浜中路249号',
    '上海市宝山区密山路5号',
    '北京市昌平区北农路2号'
])

# 正则子组:(市名)(区名)(路名)(编号)
pat = '(\w+市)(\w+区)(\w+路)(\d+号)'

# 映射字典
city = {'上海市': 'Shanghai', '北京市': 'Beijing'}
district = {
    '昌平区': 'CP District',
    '黄浦区': 'HP District',
    '宝山区': 'BS District'
}
road = {
    '方浜中路': 'Mid Fangbin Road',
    '密山路': 'Mishan Road',
    '北农路': 'Beinong Road'
}

# 自定义替换函数
def my_func(m):
    str_city = city[m.group(1)]
    str_district = district[m.group(2)]
    str_road = road[m.group(3)]
    # 编号去掉末尾的"号",加No.
    str_no = 'No. ' + m.group(4)[:-1]
    return ' '.join([str_city, str_district, str_road, str_no])

# 执行替换
s.str.replace(pat, my_func, regex=True)

输出:

0    Shanghai HP District Mid Fangbin Road No. 249
1        Shanghai BS District Mishan Road No. 5
2       Beijing CP District Beinong Road No. 2
dtype: object

(3)命名子组

(?P<名称>模式)给子组命名,让代码更易读、可维护性更强:

# 命名子组正则
pat = '(?P<市名>\w+市)(?P<区名>\w+区)(?P<路名>\w+路)(?P<编号>\d+号)'

def my_func(m):
    # 用子组名替代数字索引
    str_city = city[m.group('市名')]
    str_district = district[m.group('区名')]
    str_road = road[m.group('路名')]
    str_no = 'No. ' + m.group('编号')[:-1]
    return ' '.join([str_city, str_district, str_road, str_no])

s.str.replace(pat, my_func, regex=True)

输出和数字子组完全一致,代码可读性大幅提升。

5.提取

提取是带捕获的匹配,可以从文本中精准提取目标字段,是文本特征工程的核心工具。

(1)str.extract:单次匹配

  • 功能:每个字符串只匹配第一个符合模式的内容,返回 DataFrame,每列对应一个子组。

  • 示例:提取地址中的市、区、路、编号

    pat = '(\w+市)(\w+区)(\w+路)(\d+号)'
    s.str.extract(pat)
    

    输出:

    0 1 2 3
    0 上海市 黄浦区 方浜中路 249 号
    1 上海市 宝山区 密山路 5 号
    2 北京市 昌平区 北农路 2 号
  • 命名子组版本:直接给 DataFrame 列命名

    pat = '(?P<市名>\w+市)(?P<区名>\w+区)(?P<路名>\w+路)(?P<编号>\d+号)'
    s.str.extract(pat)
    

    输出:

    市名 区名 路名 编号
    0 上海市 黄浦区 方浜中路 249 号
    1 上海市 宝山区 密山路 5 号
    2 北京市 昌平区 北农路 2 号

(2)str.extractall:全量匹配

  • 功能:匹配字符串中所有符合模式的内容,返回多级索引 DataFrame(第一层是原 Series 索引,第二层是匹配序号)。

  • 示例:提取字符串中的多组数字

    s = pd.Series(['A135T15,A26S5','B674S2,B25T6'], index = ['my_A','my_B'])
    # 正则:匹配A/B开头+数字+T/S+数字
    pat = '[A|B](\d+)[T|S](\d+)'
    s.str.extractall(pat)
    

    输出:

    0 1
    my_A 0 135 15
    1 26 5
    my_B 0 674 2
    1 25 6
  • 命名子组版本:给列命名

    pat_with_name = '[A|B](?P<name1>\d+)[T|S](?P<name2>\d+)'
    s.str.extractall(pat_with_name)
    

    输出:

    name1 name2
    my_A 0 135 15
    1 26 5
    my_B 0 674 2
    1 25 6

(3)str.findall:列表式全量匹配

  • 功能:和str.extractall类似,但返回列表 Series,每个元素是该字符串的所有匹配结果(元组形式)。
  • 示例
    s.str.findall(pat)
    
    输出:
    my_A    [(135, 15), (26, 5)]
    my_B    [(674, 2), (25, 6)]
    dtype: object
    
  1. str.replace vs replace:绝对不要混淆!DataFrame.replace是值替换,不支持正则子组和函数替换,文本处理必须用str.replace
  2. 正则转义:如果要匹配.*?等元字符,必须加\转义,或设置regex=False
  3. 子组数量str.extract的列数等于正则中子组的数量,无捕获组会报错。
  4. 多级索引处理str.extractall返回的多级索引,可用.reset_index()展平为普通 DataFrame

四.常用字符串函数

1.字母型函数

核心函数对照表

函数 功能 示例输入 示例输出
str.upper() 全部转为大写 ['lower', 'CAPITALS', 'this is a sentence', 'SwApCaSe'] ['LOWER', 'CAPITALS', 'THIS IS A SENTENCE', 'SWAPCASE']
str.lower() 全部转为小写 同上 ['lower', 'capitals', 'this is a sentence', 'swapcase']
str.title() 每个单词首字母大写,其余小写 同上 ['Lower', 'Capitals', 'This Is A Sentence', 'Swapcase']
str.capitalize() 第一个字符大写,其余小写 同上 ['Lower', 'Capitals', 'This is a sentence', 'Swapcase']
str.swapcase() 大小写反转(大写转小写,小写转大写) 同上 ['LOWER', 'capitals', 'THIS IS A SENTENCE', 'sWaPcAsE']

关键区别

  • title():按单词拆分,每个单词首字母大写(适合标题、名称)
  • capitalize():仅整个字符串的第一个字符大写(适合句子开头)
  • swapcase():完全反转大小写,无其他格式修改

2.数值型函数

pd.to_numeric() str对象方法,但文本处理中高频使用,用于将字符串格式的数值转为真正的数值类型,同时处理非法值。

核心参数

  • errors:非数值的处理模式,3 个核心选项:
    • raise:遇到无法转换的值直接报错(默认)
    • coerce:无法转换的值设为NaN(缺失值)
    • ignore:保留原字符串,不转换
  • downcast:数值类型向下转换(如float64int64,可选)
import pandas as pd
s = pd.Series(['1', '2.2', '2e', '??', '-2.1', '0'])

# 1. errors='ignore':保留无法转换的原字符串
print(pd.to_numeric(s, errors='ignore'))
# 输出:
# 0      1
# 1    2.2
# 2     2e
# 3     ??
# 4   -2.1
# 5      0
# dtype: object

# 2. errors='coerce':无法转换的设为NaN
print(pd.to_numeric(s, errors='coerce'))
# 输出:
# 0    1.0
# 1    2.2
# 2    NaN
# 3    NaN
# 4   -2.1
# 5    0.0
# dtype: float64

# 3. 数据清洗:快速筛选出非数值的行
print(s[pd.to_numeric(s, errors='coerce').isna()])
# 输出:
# 2    2e
# 3    ??
# dtype: object

3.统计型函数 

 (1) 计数 str.count()

  • 功能:统计字符串中正则模式出现的次数
  • 示例:
    s = pd.Series(['cat rat fat at', 'get feed sheet heat'])
    s.str.count('[r|f]at|ee')
    # 输出:
    # 0    2
    # 1    2
    # dtype: int64
    

(2)长度str.len()

  • 功能:返回每个字符串的字符长度(含空格、符号)
  • 示例:
    s.str.len()
    # 输出:
    # 0    14
    # 1    19
    # dtype: int64
    

4.格式型函数

(1)除空型:去除空格

函数 功能 示例输入 处理后长度
str.strip() 去除两侧空格 [' col1', 'col2 ', ' col3 '] [4, 4, 4]
str.rstrip() 去除右侧空格 同上 [5, 4, 5]
str.lstrip() 去除左侧空格 同上 [4, 5, 5]
  • 适用场景:数据清洗(如列名、用户输入的首尾空格)、格式标准化

(2) 填充型:字符串补全

(a)通用填充:str.pad()
  • 功能:自定义填充方向、长度、填充字符
  • 参数:
    • width:目标长度
    • side:填充方向(left/right/both
    • fillchar:填充字符(默认空格)
  • 示例:
    s = pd.Series(['a','b','c'])
    # 左侧填充*,长度5
    s.str.pad(5, 'left', '*')  # 输出: ['****a', '****b', '****c']
    # 右侧填充*,长度5
    s.str.pad(5, 'right', '*') # 输出: ['a****', 'b****', 'c****']
    # 两侧填充*,长度5
    s.str.pad(5, 'both', '*')  # 输出: ['**a**', '**b**', '**c**']
    
(b)等价快捷函数
函数 等价pad写法 功能
str.rjust(width, fillchar) pad(width, 'left', fillchar) 右侧对齐(左侧填充)
str.ljust(width, fillchar) pad(width, 'right', fillchar) 左侧对齐(右侧填充)
str.center(width, fillchar) pad(width, 'both', fillchar) 居中对齐(两侧填充)

注意:ljust右侧填充,不是左侧填充,不要被名称误导!

(c)专用补 0:str.zfill()
  • 功能:专门用于左侧补 0,固定长度,完美解决证券代码、编号等前导 0 丢失问题
  • 示例:
    s = pd.Series([7, 155, 303000]).astype('string')
    # 3种等价写法,都输出6位字符串,左侧补0
    s.str.pad(6, 'left', '0')
    s.str.rjust(6, '0')
    s.str.zfill(6)
    # 输出:
    # 0    000007
    # 1    000155
    # 2    303000
    # dtype: string
    
  1. ljust/rjust方向误区ljust左对齐(右侧填充)rjust右对齐(左侧填充),不要按字面意思搞反。
  2. pd.to_numeric不是str方法:必须直接调用pd.to_numeric(series),不能写series.str.to_numeric()
  3. zfill仅补 0:如果需要填充其他字符,必须用pad/rjust/ljust
  4. str.count支持正则:可以统计复杂模式的出现次数,不只是单个字符。
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐