字符串
Python 中的字符串(string)是一系列字符的有序集合,可以包括字母、数字、标点符号和其他特殊字符。字符串是一个不可变的序列数据类型。
创建字符串
在 Python 中,较短的字符串,可以使用单引号 ' '、双引号 " " 来表示。跨越多行的长字符串,可以使用三引号 ''' ''' 或 """ """ 来表示。例如:
string1 = 'Hello, World!'
string2 = "Hello, World!"
string3 = '''Hello
World!''' # 这是一个多行字符串
string4 = """Another
multi-line
string."""
三引号包裹的大段文字也经常被用来作为程序的注释,这样就不需要在每行的注释前面都加上一个井号了,相当于 C、Java 等语言中的 /* */ 注释符号。
一个小技巧是,如果需要创建的字符串中包含双引号,那么就用单引号来包裹它;反之,如果需要创建的字符串中包含单引号,那么就用双引号来包裹它。例如:
"""
这就是一段注释,程序逻辑并不会用到这里的文字。
下面的语句可以打印出带双引号的文字:
"""
print('小明说: "这不是我干的!"')
print("I'm a student.")
如果创建的字符串中单双引号都需要使用,那么可以使用三引号。如果这些都要用到,那就只能使用转义字符了。
字符串操作
最常用的字符串操作包括拼接、截断等。
拼接
连接两个字符串可以使用 + 运算符:
greeting = "Hello, " + "World!" # 结果:"Hello, World!"
如果重复一段文字,可以使用 * 运算符:
repeated = "abc" * 3 # 结果:"abcabcabc"
索引
索引(index)操作用于从字符串中提取单个字符。其表示方法是:在需要被索引的字符串或变量后面加上方括号,方括号内包含一个整数,表示该字符在字符串中的位置(从 0 开始计数)。非负整数表示从左向右数,最左侧字符的索引是 0;负数则表示从右向左数,最右侧字符的索引是 -1。如果索引超出了字符串的范围,程序会报错。例如:
greeting = "Hello, World!"
print(greeting[0]) # 输出:"H"
print(greeting[-1]) # 输出:"!"
print(greeting[20]) # 程序报错
chinese = "我在学习 Python"
print(chinese[1]) # 输出:"在"
Python 3 比很多其他语言做得更好的一点是,它的字符串默认采用了 Unicode 编码。这意味着字符串中的每个中文字同样是一个字符,占用一个索引单位。这样就不用担心在处理不同语言时使用不一样的索引机制了,避免了像 C 语言中不小心索引到半个中文字符数据的情况。
因为字符串是一个不可变的序列数据类型。试图改变字符串中的字符,程序会报错,比如运行下面的程序:
greeting = "Hello, " + "World!"
greeting[0] = "W" # 程序报错
切片
索引只会取一个字符,我们也可以从字符串中选取一串子字符串,这样的操作被称为切片(slice)。切片使用方括号和冒号获取字符串的子串。方括号中的冒号两边可以指定两个数字,第一个数字是子串开始的位置,第二个数字是子串结束的位置。但是需要注意的是,这两个位置是“左闭右开”的(即包含起始位置,不包含结束位置),只会截取到结束位置前的一个字符。
切片使用的这两个数字可以省略:如果省略左侧数字,表示从最左端开始截取;如果省略右侧数字,则表示截取到最右端。
greeting = "Hello, World!"
print(greeting[1:5]) # 输出:"ello"
print(greeting[7:-1]) # 输出:"World"
print(greeting[7:]) # 输出:"World!"
print(greeting[:]) # 输出:"Hello, World!"
切片还可以接受第三个参数,表示步长(step),即每隔几个字符取一个。
greeting = "Hello, World!"
print(greeting[0:5:2]) # 输出:"Hlo" (在索引0-5范围内,每隔2个取一个)
print(greeting[::-1]) # 输出:"!dlroW ,olleH" (步长为-1,表示倒序截取,这是反转字符串最常用的方法)
字符串长度
使用函数 len() 可以获取字符串的长度,比如:
greeting = "Hello, World!"
length = len(greeting) # 结果:13
字符串的方法
Python 中的各种数据本身都是对象,因而都有一系列操作方法。例如,使用整数的 bit_length() 方法可以得到该整数的二进制有效位数等。在 Python 中可以使用内置的 dir() 函数列出一个对象所有的属性和方法:
print(dir(""))
对比函数和方法:
- 函数(Function): 是一段独立的代码块,可以接收输入参数, 执行特定操作,并返回一个值。
- 方法(Method): 是附加在类或对象上的函数。在面向对象编程中,方法通常用于操作或与对象的内部数据进行交互。方法就是函数在特定语境下的另一种称谓。
整数的方法可能不那么常用,但是字符串的一些方法还是非常常用的,比如:
- str.upper(): 将所有字符转换为大写。
- str.lower(): 将所有字符转换为小写。
- str.startswith(prefix): 检查字符串是否以特定前缀开始。
- str.endswith(suffix): 检查字符串是否以特定后缀结束。
- str.find(sub): 返回子字符串首次出现的索引,如果未找到则返回-1。
- str.replace(old, new): 将所有出现的旧子字符串替换为新子字符串。
- str.split(delimiter): 根据指定的分隔符分割字符串。
- str.join(iterable): 使用字符串作为分隔符连接可迭代对象中的字符串。
在使用上,对象的方法与普通函数的主要区别在于:函数在操作数据时,是将数据作为参数传给函数,例如 len(greeting);而方法虽然也是函数,但在调用时需要先写数据对象(或变量),紧接着写一个点号 .,再写方法名,例如 greeting.upper()。
下面是一些使用字符串方法的示例:
greeting = "Hello, World!"
# 使用 str.upper() 将所有字符转换为大写
upper_string = greeting.upper()
print(upper_string) # 输出: "HELLO, WORLD!"
# 使用 str.lower() 将所有字符转换为小写
lower_string = greeting.lower()
print(lower_string) # 输出: "hello, world!"
# 使用 str.startswith() 检查字符串是否以特定前缀开始
prefix = "Hello"
is_start_with_prefix = greeting.startswith(prefix)
print(is_start_with_prefix) # 输出: True
# 使用 str.endswith() 检查字符串是否以特定后缀结束
suffix = "World!"
is_end_with_suffix = greeting.endswith(suffix)
print(is_end_with_suffix) # 输出: True
# 使用 str.find() 返回子字符串首次出现的索引
sub_string = "World"
index = greeting.find(sub_string)
print(index) # 输出: 7
# 如果子字符串不存在,会返回-1
sub_string = "Java"
index = greeting.find(sub_string)
print(index) # 输出: -1
# 使用 str.replace() 将所有出现的旧子字符串替换为新子字符串
replaced_string = greeting.replace("World", "Python")
print(replaced_string) # 输出: "Hello, Python!"
# 使用 str.split() 根据指定的分隔符分割字符串
delimiter = ", " # 使用逗号加空格分割
split_strings = greeting.split(delimiter)
print(split_strings) # 输出: ['Hello', 'World!']
# 使用 str.join() 使用字符串作为分隔符连接可迭代对象中的字符串
words = ["Hello", "Python"]
delimiter = ", "
joined_string = delimiter.join(words)
print(joined_string) # 输出: "Hello, Python"
转义字符
一些特殊的字符是无法直接在键盘上输入的,可能也无法直接在屏幕上显示出来。对于这些特殊字符,Python 通过“转义”(escape)来表示它们。转义是使用反斜杠 \ 开始的字符序列,它代表一个特定的字符或字符序列。
以下是 Python 中常用的转义序列:
\\: 代表一个反斜杠字符\。\': 代表一个单引号字符'。\": 代表一个双引号字符"。\n: 代表一个换行符。\t: 代表一个制表�为了解决这个问题,1990 年开始,计算机业界开始研发一种新的编码标准,它可以覆盖全世界所有的字符,也就是说,任何一个字符都有自己独占的编码,在任何系统下都会保持这个编码,这样就不会出现换个系统就乱码的问题了。这就是 Unicode 编码,也称万国码、单一码。Unicode 规定了字符集,但该字符集存在多种不同的编码实现格式。Windows 采用了 UTF-16LE 格式的 Unicode 编码(UTF 全称为 Unicode Transformation Format),使用 16 位(双字节)数据表示一个字符。而当前最流行的 Unicode 编码格式是 UTF-8。这是一种可变长度的编码方式,根据字符 of 常用程度,使用 1 到 4 个字节来表示。目前绝大多数的 Unicode 文档采用的都是 UTF-8 编码,它也是 Python 字符的默认编码格式。
多数情况下,字符串和字节之间的转换采用默认编码格式即可,但也有可能,我们需要把字符串转换成其他编码格式,以确保它们在一些非 UTF-8 编码的设备上正确显示。下面用代码演示一下几种不同编码格式的区别。出: Hello
World! (输出过程中换行了)
print("Hello\tWorld!") # 输出: Hello World! print("\ 是个反斜杠") # 输出: \ 是个反斜杠
如果不想让反斜杠起到转义作用,可以使用原始字符串。在字符串前加上 r 或 R,使其成为原始字符串,这样就不会对字符串中的 `\` 进行转义。
例如:
```python
print(r"Hello\nWorld") # 输出: Hello\nWorld
这种情况在表示文件路径时特别有用,例如 r"C:\path\to\directory",这样就不需要每个反斜杠都进行转义了。
字符串格式化
字符串格式化是将特定的值插入到字符串的某些位置的过程。Python 提供了多种方式来格式化字符串。早期 Python 使用 % 运算符进行格式化,后来出现了更强大的 str.format() 方法。虽然这两者在现代 Python 代码中依然有效,且在某些特定场景(如模板字符串存储在外部文件中)下仍然必须使用 str.format(),但对于日常编码,目前 Python 最推荐且最高效的方法是使用 f-string。
f-string 使用前缀 f 来定义字符串,然后在字符串中的花括号 {} 内直接包含 Python 表达式。在显示字符串的时候,Python 会计算花括号 {} 内表达式的值,直接把这个值作为字符串的一部分显示出来。比如,把变量值嵌入字符串:
name = "老王"
age = 60
print(f"我是{name},我今年 {age} 了。")
# 输出: 我是老王,我今年 60 了。
f-strings 允许执行简单的表达式,甚至函数调用:
name = "老王"
age = 60
print(f"我是{name},我五年后是 {age + 5} 岁。")
# 输出: 我是老王,我五年后是 65 岁。
如果只是把单个其他类型的数据转换成字符串,也可以不用 f-string,而是使用 str() 函数,比如:
print(str(123)) # 输出: '123'
print(str(123.456)) # 输出: '123.456'
print(str([1, 2, 3])) # 输出: '[1, 2, 3]'
print(str((1, 2, 3))) # 输出: '(1, 2, 3)'
print(str(True)) # 输出: 'True'
小数的格式代码
如果要格式化小数,可以在大括号中添加格式说明符来控制小数的显示方式。格式说明符通常遵循 {变量名:格式代码} 的形式。
例如,如果想要格式化一个浮点数,并且希望它显示为小数点后两位,你可以这样做:
number = 123.456
formatted_number = f"{number:.2f}"
print(formatted_number) # 输出: 123.46
在这个例子中,.2f 是一个格式说明符,它表示将数字格式化为浮点数,并保留两位小数。.2 指定了小数点后的位数,而 f 表示浮点数格式。
除了控制小数点后的位数,f-string 还允许进行各种其他类型的格式化,例如填充、对齐、百分比格式等。例如:
-
使用百分比格式:
percentage = 0.1234formatted_percentage = f"{percentage:.2%}"print(formatted_percentage) # 输出: 12.34% -
指定宽度和对齐方式,小数表示:
number = 123.456formatted_number = f"{number:10.2f}"print(formatted_number) # 输出: 123.46这里
10.2f表示总宽度为 10 个字符,其中小数点后有两位,右对齐。 -
指定宽度和对齐方式,科学记数法表示:
number = 123.456formatted_number = f"{number:10.2e}" # 显式指定 e 表示科学计数法print(formatted_number) # 输出: 1.2e+02这里
10.2表示总宽度为 10 个字符,.2表示保留小数点后两位。
字节序列
表示方法
字节序列类型(bytes,有时候就简称为“字节”)与字符串看上去非常类似,它在代码中通过在字符串字面量前加上字母 b 前缀来表示。
s = "Hello, World!" # 这是字符串
print(s)
byte_sequence = b'Hello, World!' # 这是字节序列,而非字符串
print(byte_sequence) # 输出: b'Hello, World!'
# 有时会用十六进制格式表示字节序列
b = b'\x48\x65\x6c\x6c\x6f\x2c\x20\x57\x6f\x72\x6c\x64\x21'
print(b) # 输出: b'Hello, World!'
注意:虽然我们用十六进制 \x48 等方式定义了变量 b,但 Python 在打印字节序列时,如果该字节对应的是 ASCII 可打印字符(如字母、数字),它会自动显示为对应的字符(如 H),只有无法显示的字节才会保留十六进制形式。
字节序列与字符串的主要区别在于存储的内容和应用场景。
字符串是由字符组成的序列,通常用于存储文本信息。在 Python 3 中,字符串由 Unicode 字符组成,可以表示几乎所有语言中的字符。字符串主要用于读取或写入文本文件、处理表单输入的文本、在屏幕上显示文本等。
字节序列保存的数据除了文本之外,也可以是任何二进制数据。字节序列顾名思义,是字节的序列。字节是 8 位长的二进制数,可以表示 0 到 255 的数值。字节序列通常用于读取或写入二进制文件(如图片或视频文件)、网络套接字通信、数据加解密等。
字符串和字节之间的转换
有时我们需要将从网络或其他设备中读取的、以字节形式保存的文本数据转换为字符串,或者需要将字符串以二进制形式发送。这时,可以使用字符串的 encode() 方法和字节序列的 decode() 方法在字符串与字节之间进行转换。转换时必须指定文字编码。使用不同的编码,同样的文字转换成的二进制数据是不同的。Python 默认使用 UTF-8 来编码字符串。在此之前,我们先来简单了解一下字符编码的发展历史:
计算机是在美国被发明的,所以 当时很自然的就只考虑了处理英文。最早出现且最著名的字符编码标准是 ASCII 标准(American Standard Code for Information Interchange,美国信息互换标准代码)。它定义了 128 个字符,包括英文字母大小写,数字,常用的标点和一些特殊符号。当时世界上大多数计算机都在使用 ASCII 方案来保存英文文本。该方案最大的问题是只支持英文字符,于是其他国家、组织和公司纷纷开始扩展这个标准,以支持其他字符,如中文、日文字符、制表符及数学符号等。在中国,最常用的标准,包括 GB2312、GBK、GB18030 等都是对 ASCII 的中文扩展。这些扩展出的标准有一个很麻烦的问题:同一个数值在不同的编码标准下被赋予了不同的含义。例如,某一数值在中文标准下可能是一个中文字符,在韩文标准下可能就是一个完全不相关的制表符。这就导致在中文环境下开发的软件,在韩文系统上运行时显示的完全是乱码。如果有人需要在一个系统中同时运行一个中文软件和一个韩文软件,就只能有一个软件可以正确显示文字。
为了解决这个问题,1990 年开始,计算机业界开始研发一种新的编码标准,它可以覆盖全世界所有的字符,也就是说,任何一个字符都有自己独占的编码,在任何系统下都会保持这个编码,这样就不会出现换个系统就乱码的问题了。这就是 unicode 编码,也叫万国码、单一码。unicode 规定了字符集,但是这套字符集也还存在多种不同的编码格式。Windows 采用了 UTF-16LE 格式的 unicode 编码(UTF全称为 Unicode Transformation Format),使用 16 位的(双字节)数据表示一个字符。但是当前最流行的 unicode 编码格式却是 UTF-8,这是一种变长的编码方式,根据字符的常用程度,使用不同长度的编码来表示这个字符,编码长度有可能是 1 到 6 个字节。目前大多 数的 unicode 文档采用的都是 UTF-8 编码,它也是 Python 中字符的默认编码格式。
多数情况下,字符串和字节之间的转换采用默认编码格式即可,但也有可能,我们需要把字符串转换成其它编码格式,以确保它们在一些非 UTF-8 编码的设备上正确显示。下面用代码演示一下几种不同编码格式的区别。
中文的几种不同编码:
chinese_str = "中文"
# UTF-8 是 Python 默认的编码格式
print(chinese_str.encode('UTF-8')) # 输出: b'\xe4\xb8\xad\xe6\x96\x87'
# UTF-16LE 是 Windows 的编码格式
print(chinese_str.encode('UTF-16LE')) # 输出: b'-N\x87e'
# GB2312 是最常用的非 Unicode 中文编码格式
print(chinese_str.encode('GB2312')) # 输出: b'\xd6\xd0\xce\xc4'
可以看到,几种不同编码把同一个中文字符转换成了不同的二进制数据。
英文也可以使用这些编码格式:
english_str = "Hello"
print(english_str.encode('UTF-8')) # 输出: b'Hello'
print(english_str.encode('GB2312')) # 输出: b'Hello'
print(english_str.encode('ASCII')) # 输出: b'Hello'
print(english_str.encode('UTF-16LE')) # 输出: b'H\x00e\x00l\x00l\x00o\x00'
UTF-8 和 GB2312 对于英文字符的编码是兼容 ASCII 标准的,它们把英文字符转换成的二进制数据与 ASCII 的标准完全相同。但 Windows 采用的 UTF-16LE 编码并不兼容 ASCII 标准。
把二进制数据解码为字符串的时候,一定要采用同样格式的编码,否则转换出来很可能就不是正确的字符串了:
chinese_str = "中文"
print(chinese_str.encode('UTF-8').decode('UTF-8'))
print(chinese_str.encode('UTF-16LE').decode('UTF-16LE'))
print(chinese_str.encode('GB2312').decode('GB2312'))
# 上面的解码都可以正确得到字符串,但下面的解码过程会出错,因为使用了错误的编码格式
# print(chinese_str.encode('UTF-8').decode('GB2312'))
english_str = "Hello"
print(english_str.encode('UTF-8').decode('ASCII')) #输出: Hello
# 上一行代码虽然使用了不同的格式编解码,但两种格式对于英文字符是兼容的,因此依然可以得到正确的字符串
练习
字符串的很多操作方法需要结合条件、循环等语句才能完成。这里仅列出一些最基础的练习题目。等到我们学习了后续章节,会再进行更多的练习。
- 判断下面程序的运行结果:
print(len("Hello\nWorld!")) - 反转字符串:输入一个字符串并输出它的反转形式,比如输入 "hello",则输出 "olleh"。
- 是否是回文:编写一个程序检查输入的字符串是否为回文(正反读一样的字符串)。
- 首字母大写:编写一个程序,将字符串中的每个单词的首字母大写。
- 整数所有位上数字的乘积:输入一个表示整数的字符串,编写程序计算该数字符串中所有位上数字的乘积。
- 变位词:编写程序,输入两个字符串,判断它们是否为变位词(即由相同字符组成,字符顺序不同)。
- 最大相同字符长度:输入一个字符串,统计其中连续相同字符的最大长度。例如,字符串 aaabbbbaa 中,最大连续长度是 4,有连续的 4 个 b。