Skip to main content

字符串

info

将所有行限制在最多79个字符。对于文档字符串或注释等流动的长文本块,行长度应限制在72个字符。

愚蠢的一致性是小心眼的产物。风格指南的建议有时并不适用。当有疑问时,运用你的最佳判断。看看其他例子,决定什么看起来最好。不要犹豫,去问吧!

PEP 8 – Python代码风格指南

字符串

直接使用引号定义字符串是最常见的方式,定义字符串可以使用单引号、双引号、三重引号。可以在不同类型的引号内部包含其他引号。

当字符串内包含引号时,需要用不同的引号包裹转义字符

print("Hello, world!") # 双引号
print('Hello, world!') # 单引号
print("""Hello, world!""") # 三重引号
print('''Hello, world!''') # 三重引号

# 当你想要定义一个包含单引号的字符串时,可以用双引号包裹
print("It's a nice day!") # It's a nice day!

# 反之,当字符串包含双引号时,可以用单引号包裹
print('He said, "Hello!"') # He said, "Hello!"

# 对于多行字符串,三引号允许内部包含单双引号
print("""He said, "It's a nice day!" """) # He said, "It's a nice day!"

# 对于三引号字符串,和上面的包裹案例,也可以使用转义字符来定义
print("""We can use \"\"\" to define string """) # We can use """ to define string
print("He said, \"It's a nice day!\"") # He said, "It's a nice day!"
print("He said, \"Hello!\"") # He said, "Hello!"
print("It\'s a nice day!") # It's a nice day!

# 连续定义多个字符串(中间有无空格均可)触发字符串字面值合并
string = "1""2" '3'
print(string)
# 输出:
# 123
tip

字符串属于不可变序列对象。

不可变的,这意味着每次修改都会创建新字符串。

序列对象,表示拥有一些共性方法:

  • 加法拼接,返回新的字符串
  • 索引,返回单个字符
  • 切片,返回新的字符串
  • 可遍历,每次返回单个字符
  • 可使用in判断是否包含
  • 可使用len函数获取长度
s = 'good morning'
# 字符串的索引
s[0] # g
s[-2] # n

# 字符串的切片
s[-3:] # ing
s[:-3] # good morn
s[:] # good morning

# 字符串的拼接
s[:-3] + s[-3:] # good morning

# 遍历
for i in s:
print(i,end=' ')
# 输出:
# g o o d m o r n i n g

# in判断
'a' in 'abc' # True
'z' in 'abc' # False

# 使用len函数获取长度
len(s) # 13

# 需要注意避免在循环中使用 `+` 操作符(每次拼接都会创建新字符串)
# 推荐使用列表收集,最后使用`str.join()` 连接字符串。
# 低效方式
s = ""
for i in range(10000):
s += str(i) # 创建新字符串每次迭代

# 高效方式
parts = []
for i in range(10000):
parts.append(str(i))
s = "".join(parts)

# 进阶方式,流式写入
import io
s = io.StringIO()
for i in range(10000):
s.write(str(i))
s = s.getvalue()

str函数

str() 函数用于将其他类型的数据转换为字符串类型。这在需要将数字、列表或其他对象作为字符串处理时非常有用。

str函数签名:str(object='') -> str

参数说明:

  • object:要转换的对象,默认为空字符串

返回值:

  • 返回一个字符串
# 将整数转换为字符串
num = 123
str_num = str(num)
print(str_num) # 输出: '123'
print(type(str_num)) # 输出: <class 'str'>

# 将列表转换为字符串
lst = [1, 2, 3]
str_lst = str(lst)
print(str_lst) # 输出: '[1, 2, 3]'

# 将浮点数转换为字符串
float_num = 3.14
str_float = str(float_num)
print(str_float) # 输出: '3.14'

str() 函数会调用对象的 __str__() 方法来生成字符串表示形式。对于自定义类,可以重写 __str__() 来控制转换结果。

字符串插值

info

f-string提供了一种简洁、可读性强的字符串格式化方法。

它在运行时计算表达式,比传统的%格式化和str.format()更快、更直观。

而且支持调试表达式、函数调用。

name = "world"
print(f"Hello, {name}!") # Hello, world!

PEP 498 – 字面字符串插值

# 嵌入表达式
x = 10
y = 20
result = f"{x} add {y} is {x + y}"
print(result) # 输出: 10 add 20 is 30

# 支持函数、属性、方法的调用
name="jack"
info = f"{name}'s name first letter is {name[0].upper()} , the name length is {len(name)}"
print(info) # 输出: jack's name first letter is J , the name length is 4
info

模板字符串为开发者提供了在字符串组合前访问字符串及其插值值的能力。这为 Python 语言带来了原生的灵活字符串处理功能,并支持安全检查、Web 模板、特定领域语言等。

预计Python 3.14 支持模板字符串。

name = "World"
template = t"Hello {name}"
assert template.interpolations[0].value == "World"

PEP 750 – Template Strings

格式说明符

在 str.format() 和 f-strings 中,可以使用格式说明符来控制输出的格式。这对于数字、日期和字符串的精确格式化非常实用。

数字格式化

# 使用 f-strings时,可以直接将值写入,但是推荐先定义变量,代码可读性更好

# 保留两位整数,不足时左侧补零
print(f"{5:02d}") # 输出: 05
# 推荐写法:
value = 5
print(f"{value:02d}")

# 保留两位小数,不足末位补0
pi = 3.14159
print(f"{pi:.2f}") # 输出: 3.14

# 调试表达式,等价于print(f"var={var}")
var = 42
print(f"{var=}") # 输出: var=42


# 对于数字,用逗号或下划线作为组分隔符
data = 123456789
print(f'{data:,}') # 123,456,789
print(f'{data:_}') # 123_456_789

# 表示为百分数,保留两位小数
points = 19
total = 22
print(f'{points/total:.2%}') # 86.36%
# 等价于
print(f'{0.8636363636363636:.2%}') # 86.36%

# 对于数字,格式也支持转化为其他进制
num = 42
print(f"int: {num:d}; hex: {num:x}; oct: {num:o}; bin: {num:b}")
# 输出:int: 42; hex: 2a; oct: 52; bin: 101010

print(f"int: {num:d}; hex: {num:#x}; oct: {num:#o}; bin: {num:#b}")
# 输出:int: 42; hex: 0x2a; oct: 0o52; bin: 0b101010

# 总是显示符号
print(f'{3.14:+f}; {-3.14:+f}')
# 输出:+3.140000; -3.140000

# 对正数显示一个空格
print(f'{3.14: f}; {-3.14: f}')
# 输出: 3.140000; -3.140000

# 只显示负号 -- 等同于 '{:f}; {:f}'
print(f'{3.14:-f}; {-3.14:-f}')
# 输出:3.140000; -3.140000

文本对齐

# 为了便于观察,我在末尾添加了竖线|
hello = "hello|"
name = "bob|"
# 右对齐,表示字符串长度为10,不足时用空格填充
print("{:>10}".format(hello))
print("{:>10}".format(name))
# 输出:
# hello|
# bob|

# 居中对齐,表示字符串长度为10,不足时用空格填充
print("{:^10}".format(hello))
print("{:^10}".format(name))
# 输出:
# hello|
# bob|

# 居左对齐(默认),表示字符串长度为10,不足时用空格填充
print("{:<10}".format(hello))
print("{:<10}".format(name))
# 输出:
# hello|
# bob|

# 左右对齐
class Datebase:
url = '127.0.0.1'
password = '123456'

print(f"""
{"test url:":<20}{Datebase.url:>20}
{"test password:":<20}{Datebase.password:>20}
""")
# 输出:
# test url: 127.0.0.1
# test password: 123456
#

# 带有填充的对齐
# text表示文本,第一处align表示填充字符,第二处align表示对齐方式,16表示长度
for align, text in zip('<^>', ['left', 'center', 'right']):
print(f'{text:{align}{align}16}')
# 输出:
# left<<<<<<<<<<<<
# ^^^^^^center^^^^^
# >>>>>>>>>>right

使用特定类型的专属格式化

import datetime
d = datetime.datetime(2008, 12, 3, 12, 15, 58)
print('{:%Y-%m-%d %H:%M:%S}'.format(d)) # 2008-12-03 12:15:58
# # 2008-12-03 是Python3.0.0的发布日期。

Unicode 和转义字符

Python3 的字符就是Unicode字符。

info

Unicode 字符串类型已更改为支持多种内部表示,具体取决于字符的最大 Unicode 序数(1、2 或 4 字节)。这将允许在常见情况下使用空间高效的表示,但在所有系统上都能访问完整的 UCS-4。

PEP 393的灵活字符串表示

Unicode

  • bytes: Python 中的字节串类型,用于表示二进制数据。
  • ASCII: 最古老的编码标准,只包含128个字符。
  • Unicode: 一种字符编码标准,为世界上大多数字符分配唯一的代码点。
  • UTF-8: 全称 Unicode Transformation Format 8,向后兼容 ASCII?。是最常见的 Unicode 编码形式,UTF-8是变长编码,1-4字节表示一个Unicode码点。
  • 码表: 字符与编码的映射表,表头包含显示字符、编码值、描述。

下面的示例中,我刻意避免使用字符,为了不让你误解字符串的定义。

# 直接定义,表示Unicode的明文
s = "Hello, 世界!"
print(s) # 输出: Hello, 世界!

# 如果你的电脑无法安装任何输入法,那么你只能使用Unicode码点来表示非英文字符。
# 间接定义,表示Unicode码表的第4e16个文字和第754c个文字,小u后面接4位16进制的数字,即支持16-bit
u = 'Hello, \u4e16\u754c!'
print(u) # 输出: Hello, 世界!

# 如果你要表示的文字在码表中相当靠后,则可以使用大U定义,大U后面接8位16进制的数字,即支持32-bit
U = 'Hello, \U00004e16\U0000754c!'
print(U) # 输出: Hello, 世界!

# 单纯依靠十六进制码点来表示字符好比网站访问靠IP地址,非常不便。因此Unicode提供了名称来表示字符,就和网站域名一样。
# 你更愿意访问http://142.251.42.100:443/ 还是 https://www.baidu.com/ 呢?
# Unicode 名称此时此刻相当于某种助记符,比如雪人、欧元符号。
print('\N{SNOWMAN}\N{EURO SIGN}')
# 输出:☃€

# 编码为 bytes
b = s.encode('utf-8')
print(b) # 输出: b'Hello, \xe4\xb8\x96\xe7\x95\x8c!'

# 解码回字符串
decoded = b.decode('utf-8')
print(decoded) # 输出: Hello, 世界!

# 处理编码错误
try:
b.decode('ascii')
except UnicodeDecodeError as e:
print(f"Error: {e}") # 输出: Error: 'ascii' codec can't decode byte ...
tip

查询字符的名称可以使用unicodedata模块。但是注意,\N后面的内容不支持拼接显示。

import unicodedata

# 查询字符 '世界' 的 Unicode 名称(Unicode 名称是字符的官方名称,相当于字符的身份证号)
# 解读一下输出结果你就会发现,汉字的命名规则是:CJK UNIFIED IDEOGRAPH-XXXX,其中XXXX是字符的编码值。
print(unicodedata.name('世'),unicodedata.name('界'))
# 输出:CJK UNIFIED IDEOGRAPH-4E16 CJK UNIFIED IDEOGRAPH-754C
print('\N{CJK UNIFIED IDEOGRAPH-4E16}\N{CJK UNIFIED IDEOGRAPH-754C}')
# 输出:世界

# 会报错:因为\N不支持拼接显示,包含字面值合并、加法拼接、变量传递等
name = 'CJK UNIFIED IDEOGRAPH-4E16'
print(f'\N{name}')

转义字符

转义字符是一种在编程语言和标记语言中使用的特殊字符序列,它是一种语法层面的约定。官方翻译为转义序列。

比如你的键盘没有Tab键,那么你可以输入\t来表示Tab。

转义字符也是字符串的一部分,因此也属于Unicode字符。

部分转义字符直接对应ASCII码表中的字符?

tip

如果响铃符号在Vscode中不触发,可以试试在终端中体验。

下面写法是等价的。都代表 ASCII 码值为 7 的字符。

\a表示 ASCII 响铃字符的助记符(mnemonic escape sequence)。

它旨在让代码更具可读性,让人一眼就知道这个转义序列的意图是发出响铃。

print('\a') # 助记符
print('\x07') # 16进制
print('\7') # 8进制
print('\07') # 8进制
print('\007') # 8进制
print("\u0007") # Unicode 16进制
print("\U00000007") # Unicode 16进制
print('\N{BEL}') # 直接使用Unicode名称

与数字定义进制相比,字符串转义序列仅支持8进制、16进制,转义后指向码表中的字符。

8进制定义需要以\开头,不超过三位。最大值为\377

16进制定义时,需要以\x开头,不超过两位。最大值为\xff

转义字符往往以反斜杠\开头,下面的示例展示了转义字符、反转义字符的用法。

path_str = r'"c:\desktop\new"'
path = r'c:\desktop\new'
literal_bs = r"ABC\bD"
literal_ff = r"第一页内容\f第二页内容"
literal_nl = r"这一行\n下一行"
literal_cr = r"ABC\rD"
literal_tab = r"列1\t列2\t列3"
literal_vt = r"上\v下"
newline = r"PEP8 中推荐每行不超过79个字符,如果一个字符串过长,\
可以使用\换行。"

letter = f"""
亲爱的读者:

\t你好!这是一封用常见转义字符排版并介绍它们的信。

\t1) \\t ASCII 水平制表符(TAB)
\t 文本写法: "{literal_tab}"
\t 输出效果: 列1\t列2\t列3

\t2) \\n ASCII 换行符(LF)
\t 文本写法: "{literal_nl}"
\t 输出效果: 这一行
\t 下一行

\t3) \\f ASCII 换页符(FF)和\\v ASCII 垂直制表符(VT)
\t 文本写法: "{literal_ff}" 和"{literal_vt}"
\t 输出效果: 最早用于打印机换页和垂直定位,目前已经很少使用了,在一些终端/编辑器中表现和换行符一致。

\t4) \\r ASCII 回车符(CR)和 \\b ASCII 退格符(BS)
\t 文本写法: "{literal_cr}" 和 "{literal_bs}"
\t 输出效果: \\r将光标移到当前行的最开始,因此"ABC\\rD" 实际显示常为 "DBC"
\t 输出效果: \\b将光标移到当前行的前一个字符的位置,因此"ABC\\bD" 实际显示常为 "ABD"

\t5) \\<newline>表示斜杠后面接新的一行,会忽略换行。可用于长字符串的定义。
\t 文本写法: "{newline}"
\t 输出效果: PEP8 中推荐每行不超过79个字符,如果一个字符串过长,可以使用\换行。

\t6) 转义字符有时会添乱。\\ + \\ 表示一个反斜杠且不触发转义。但是长文本下我更推荐r来定义原始字符串。
\t 原始字符:{path_str}
\t 输出效果:c:\\desktop\new
\t 改进写法1:"c:\\\\desktop\\\\new" # 输出:{path}
\t 改进写法2:r"c:\\desktop\\new" # 输出:{path}

此致\n\t敬礼!
"""

print(letter)
# 输出:

"""
亲爱的读者:

你好!这是一封用常见转义字符排版并介绍它们的信。

1) \t ASCII 水平制表符(TAB)
文本写法: "列1\t列2\t列3"
输出效果: 列1 列2 列3

2) \n ASCII 换行符(LF)
文本写法: "这一行\n下一行"
输出效果: 这一行
下一行

3) \f ASCII 换页符(FF)和\v ASCII 垂直制表符(VT)
文本写法: "第一页内容\f第二页内容" 和"上\v下"
输出效果: 最早用于打印机换页和垂直定位,目前已经很少使用了,在一些终端/编辑器中表现和换行符一致。

4) \r ASCII 回车符(CR)和 \b ASCII 退格符(BS)
文本写法: "ABC\rD" 和 "ABC\bD"
输出效果: \r将光标移到当前行的最开始,因此"ABC\rD" 实际显示常为 "DBC"
输出效果: \b将光标移到当前行的前一个字符的位置,因此"ABC\bD" 实际显示常为 "ABD"

5) \<newline>表示斜杠后面接新的一行,会忽略换行。可用于长字符串的定义。
文本写法: "PEP8 中推荐每行不超过79个字符,如果一个字符串过长,\
可以使用\换行。"
输出效果: PEP8 中推荐每行不超过79个字符,如果一个字符串过长,可以使用\换行。

6) 转义字符有时会添乱。\ + \ 表示一个反斜杠且不触发转义。但是长文本下我更推荐r来定义原始字符串。
原始字符:"c:\desktop\new"
输出效果:c:\desktop
ew
改进写法1:"c:\\desktop\\new" # 输出:c:\desktop\new
改进写法2:r"c:\desktop\new" # 输出:c:\desktop\new

此致
敬礼!
"""

字符串附加方法

Python中字符串类型的附加方法相当多。你可以根据自己的节奏选择学习。

内置函数

ord函数、chr函数

ord() 函数和 chr() 函数是Python中用于处理字符串和Unicode字符的函数。

ord函数签名:ord(character) -> int

参数说明:

  • character:要转换的字符

返回值:

  • 返回字符的Unicode码值

chr函数签名:chr(integer) -> str

参数说明:

  • integer:要转换的整数

返回值:

  • 返回整数对应的字符
print(ord('A')) # 65
print(chr(65)) # A

print(ord('Z')) # 90
print(chr(90)) # Z

for _ in range(ord('A'), ord('Z')+1):
print(chr(_),end=' ')
# 输出:A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

bin函数、oct函数、hex函数

这一组函数用于将整数转换为二进制、八进制、十六进制字符串

bin函数签名:bin(number) -> str

oct函数签名:oct(number) -> str

hex函数签名:hex(number) -> str

参数说明:

  • number:要转换的整数

返回值:

  • bin函数返回二进制字符串,以'0b'开头
  • oct函数返回八进制字符串,以'0o'开头
  • hex函数返回十六进制字符串,以'0x'开头

如果想要转换回去,可以使用int()函数,并指定进制。

num = 10
bin_num = bin(num)
print(bin_num) # 0b1010
print(type(bin_num)) # <class 'str'>
oct_num = oct(num)
print(oct_num) # 0o12
print(type(oct_num)) # <class 'str'>
hex_num = hex(num)
print(hex_num) # 0xa
print(type(hex_num)) # <class 'str'>

num_bin = int(bin_num,2)
num_oct = int(oct_num,8)
num_hex = int(hex_num,16)
print(num_bin) # 10
print(type(num_bin)) # <class 'int'>
print(num_oct) # 10
print(type(num_oct)) # <class 'int'>
print(num_hex) # 10
print(type(num_hex)) # <class 'int'>

bytes函数、bytearray函数

bytes() 函数和 bytearray() 函数用于将字符串转换为字节串

bytes函数签名:bytes(string, encoding='utf-8', errors='strict')

bytearray函数签名:bytearray(string, encoding='utf-8', errors='strict')

参数说明:

  • string:要转换的字符串
  • encoding:字符串的编码方式,默认为'utf-8'
  • errors:错误处理方式,默认为'strict'

返回值:

  • bytes函数返回bytes对象,bytearray函数返回bytearray对象

当你索引或迭代一个字符串时,你得到的是一个 str 类型的单个字符。

当你索引或迭代一个字节序列时,你得到的是一个 int 类型,表示该字节的数值(0 到 255)

s = 'Hello, 世界!'
bytes_s = bytes(s,encoding='utf-8')
print(bytes_s) # b'Hello, \xe4\xb8\x96\xe7\x95\x8c!'
print(type(bytes_s)) # <class 'bytes'>
# bytes 属于不可变对象

bytearray_s = bytearray(s,encoding='utf-8')
print(bytearray_s) # bytearray(b'Hello, \xe4\xb8\x96\xe7\x95\x8c!')
print(type(bytearray_s)) # <class 'bytearray'>
# bytearray 属于可变对象,适合原地修改
tip
  • 位 (bit):计算机中存储数据的最小单位,只能表示 0 或 1。
  • 字节 (byte):计算机中处理数据的最小单位,等于 8 个位。
  • b'...':在 Python 中,这表示一个字节串,用于处理原始的二进制数据。

如果你尝试用普通字符串模式 ('r') 去读取一个非文本文件?,Python 就会报错,因为它会试图将这些原始字节解释成文本,但很多字节序列在文本编码中是无效的。

# 假设我们有一个图片文件叫 'images.webp'
# 计算机读取这个文件时,读取的是原始的字节数据
# 我们可以用 'rb' 模式来模拟这个过程,'r' 表示读取,'b' 表示以二进制(bytes)模式读取

# 报错
with open('images.webp', 'r') as f: pass

# 正确
try:
with open('images.webp', 'rb') as f:
# data 变量现在是一个字节串 (bytes)
data = f.read()

print(f"文件大小:{len(data)} 字节")
print(f"数据类型:{type(data)}")

# 打印部分字节数据,你会看到它不是人类可读的字符串
# 而是十六进制的原始数据,这就是图片文件的本质
print(f"部分字节数据(前20个):{data[:20]}")

except FileNotFoundError:
print("找不到 images.webp 文件,请确保文件存在。")

# 输出:
# 文件大小:12345 字节
# 数据类型:<class 'bytes'>
# 部分字节数据(前20个):b'RIFF\x86\xba\x00\x00WEBPVP8 z\xba\x00\x00'

ascii函数

当你在处理需要与其他系统或语言(特别是那些只支持 ASCII 的系统)交互的数据时,ascii() 可以确保你的数据表示形式是兼容的。

因为ascii() 的输出结果是完全由 ASCII 字符组成的,因此可以在任何只支持 ASCII 的环境中安全地传输和存储。

ascii函数签名:ascii(object) -> str

参数说明:

  • object:要转换的对象

返回值:

  • 返回对象的ASCII表示
data = {'name': '张三', 'age': 30}
# ascii(data) 将所有非 ASCII 字符转义
print(ascii(data))
# 输出:{'name': '\\u5f20\\u4e09', 'age': 30}

repr函数

repr() 函数返回一个明确的、可用于重新创建对象的表达式。通常调用对象的 __repr__() 方法。

repr函数签名:repr(object) -> str

参数说明:

  • object:要转换的对象

返回值:

  • 返回对象的repr表示
# 普通打印无法区分数字和字符串
print(1,"1") # 1 1

# repr 可以区分数字和字符串,输出内容可以用于重新创建对象
print(repr(1),repr("1"))# 1 '1'

format函数

format() 函数用于格式化字符串。它早于f-string出现,是传统的字符串格式化方法。

支持的格式化方式与f-string类似,但语法稍有不同,且不支持f-string的调试表达式(如f{var=})、表达式内的函数、方法调用(如f{len(var)})。

format函数签名:format(value, format_spec) -> str

参数说明:

  • value:要格式化的值
  • format_spec:格式化字符串

返回值:

  • 返回格式化后的字符串
print(format(123.456, '.2f')) # 123.46
print(format(123.456, '.2%')) # 12345.60%
print(format(123.456, '.2e')) # 1.23e+02

相关库推荐

  • io.StringIO:把内存中的一个字符串,当成一个文件来处理。
info

假设你有一个函数,它要求传入一个文件对象作为参数,但你并不想真的在磁盘上创建一个文件。这时,你可以用 io.StringIO 创建一个内存中的“假文件”。

import io

def process_file(file_object):
"""一个需要文件对象作为参数的函数"""
content = file_object.read()
# 假设这里对文件内容进行了一些处理
return content.upper()

# 使用 io.StringIO 创建一个内存中的文件
text_data = "hello world from memory"
string_file = io.StringIO(text_data)

# 将内存中的文件对象传递给函数
result = process_file(string_file)

print(result)
# 输出:HELLO WORLD FROM MEMORY