上一章讲了字符串比较底层的一些知识但是对于想快速上手的人来讲难免枯燥无味而且不同的知识储备的人读了会有不同的感受对于大多数人来讲能快速上手才是王道。但是速成的东西往往不会太扎实也不会走的更远。当然为了解燃眉之急速成也是有好处的。这一篇文章我们将讲解字符串的字面量这是了解字符串的第一步。或许等你完全理解了这一章的内容然后想继续修炼内功心法可能会想再看上一章的内容。当然上一章的讲解也是浅尝则止不算详细但是这对大部分人已经够用了等某天你成为python专家可能就需要更丰富的知识储备了。讲到字符串我相信很多人会听到一个词字符串字面量。听到这个词的时候我相信大多数人都会觉得每个字都知道但是连在一起怎么这么别扭。字符串我们已经明白了那么字面量是什么意思我相信很多老师都不会特别去说这个词字面量如果我稍稍改动一下我想大家一定会恍然大悟。我们在短视频或者电视剧里经常听到这样的问答你这话什么意思回答字面意思。这虽然有点俏皮话的意味但是字面量确实就是字面意思。就是你写出来是什么就是什么的意思。在程序中你写了一个字符串“12345”这个就是字符串的字面量。我相信读到这里你已经明白了字面量但是字符串的字面量远比你想象的要复杂一些所以字符串的字面量我们要单独拿出来说。其实除了字符串别的类型也有字面量比如整数我写一个18就是字面量因为整数比较简单大家都不会特意去谈它的字面量。接下来我们要开始详细谈谈字符串的字面量。1.空字符串a b c d print(a{},b{},c{},d{}.format(a,b,c,d)) #输出结果a,b,c,d上面的代码abcd都可以表示空字符串从打印结果也可以看到abcd打出的值都为空。这三种引号的说明请参照上一章。2.引号互相嵌套a Lily‘s apple b Lily“s apple c Lily‘s apple d Lily‘s apple #e Lilys apple #f Lilys apple #g hello h hello print(a{},b{},c{},d{},h{}.format(a,b,c,d,h)) #输出结果aLily‘s apple,bLily“s apple,cLily‘s apple,dLily‘s apple,h hello这些引号之间可以互相的嵌套,只要你不引起歧义就没有问题。其中的efg都报错了对与e把双引号当成字符边界字符串开始的引号会当作边界的开始此时解释器会判断是单引号双引号还是三引号来作为字符串的开始标志此时在中间加一个双引号会被认为是字符串的结束然后字符s被被当作是新的开始但是这个s开始的字符没有被被引号包含会认为是错误。同理f也是同样的原因。比较有意思的是g这个也会被认为是错误因为连着四个双引号会让解释器混乱无法分辨出真正的开始的引号是单双引号还是三双引号最后也会报错。h在三个双引号后见加了一个空格这样解释器就是知道是以三个双引号作为字符串的边界不会引起歧义所以正确。当然出错的组合还有很多但是记住一条开始位置确定字符串边界的引号形式只要字符串中不存在和边界引号一样的形式就不会出错。 关于三引号的作用上一章已经讲过了请参照上一章的说明。3.转义字符串字符串中有转义字符这个很常见假如你这个字符串是一篇文章其中肯定涉及到换行我们都知道是换行是\n当然除了换行转义字符还有别的转义字符。你一定有个疑问为什么需要转义字符因为有些字符既是普通字符但是在特定的条件下又需要有特殊的含义所谓的转义就是改变它原有的意义。当然你又可能有疑问为什么非要用相同的字符而不用别的字符代替呢其实这个应该有语言设计的历史原因同时也有现实的考量。因为键盘上的字符就那么多语言的设计者又是外国人所以很自然而然的用他们常用的26个字母以及一些有特殊的常用符号。如果我们用中文设计一门语言是不是就不需要转义呢这个问题只能留给以后的天才了。接下来我们继续说转义字符为了说清楚每个转义字符的用法我都会举例子说明希望通过本次的学习你能完全掌握转义字符。a.延续行 \新行我们在写代码的时候如果一个字符串太长可能需要多行来书写因为向右滑动编辑器去看代码总是一件让人恼火的事情我自己反正是不会允许将代码写在屏幕之外的。这时候有一个办法就是用到\新行看如下的代码a 李雷 \ 你好么 b 李雷\ 你好么 c 李雷\ 你好么 print(a{}.format(a)) #输出a李雷你好么 print(b{}.format(b)) #输出b李雷 你好么 print(c{}.format(c)) #输出c李雷你好么我们可以观察到两种不同的形式都可以实现换行。两者的共同点是“\”的后面不能跟任何可打印字符只能是换行。a是将两个字符串链接了起来虽然第二行前面有空格但是因为第二行和第一行都是两个独立的字符串所以结果就是直接将两个字符串拼在了一起和字符串之外的换行没有关系。b和a不同的地方是这两行都在一个字符串内所以第二行前面的空格也会计入字符串为了取得和a一样的效果就得像c一样第二行开始不要有空格。b.反斜杠\\从上面的例子可以看出反斜杠有特殊的含义如果你的字符串中包含了反斜杠而不是换行的用途那么这个字符串会报告警告。看下面的例子#a ab\c b ab\\c print(b{}.format(b)) #输出为 bab\ca ab\c 解释器虽然不会报错但是会给出一条警告SyntaxWarning: invalid escape sequence \c提示\c不是有效的转义字符。到这里你可能就会明白了在字符串中/开始就表示转义虽然\c解释器提示了不是有效的转义但是假如你想在字符串中加入一个\n呢我们都知道斜杠\n是转义字符换行看下面的代码a ab\nc b ab\\nc print(a{}.format(a)) #输出为 aab # c print(b{}.format(b)) #输出为bab\nc对于a来讲\n转义成了换行所以打印出来两行第一行是aab第二行是c这显然不是我们想要的效果。为了阻止\和后面的字符组合产生转义此时我们可以再加一个斜杠形成了双斜杠\\其实这个双斜杠也是一种转义它让第二个斜杠代表他自己而不是转义的开始。所以我们可以这样理解如果你在字符串中需要显示一个斜杠必须在它前面加上一个斜杠来转义让它表示斜杠而不是转义的开始这听上去有点绕不过事实如此。当然你也可以简单的理解为在字符串中你要显示斜杠那就得用双斜杠当然如果你想显示双斜杠那就得用四个斜杠如下面代码所示a ab\\\\c print(a{}.format(a)) #输出为 aab\\cc.单引号和双引号\ \在上面我们提高字符串可以包含在单引号或者双引号内但是如果想在单引号里面再包含单引号就会出错。幸运的是我们有转义字符可以让你单引号里面包含单引号或者双引号里面包含双引号如下代码所示a a\c b a\c c ac d a print(a{},b{}.format(a,b)) #输出为 aac,bac print(c{},d{}.format(c,d)) #输出为 cac,daccd和ab的打印效果是一样的。所以一般情况下我们会选择c和d的方式毕竟看上去容易理解。如果你要在字符串中既要包含单引号又要包含双引号如果是用双引号或者单引号包裹字符串那么就必须用到转义字符了当然如果我们用三个单引号或者三个双引号也能达成这样的效果但是在某些场景可能不适用看下面的代码a a\c\d b a\c\d c acd d acd #e abc #f abc g \abc\ h \abc\ i abc j abc print(a{},b{}.format(a,b)) #输出为 aacd,bacd print(c{},d{}.format(c,d)) #输出为 cacd,dacd print(g{},h{}.format(g,h)) #输出为 gabc,habc print(i{},j{}.format(i,j)) #输出为 iabc,jabc上面的代码a和b我们用的转义可以成功的在字符串中包含单引号和双引号。c和d采用三个单引号或者双引号也可以不转义的情况下包含双引号或者单引号。注意到e和f解释器会报错的原因上面我们已经说了。我们可以用gh转义的形式当然更聪明的做法是用i和j说白了还是避免产生字符串起始位置的歧义。上面的这些代码看上去有点鸡肋一般情况下我们不会用到这么复杂的场景但是思考这些场景能够加深你对字符串引号转义的理解。d.响铃\a这个转义我理解目前已经没有啥意义了据说是可以让计算机发出哔的一声但是目前我的电脑上并没有发出这种声音这是一种历史的产物对于当下来讲已经毫无意义了。e.退格: \b这个转义字符现实中用的很少顾名思义就是回退一个字符的位置这个在交互式的命令行场景用的比较多我们可以看一个简单的例子a ab\bc print(a{}.format(a)) #输出结果 aac上面代码\b回退了一个字符将b去掉了所以最后打印的是ac。f.换页\f这个换页的转义字符也是个老古董了目前很少用到基本上可以忽略这里也就不再举例说明了。g.换行\n这个转义字符最为常用了例如我们要打印多行的字符串可以通过换行符来实现。如下代码所示a ab\ncd\nef print(a{}.format(a)) #输出结果 aab # cd # ef上面的代码我们通过两个换行转义字符将字符串分成了三行。我们在读取文件的时候按行读取每一行末尾都会带上\n因为文件中的每一行末尾存的都是\n来标识这一行的结束。但是这也不完全对其实在windows系统中文件中的换行符是\r\n而在linux和mac系统中文件存储的是\n。这个你本不用担心对于python来讲它自动屏蔽了这种差异当你在windows上用python写入文件的时候比如你通过file.write(abc\n)python会自动在文件中将\n转换成\r\n而当你读取文件的时候python又会去掉\r所以你看的读入内容和写入的内容是一致的并没有\r。python可以屏蔽系统之间的差异但是或许你有这样的经历你在服务器端一般都是linux服务器拷贝了一个log日志然后在windows系统用记事本打开然后发现没有换行全部挤成了一团。这就是记事本不认\n当换行linux生成日志是\n当作换行的。为什么windows不去改而适应这种场景呢我理解这就是一种西式的傲慢之前我们用国外的产品觉得很好用现在呢国外的产品为什么不能像国产那样更贴合用户呢这就是西式傲慢汽车就是一个很常见的例子。当然或许你有不同的观点这只是一个题外话。h.回车\r上面我们谈换行的时候已经谈到了这个\r除了和\n一起组成换行最常用的就是数据提交例如我们用input函数从标准输入输出流读取数据如下代码所示a input() #输入123然后回车 print(a{}.format(a)) #输出结果a123作为input函数的使用者我们不用关心字符串中的转义字符\r但是假如你是实现input函数的人那么就需要关注标准输入流的字符当按下回车键你收到一个是\r的转义字符知道这是输入结束了你就得到了用户输入的字符串。上面的代码输入了123回车通过将输入复值给变量a打印出来的结果也是123。i.制表符\t 和 \v这两个转义字符一起讲是因为这个和制表相关\t叫水平制表符\v叫垂直制表符。对于\t它对应我们电脑上的tab键\v没有对应这两个制表符都和早期打印机相关目前我们能用到的只有\t了。一般默认的情况下水平制表符一般占位4个空格一般我们在编辑器中按tab会发现光标移动了四个空格的位置。当然不同的编辑器或者系统对这个tab键的解释不一样所以你在写python代码的时候千万不要空格和tab键混用这样一旦到别的平台或者ide环境会发现运行错误。这里面的原因是多样的但是最根本的原因是假如你在windows上创建了一个python文件然后去linux上执行但是因为有一些问题你在linux修改如果你用了tab此时就有可能发生令人恐怖的IndentationError的错误这个错误你用肉眼是看不出来的我就遇到过这种表情情况。这改起来会非常的繁琐python官方建议是用空格而不是tab看下面的代码a ab\tc b ab\vc print(a{},b{}.format(a,b,)) #输出结果aab c,babc从上面代码的打印来讲\t占用了四个空格大小但是\v似乎没有占位这个不同编辑器展示有关不管怎么讲都不建议在你的字符串中输入这两个转义字符我们有空格可以代替这里讲到它是让你遇到之后能明白是什么意思但是完全可以避免使用它们。j.十六进制字符\xhh在之前的章节中我们讲过16进制整数用0xhh表示在字符串中如果我们要输入16进制形式就需要以\x开头但是这个和整数16进制不同的是\x后面只能跟两个16进制数字也就是说8bit大小。这个值的范围是0-255当你把16进制转义字符输入字符串并打印的时候解释器会尽量打印出对应的字母其实这揭示了一个映射关系每个字符在内存中存储的都是一个整数。我们面前提到过unicode它是一个统一的标准将全世界所有的文字都统一编码对应唯一的一个数字。后面我们会专门开一章来谈unicode到时候我们再来详细解释。先看下面的代码a \x42 b \xf7 c \x80 d \x48ello #f \x4gello print(a{},b{},c{},d{}.format(a, b, c, d)) #输出结果aB,b÷,c,dHello上面的结果中c的输出是个特殊字符因为上面的代码显示的问题所以展示为空我用pycharm显示的是所以至于如何显示完全取决于你的环境不过在调用print打印的时候python会尽量将其转换成对应的字符展示所以有了能你看到的是奇怪的符号甚至是空。这都不奇怪其实你有如果打开一个二进制文件的经验看到的都是乱七八糟的东西因为编辑器需要展示出文本给你看但是这些都是二进制所以编辑器会根据自己的映射来展示字符。关于二进制文件我们会在后续章节解释这里就不过多的解释了。k.八进制\000八进制在讲整数的时候也讲过是0o开头这是python3的写法在python2是0开头。为什么会有八进制的存在这是个历史遗留问题和制表符一样目前我们已经几乎不会用到了和上面的原因一样为了能后看懂这个含义我们还是有必要再说明一下。和16进制的转义不同八进制的形式更灵活\后面可以跟1-3个数字这三个数字必须是小于等于7的我们看下代码就明白了a \102 b \367 c \200 d \1102ello e \191 f \47ht #g \9 print(a{},b{},c{},d{},e{},f{}.format(a, b, c, d, e,f)) #输出结果aB,b÷,c,dH2ello,e91,fht上面代码abcd从8进制重新写了16进制和之前的16进制表示是一样的只是d我多加了一个数字来说明8进制最多三位数字如果\后面跟了四位数字那么只转义前面三个后面的数字按照正常显示。对于e说明了两个问题一是\可以跟一位数字二是遇到不属于8进制的数字则转义停止所以打印出来是一个控制字符加两个数字91控制字符不可打印所以看上去像是没有。f说明八进制可以是两位数字打印的是一个单引号g会给你一个警告告诉你无法转义。可能说到这里有人对8进制的转换感兴趣如果你没有读过我之前的文章那我建议你阅读我之前两篇文章来了解各种进制以及内存中是如何表示的pyhon-整数以及内存表示方式 pyhon-十进制二进制八进制十六进制相互转换l.空字符\0如果你学过c语言你肯定知道c语言的字符串是以\0结尾的这表示字符串的结束。还有就是以\0做占位符例如在网络协议的数据填充和底层的内存数据的占位。他只是占位符也是不可打印的在python中你可以认为是我们的Null看下面的代码a a\0b print(a{}len(a){}.format(a,len(a))) #输出结果aablen(a)3字符串的长度是3说明\0确实起到了占位的作用我们看到这个字符是无法打印的当然在pycharm中运行你可以看到确实输出了一个奇怪的字符这并不矛盾。这个\0非必要也尽量不要使用除非你在编写底层的代码要不然你是没有机会用到的。m.Unicode 数据库ID\N{数据库的名称}这个转义的意思是根据unicode数据库中的字符的名称来获取字符。我们看一下下面的例子a \N{LATIN SMALL LETTER A} b \N{SMILING FACE WITH SMILING EYES} print(a{}len(a){},b{},len(b){}.format(a, len(a), b, len(b))) #输出结果:aalen(a)1,b,len(b)1 print(ord(a){}ord(b){}.format(ord(a), ord(b))) #输出结果:ord(a)97ord(b)128522a是小写字母ab是一个笑脸的符号它们的长度都是1。这个里我们引入了一个ord函数这个函数是返回字母对应的uniode的码点值我们看到a是97而这个笑脸符号是128522这个远远大于asc码的数值范围所以肯定不是asc编码。虽然我们还没有谈到unicode但是为了不让你现在感觉迷茫我来简单的说明一下编码unicode以及pyhon内存这三者之间的关系。我们最熟悉的编码是ascII还有latin-1然后我还听说过UTF-8UTF-16UTF-32这些说的都是编码方式。而Unicode只是给全世界的所有字符分配了一个全球唯一的码点值所以才有unicode数据库的说法通过码点值你可以找到这个字符。所以对于一个unicode码点UTF-8UTF-16UTF-32编码后的值可能是不同的当你想讲一个文本发送到网络或者保存到文件中的时候你首先得指定编码方式我们用的最多的是UTF-8相反当你从网路中读取字符串或者从文件中读取字符串的时候你就得指定和原来编码方式一样的解码方式这样解码后的值才能正确的和unicode码点值对应这样你才能得到你想要的字符串的内容。那么python的内存存储的值是什么呢是码点值么答案是否定的python有自己的一套编码方式它不同于UTF-8UTF-16UTF-32它很灵活目的就是最大限度的节约内存。所以在内存中存储的值有可能和码点值相同有可能和码点值不同。对于同一个字符不同的编码方式得到的值可能就是不相同的但是它们解码后对应同一个码点值。这个问题就谈到这里回头讲unicode的时候我们再详细的说。n.Uicode字符\uhhhh(16位)和\Uhhhhhhhh(32位)这个是专门为Unicode设计的表示方式其实这个\xhh没什么本质的区别都是表示一个数字只是表示的范围不同。\xhh只能用8bit来表是\uhhhh只能用16bit表示\Uhhhhhhhh只能用32bit表示。注意这个只是单纯的数值表示和编码方式UTF-16和UTF-32没什么关系。看一下下面的代码a \x42 b \102 c \u0042 d \U00000042 e ord(B) print(a{},b{},c{},d{},e{}.format(a, b, c, d, e)) #输出结果aB,bB,cB,dB,e66我们用了六进制八进制unicode 16位和unicode 32位来表是同一个字符它们转化成十进制都是66,B码点值也是66。这不是巧合对于python内部来讲用户输入的字母的值就是码点值这个码点值在python内部如何存储这个就是python内部的机制了可能等于码点值可能不等于码点值但是对于用户来讲你在字符串输入的值就是码点值。只有当你把你的字符串写入外部文件中你才需要编码同样从外部文件读取字符你才需要解码。其他的时候你可以认为你直接和码点值打交道因为python内部默认实现了这种转换。你同ord函数返回的是码点值同样你写入字符串的值也是码点值可以这么认为如果只是在python内部用可以不考虑编解码的问题因为你的字符串python已经为你自动编解码了这不需要你操心。讲到这里我们这一章就结束了。我相信很多书本在讲转义字符的时候只是列一个表格并不会做特别详尽的说明这也是我在学习python的时候遇到的问题。当然其中的有些内容仍略显简单这也是没有办法的事情要讲的大而全又很详细这太难了我们留待以后再说。这一章我们主要就学习了字面量的含义以及转义字符下一章我们再谈谈字符串的一些操作方法。