第六章 字符串操作

6.1 处理字符串

6.1.1 字符串字面量

在此次,我们主要解决一个问题,就是怎么在字符串中再插入单引号。都知道字符串本身是自带引号的,所以再单引号中再次插入单引号就无法执行,就要采用一些办法。

1. 双引号

对于字符串来说,用双引号和单引号进行包括,效果是一样的,所以可以用双引号,而单引号在字符串中就不会被识别成字符串截止标志,如:

spam = "That is Alice's cat."
2.转义字符

顾名思义,“转义字符”就是将某个字符的原有效果给覆盖,达到这个字符原有不能完成的功能。转义字符的标志是“\”,在斜杠的后面加上想要转义的目标字符,就构成了完整的转义字符,例如想要转义单引号,即 \'。上面的案例也可以完成,

spam = 'Say hi to Bob\'s mother.'

按照原来的单引号规则,字符串在Bob就结束了,但是有了转义字符,对其后面的单引号处理,就消除了他的结尾标志,变成了普通的字符,然后结尾就变成了mother。

当然,还有一些转义字符,如下:

里面有一些特殊的转义字符,如 \t和 \n。这两个字符在很多语言中都通用,并且具有一样的含义,例如C++中,\n也可以用来换行。python中也是一样的用法,如下:

>>> print("Hello there!\nHow are you?\nI\'m doing fine.")


Hello there!
How are you?
I'm doing fine.
3.原始字符串

在字符串的引号的前面加上r,就可以把字符串设置成原始字符串,把其中的所以字符的效果去除,变成最普通的字符,如:

>>> print(r'that is carol\'s \ncat')


that is carol\'s \ncat

原先的\n字符是换行命令,但是经过r的前缀后,失去了原本的作用,变成普通的“\n”字符。

4.用三重引号的多行字符串

三重引号多在字符串换行时使用,因为它可以根据格式自动换行,就省去了\n的转义字符,变得更加简单,如下:

print('''Dear Alice,

Eve's cat has been arrested for catnapping, cat burglary, and extortion.

Sincerely,
Bob''')

注意到其中的Eve's没有使用转义字符,并且使用转义字符也可以输出同样的文本,如下:

print('Dear Alice,\n\nEve\'s cat has been arrested for catnapping, cat
burglary, and extortion.\n\nSincerely,\nBob')

输出了同样结果:

Dear Alice,

Eve's cat has been arrested for catnapping, cat burglary, and extortion.

Sincerely,
Bob

而两者相对比,第一种更加简单。

6.1.2 字符串索引和切片

这一点在上一章的实践活动中就使用过了,字符串和列表的取词差不多,使用索引就可以提取指定的字符,例如:

'   H  e  l  l  o  ,     w  o  r  l   d   !  '
   0  1  2  3  4  5  6  7  8  9  10  11  12

可以使用列表类型的索引进行字符提取:

>>> spam = 'Hello, world!'
>>> spam[0]
'H'
>>> spam[4]
'o'
>>> spam[-1]
'!'
>>> spam[0:5]
'Hello'
>>> spam[:5]
'Hello'
>>> spam[7:]
'world!'

与列表类似,不过多赘述。

6.1.3 字符串的in和not in操作符

这两个操作符不仅适用于列表,同样适用于字符串,输出结果为布尔值,False和True。

>>> 'Hello' in 'Hello, World'
True
>>> 'Hello' in 'Hello'
True
>>> 'HELLO' in 'Hello, World'
False
>>> '' in 'spam'
True
>>> 'cats' not in 'cats and dogs'
False

与列表类似,不过多赘述。

6.2 将字符串放入其他字符串

在这之前,我们对字符串的连接或插入都是使用“+”号,但是这种方法毕竟太过复杂,且不美观,需要书写大量字符串和符号,下面介绍两种方法:

1.%s运算符

我们可以在需要插入字符串的地方用%s代替,在整个字符串结束后,在末尾对%s运算符标记的地方进行赋值,例如:

'My name is %s. I am %s years old.' % (name, age)

这种方法就看上去很整洁,所以有些代码段中会使用这个办法。在代码前,对name和age进行定义并赋值,(name,age)自动调用并代入,得到:

'My name is Al. I am 4000 years old.'
2.f前缀

这个方法是最简单的,也是最直接的,在大部分代码段中都可以看到它:

name = 'Al'
age = 4000
f'My name is {name}. Next year I will be {age + 1}.'

直接对需要插入的字符串的位置用变量加大括号代替,简明易懂。而且还可以在大括号中添加运算,例如age+1。他得到的结果如上同,但需要注意的是,这个方法的核心就在于f前缀,如果没有,就会被当成一整个字符串输出,达不到插入效果。

6.3 有用的字符串方法

6.3.1 字符串方法upper()、lower()、isupper()和islower()

upper()和lower()方法就是转换字母的大小写,让一些程序的容错率更高,其中upper()是转换成大写的,lower()是转换成小写的,如下:

print('How are you?')
feeling = input()
if feeling.lower() == 'great':
    print('I feel great too.')
else:
    print('I hope the rest of your day is good.')

在这个程序中,使用了lower()方法,目的是把输入的字符全部变成小写。如果输入的字符串是不规则的,即字母大写和小写都有,无法做到精确匹配,所以就可以使用对输入字符串进行处理,格式同意后,可以使用if语句进行判断。运行如下:

How are you?
GREat
I feel great too.

How are you?
greaT
I feel great too.

而isupper()和islower()方法是用来判断字符串是否是否是大小写,输出布尔值:

>>> spam = 'Hello, world!'
>>> spam.islower()
False
>>> spam.isupper()
False
>>> 'HELLO'.isupper()
True
>>> 'abc12345'.islower()
True

当然,这四种方法都对非字符串无用,只关注字符串的情况,所以对“abc12345”输出true。这四个方法是可以嵌套使用的,例如:

>>> 'Hello'.upper().lower().upper()
'HELLO'
>>> 'HELLO'.lower().islower()
True

第一个的要求可能是把字符串变成大写,当然第一步的upper()就够了,此处是为了验证方法的多重调用。

第二个就是对全大写的字符串进行小写化处理,再判断是否已经全部小写。

6.3.2 isX字符串方法

isX字符串方法并不是一个具体的方法,而是一些方法的统称,它们都是以is开头的方法。如下:

简单示例如下:

>>> 'hello'.isalpha()
True
>>> 'hello123'.isalpha()
False
>>> 'hello123'.isalnum()
True
>>> 'hello'.isalnum()
True
>>> '123'.isdecimal()
True
>>> ' '.isspace()
True
>>> 'This Is Title Case'.istitle()
True
>>> 'This Is Title Case 123'.istitle()
True
>>> 'This Is not Title Case'.istitle()
False
>>> 'This Is NOT Title Case Either'.istitle()
False

首先,isalpha()方法只适用于全字母,对字母输出true,当然大小写都算。isalnum()方法就可以对字母和数字进行判断,同示例,对上面的“abc12345”就输出true,并且介绍中说这个方法适用于两种变量,但它对单一的变量依然有效,例如对“abc”和“123”,一样会输出true,所以此处需要注意。而isdecimal()方法就只对数字作用,isspace()方法对一些特殊的格式作用,例如换行等。最后的istitle()方法较为复杂,因为它会判断字符串中的每个单词的组成:必须是首字母大写,然后后面字母小写,对数字无用。下面运用这些方法完成一个简单的判断——检测输入的年龄和密码是否符合规范(年龄是阿拉伯数字,密码是字符和数字中一种或两种):

while True:
    print('Enter your age:')
    age = input()
    if age.isdecimal():
        break
    print('Please enter a number for your age.')

while True:
    print('Select a new password (letters and numbers only):')
    password = input()
    if password.isalnum():
        break
    print('Passwords can only have letters and numbers.')

print("年龄、密码设置成功!")

对这两个条件进行反复判断,直到输入正确,所以使用while循环,并设条件为true。第一个while是判断年龄是否输入正确,并且要求年龄输入使用阿拉伯数字,所以用英文表示的年龄都不行。如下:

Enter your age:
forty two
Please enter a number for your age.
Enter your age:
42
Select a new password (letters and numbers only):

输入42,符合isalnum()方法条件,则执行break,跳出第一个while,执行第二个while。而第二个条件就拓宽了很多,只要是数字和字母就可以通过,当然除了这两种数据类型之外,就通过不了,例如:

Select a new password (letters and numbers only):
dyy66!
Passwords can only have letters and numbers.
Select a new password (letters and numbers only):
dyy66
年龄、密码设置成功!

在把其他字符删除后,就通过,跳出第二个循环,执行成功标志print语句。所以来看,isX()字符串主要适用于验证,提醒使用者输入正确内容,再运行程序。

6.3.3 字符串方法startswith()和endswith()

这两个方法是判断某个字符串的开头或结尾是不是等于某个目标字符串,输出布尔值:

>>> 'Hello, world!'.startswith('Hello')
True
>>> 'Hello, world!'.endswith('world!')
True
>>> 'abc123'.startswith('abcdef')
False
>>> 'abc123'.endswith('12')
False

这两个方法很好理解,就是截取开头或结尾的字符串与其他比较,看是否一致。在特殊时候,这两个方法可以用来判断一个字符串的开头和结尾是不是等于另一个字符串,相当于等号(“==”)。

6.3.4 字符串方法join()和split()

下面介绍两个非常常见的方法,连接方法和切割方法。首先,join()方法是连接。例如需要将my、name、is、hero连起来,并且在单词中间加入逗号。一般情况下,可以用加号连接,如下:

spam = 'my' + ',' + 'name' + ',' + 'is' + ',' + 'hero'

这种方式已经非常古老,很繁琐,前面我们说过字符串中如何插入字符串,在6.2。所以可以试一试,在这里怎么使用:

# f前缀
a = ','
spam = f'my{a}name{a}is{a}hero'
print(spam)

# %s运算符
a = ','
spam = 'my%sname%sis%shero' % (a,a,a)
print(spam)

它们三个输出的结果是一样的,但是看代码复杂程度,可以很清晰的看出,“+”号连接是最繁琐的。而这里要再介绍一种连接手段:

print(','.join(['my', 'name', 'is', 'hero']))

join()方法,对某个字符进行处理,要求它插入在输入列表中。在列表的表项之间,加入目标字符。这四种方法的输出结果都是一样的:

my,name,is,hero

需要注意的是,join()方法需要传入一个列表,而不是其他数据类型,处理的字符会在表项之间添加,最后得到一个连接好的字符串。

其次,split()方法与join()方法相反,它负责切分字符串。而split()方法是对一个字符串进行处理,将分割标志作为条件传入split()中。若传入为空,则默认以空格作为切分标志。如下:

>>> 'My name is Simon'.split()
['My', 'name', 'is', 'Simon']

>>> 'MyABCnameABCisABCSimon'.split('ABC')
['My', 'name', 'is', 'Simon']

>>> 'My name is Simon'.split('m')
['My na', 'e is Si', 'on']

经过split()切分后,输出为一个字符串列表。当然,split()中也可以传入特殊格式符,例如换行符“\n”,用它分割后,就会把每行作为一个列表的表项输出,最后整合为一个列表。其中每一个表项就是原来段落的每一行。

6.3.5 使用partition()方法分隔字符串

partition()方法也是用来分隔字符串的,但是它的用法比较独特,适用的范围比较小,在代码中似乎很少用到。下面用例子来说明:

>>>  'Hello, world!'.partition('w')
('Hello, ', 'w', 'orld!')
>>>  'Hello, world!'.partition('world')
('Hello, ', 'world', '!')

partition()方法和split()方法的第一个区别是split()会把分隔符字符串删除,只留下除分隔符字符串之外的其他内容,而partition()方法不会删除分隔符字符串,一般会把分隔符字符串作为中间项输出,分隔符之前的内容作为第一项,分隔符之后的内容作为第三项。partition()方法,输出就是三个子字符串的元组,分为“之前文本”、“分隔符”和“之后文本”。

那如果分隔符字符串在主字符串中重复多次出现呢?那我们可以思考,partition()方法固定是输出三个元组,那么不难想到,它只对分隔符字符串第一次出现时才分割,之后再出现就不再分割,如下:

>>> 'Hello, world!'.partition('o')
('Hell', 'o', ', world!')

对于“o”分隔符,在“hello”中已经分割,变成“hell”和“o”,变成结果元组的前两项,剩下的内容为第三项。

如果在字符串中没有分隔符字符串呢?那么就把原本的字符串作为结果元组的第一项,后两项为空。如下:

>>> 'Hello, world!'.partition('XYZ')
('Hello, world!', '', '')

综上所述,partition()方法适用范围比split()小,适合用于需要前后文本的情况。因为它只对字符串作用一次,不会像split()一样,只要有分隔符就一直分割,所以partition()在一定程度上也保护了原有字符串,对前后文本的提取提供便利。

6.3.6 用rjust()、ljust()和center()方法对齐文本

rjust()方法是实现右对齐,即right。它可以有两个参数,也可以是一个参数。第一个参数是输出结果的总长度,第二个参数是用来填充空格,下面用例子来说明:

>>> 'Hello'.rjust(20)
'         Hello'
>>> 'Hello'.rjust(20, '*')
'***************Hello'

第一个,对“Hello”进行20位右对齐,则构造20位字符串,然后将“Hello”放在最后5位,即可做到右对齐。然后没有设置第二个参数,则默认前面由空格填充。

第二个,前面与上相同,但是有了第二个参数,则说明可以用“*”号代替空格填充前面15位。

同样,对于ljust()方法来说,和rjust()方法相同,唯一区别就是一个左对齐,一个右对齐,其他用法一致,所以不过多赘述。

最后一个就是center()方法,本质用法和左、右对齐一致,它的作用是居中对齐。用第二参数去填充字符前后的空格。如下:

>>> 'Hello'.center(20)
'    Hello    '
>>> 'Hello'.center(20, '=')
'=======Hello========'

下面分析一个对齐的综合程序:

def printPicnic(itemsDict, leftWidth, rightWidth):
   print('PICNIC ITEMS'.center(leftWidth + rightWidth, '-'))
    for k, v in itemsDict.items():
        print(k.ljust(leftWidth, '.') + str(v).rjust(rightWidth))

picnicItems = {'sandwiches': 4, 'apples': 12, 'cups': 4, 'cookies': 8000}
printPicnic(picnicItems, 20, 6)

首先,定义了一个函数printPicnic(),它接受一个字典和两个数据,这个数据不确定,需要根据目标字典中的内容确定合适的长度,这也是对齐方法的缺点。在函数中,对标题进行居中对齐。根据传统菜单来说,食品左对齐,价格右对齐。所以运用items()方法成对在字典中提取键-值对。用多重赋值分别对键和值进行对齐。对键左对齐,空格用“·”代替,对值右对齐,用空格填充。运行结果如下:

-------PICNIC ITEMS-------
sandwiches..........  4
apples..............  12
cups................  4
cookies............. 8000

适当的字符长度让输出结果更加美观,所以设置长度时,可以适当观察目标字符串的长度,据此设置对齐长度。

6.3.7 用strip()、rstrip()和lstrip()方法删除空白字符

这三种方法都是删除空白字符,strip()方法是删除字符串两边的空白字符,rstrip()方法是删除字符串右边的空白字符,而lstrip()方法是删除字符串左边的空白字符。如下:

>>> spam = '  Hello, World  '
>>> spam.strip()
'Hello, World'
>>> spam.lstrip()
'Hello, World  '
>>> spam.rstrip()
'  Hello, World'

理解简单,不过多赘述。

其次,strip()方法可以拥有一个参数,可以将字符串两侧,符合参数的字符删除,如下:

>>> spam = 'SpamSpamBaconSpamEggsSpamSpam'
>>> spam.strip('ampS')
'BaconSpamEggs'

参数的顺序并不重要,“ampS”和“Spam”效果相同。当在两侧搜索时,字符不符合参数中的字符则自动停止删除,输出结果。例如将上述spam变量中的“Bacon”改成“pacon”,则输出的结果就是“conSpamEggs”,说明“B”字符和“s”(小写)字符是strip()方法终止删除的标志。同样,将“Eggs”的“s”字符变成大写,也会被strip()删除,那么“g”字符就是终止删除的标志。

6.4 使用ord()和chr()函数的字符的数量

其实在上一章的实践活动1中就出现了,不过当时没有作解释,现在来作简单解释。这两个函数主要依靠与Unicode编码,而Unicode编码是一种广泛使用的字符编码标准,它为世界上几乎所有语言的字母、数字、标点符号和特殊字符分配了唯一的数字码点,这些码点使用十六进制数表示,通常以"U+"开头。Unicode字符集包含超过130,000个字符,并被设计为全球文本交换和处理的标准。上一章的实践中,使用了ord()函数,是为了方便字符之间的处理。将“a”-“h”字符转化为数字,就可以用数字进行判断是否符合预定的条件,例如a-h的范围是97-105,如果是其他字符,则Unicode编码不在97-105之间,就可以判断错误。这两个函数为字符处理提供了便捷,那么下面来展示几个例子:

>>> ord('!') 
33
>>> chr(65) 
'A'
>>> chr(ord('A')) 
'A'
>>> chr(ord('A') + 1) 
'B'

这两个函数可以为字符和数字之间进行互相转换,也可以嵌套使用,为字符处理创造了新的思路。

6.5 - 6.7

主题与系统自动化处理有关,先不作学习,略。

6.8 小程序:Pig Latin

Pig Latin是以英语为基础的伪语言,换言之,它只是一种改变单词的方式。具体方式如下:

1.如果单词以元音开头,则在单词末尾加上“yay”。

2.如果单词以辅音或辅音簇开头,则将辅音或辅音簇移到单词末尾,然后加上“ay”。

程序示例:

Enter the English message to translate into Pig Latin:
My name is AL SWEIGART and I am 4,000 years old.
Ymay amenay isyay ALYAY EIGARTSWAY andyay Iyay amyay 4,000 yearsyay oldyay.

我们就以“My name is AL SWEIGART and I am 4,000 years old.”为原始句子。观察发现几点,第一,句子中具有大小写不固定特点,所以要对大小写不固定的单词进行小写化;第二,含有数字“4000”,而数字在这个规则中不适用,所以输出还是4000,应该使用isX方法解决;第三,观察到单词“old”后由标点,但是不能直接进行处理,因为会变成“old.yay”,所以要对结尾由标点的单词进行处理。程序完整代码如下:

print('Enter the English message to translate into Pig Latin:')
message = input()

VOWELS = ('a', 'e', 'i', 'o', 'u', 'y')

pigLatin = [] # A list of the words in Pig Latin.
for word in message.split():
    # Separate the non-letters at the start of this word:
    prefixNonLetters = ''
    while len(word) > 0 and not word[0].isalpha():
        prefixNonLetters += word[0]
        word = word[1:]
    if len(word) == 0:
        pigLatin.append(prefixNonLetters)
        continue

# Separate the non-letters at the end of this word:
    suffixNonLetters = ''
    while not word[-1].isalpha():
        suffixNonLetters += word[-1]
        word = word[:-1]

# Remember if the word was in uppercase or title case.
    wasUpper = word.isupper()
    wasTitle = word.istitle()
    word = word.lower() # Make the word lowercase for translation.

# Separate the consonants at the start of this word:
    prefixConsonants = ''
    while len(word) > 0 and not word[0] in VOWELS:
        prefixConsonants += word[0]
        word = word[1:]

# Add the Pig Latin ending to the word:
    if prefixConsonants != '':
        word += prefixConsonants + 'ay'
    else:
        word += 'yay'

# Set the word back to uppercase or title case:
    if wasUpper:
        word = word.upper()
    if wasTitle:
        word = word.title()

# Add the non-letters back to the start or end of the word.
    pigLatin.append(prefixNonLetters + word + suffixNonLetters)

# Join all the words back together into a single string:
print(' '.join(pigLatin))

我们来慢慢分析:

第一部分,先设置一个元音列表,方便之后使用。建立一个列表,用来储存已经翻译好的单词,最后使用join()方法进行格式完善。下面就是使用for循环,对split()方法分割后的每个单词进行翻译。我们知道split()方法分割完之后,得出的是每个单词单独的列表,可以用for循环。循环之下,设立新的字符串数据类型prefixNonLetters,用来储存备用字符,待会解释。使用while语句,对单词的每个字符进行判断。如果是单词的话,word.isalpha()是True值,not操作符令其为相反,即False。所以while语句不会执行,if语句也紧随跳过。但如果是数字,word.isalpha()是False值,not操作符令其为相反,即True,执行while语句。经过运行,word中的内容就转移去了变量prefixNonLetters中,变量prefixNonLetters变成“4000”,word为空值,即不满足while循环条件,执行if语句,把“4000”放入翻译完成的列表pigLatin中,程序重回for循环,翻译下一个单词。这就完美的解决了数字怎么返回原值的问题。这个第一部分,只针对非字母字符,当然标点不算,因为split()方法分割之后,一般句子格式情况下,标点会跟着前一个单词,所以所有单词都不会执行第一部分。

第二部分,设置一个新的字符串数据类型suffixNonLetters,用来存储特殊字符,即句子暂停或终止时的标点符号。这个部分就是针对标点跟单词连接在一起的情况,例如“old.”。在这里,对最后一个字符使用isalpha()方法,可以看出是否存在标点,若单词后存在标点,标点被转移到新变量suffixNonLetters中,在这里,新变量中存储着“.”标点。而再一次运行时,最后一个字符串为“d”,所以跳出while循环,继续向下执行。到最后,为“old”翻译完成后,加号连接新变量,标点即可重新出现。这个第二部分也是只针对非全字母字符,全字母单词,此阶段也是不执行。

第三部分,就是对处理完成的word字符串进行判断。它们是只有首字母大写,还是全部大写或是全部小写。两个变量wasUpper和wasTitle存储着两个布尔值,现在所有单词都变成了小写,而两个布尔值等会用作if语句的条件,还原成原格式。

第四部分,就是判断元音和辅音了。这里再次新键一个字符串类型prefixConsonants,他就是用来储存辅音的。根据代码,判断第一个字母是否为元音,若是元音,while语句条件则变成False,不会执行while语句,导致新变量prefixConsonants为空,所以下面的if和else语句就是根据这个来设定条件。元音就会跳过while语句,执行else语句,直接在单词后加“yay”。若不是元音,就会进入while循环,直到找到元音,例如“SWEIGART”,前两个字母都是辅音,所以处理之后的word变成“EIGART”,新变量prefixConsonants中包含“SW”,然后执行if语句,用加号对它们进行相加,得到“EIGARTSWAY”(这时候应该是小写!)。

第五部分,根据第三部分两个布尔值,将结果还原成原句中的大小写情况。例如上面处理过的字符串的“eigartsway”,在这一阶段,根据第三部分的赋值:wasUpper为True,wasTitle为False。所以此处调用upper()方法对其全部大写。

第六部分,就很简单了,对上述三个变量进行整合,得出最终结果。这个阶段,本质上只为非全字母字符服务,因为数字在第一部分就已经完成处理,传入最后的列表中,而全字母字符没有执行这两个新变量存在的代码段,所以都为空。

最后,对pigLatin列表进行格式优化,在列表的每一个表项之间加入空格,最后形成完整字符串。

6.9 小结

文本是最常见的数据形式,在每个代码中都能看到字符串。所以字符串处理方法是非常重要的知识,它们可以为字符串处理提供非常便捷的思路。非常多的字符串处理办法,格式、用法非常复杂,需要花费时间去记忆,需要动手实践去熟练。

本章是这本书第一部分的最后一章,说明python基础已经全部介绍完毕。本书后面的章节仍会介绍部分新概念,但现在的水平已经可以去编写稍微复杂一点的代码了。

6.10 课后习题选讲

6.10.1

答:\n 是换行符,\t 是制表符。

6.10.2

答:‘e’ ; ‘Hello’ ; ‘Hello’ ; ‘lo world!’

6.10.3

答:‘HELLO’ ; True ; ‘hello’

6.10.4

答:['Remember,', 'remember,', 'the', 'fifth', 'of', 'November.']      ;    'There-can-be-only-one.'

6.11 实践活动

6.11.1 表格输出

参考代码如下:

def printTable(List1,List2):
    for i in range(len(List1[0])):
        for j in range(len(List1)):
            print(List1[j][i].rjust(int(List2[j])),end=' ')
        print()
tableData = [['apples','oranges','cherries','banana'],
                ['Alice','Bod','Carol','David'],
                ['dogs','cats','moose','goose']]
colWidths = [0]*len(tableData)#colWidths列表中每个元素放置每个内置列表的最长字符串长度
for i in range(len(tableData)):
    for j in range(len(tableData[i])):
        if len(tableData[i][j]) > colWidths[i]:
            colWidths[i] = len(tableData[i][j])
printTable(tableData,colWidths)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

计算机懒人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值