深入解析Python字符串:从Unicode原理到跨语言对比
本文深入解析Python字符串的本质,阐述其作为Unicode码点不可变序列的定义。详细梳理ASCII、GB2312到Unicode及UTF-8的编码演进,并对比Python与Java在字符串定义、多行支持及字符处理上的差异,帮助开发者理解底层原理与跨语言特性。
在《详解Python拼接字符串的七种方式》一文中,我曾指出字符串是程序员日常工作中无法回避的核心要素。随后,我注意到一句英文表述:
There are few guarantees in life: death, taxes, and programmers needing to deal with strings. 这句话将程序员处理字符串视为与死亡和纳税同等确定的必然事件,足见其重要性之高。
回顾其他资料,这种观点得到了进一步印证。我在过往内容中已零散提及字符串的多个维度,包括文件读写、打印输出、不可变性、Intern机制、拼接方式以及关于字符串是否会被取消的讨论等。然而,这些仅触及了字符串概念的冰山一角。
鉴于此,我决定专门撰写一篇关于Python字符串的文章。本文内容可能较为基础,并非什么“高级技巧”或“冷门知识”,而是作为一份温故知新的学习笔记。
1 Python字符串是什么?
根据维基百科的定义,字符串是由零个或多个字符组成的有限序列。而在Python 3中,其定义更为明确:字符串是由Unicode码点组成的不可变序列(Strings are immutable sequences of Unicode code points.)。
作为一种序列类型,字符串支持所有序列类型通用的操作:
# 以下的s、t皆表示序列,x表示元素 x in s # 若s包含x,返回True,否则返回False x not in s # 若s包含x,返回False,否则返回True s + t # 连接两个序列 s * n # s复制n次 s[i] # s的索引第i项 s[i:j] # s切片从第i项到第j-1项 s[i:j:k] # s切片从第i项到第j-1项,间隔为k len(s) # s的长度 min(s) # s的最小元素 max(s) # s的最大元素 s.index(x) # x的索引位置 s.count(x) # s中出现x的总次数
字符串序列还具备一些特有的操作方法,受限于篇幅,此处暂不展开。预告一下,下一篇《你真的知道Python的字符串怎么用吗?》将对此进行详细介绍,敬请期待……
字符串序列属于不可变序列,这意味着它不能像可变序列那样进行就地修改。例如,在字符串“Python”后拼接“Cat”得到“PythonCat”,新生成的字符串是一个独立对象,与原字符串“Python”之间没有关联。
basename = "Python" myname = basename + "Cat" id(basename) == id(myname) >>> False
# 作为对比,列表能就地修改
baselist = ["Python"]
baselist.append("Cat")
print(baselist) >>> ['Python', 'Cat']
字符串序列与其他序列(如列表、元组)的区别在于,其“元素”被限定为Unicode码点。那么,什么是Unicode码点?简单来说,它是通过Unicode编码表示的字符。而字符又是指人类书写系统中的各类符号,包括阿拉伯数字、拉丁字母、中文、日文、藏文、标点符号、控制符号(如换行符、制表符)以及其他特殊符号(如@#¥%$*等)。至于Unicode编码,它又称万国码或国际码,是一种适用范围最广的、将书写字符映射为计算机数字的标准。
众所周知,在计算机硬件底层,数据仅由0和1表示。那么,如何用这些二进制数字来表示具有文化属性的人类字符呢?面对数量庞大且不断增长的字符集,何种编码方案最为可靠?
历史上,人类创造了多种字符编码标准。例如1963年推出的ASCII编码,主要涵盖西欧语言字符,其局限在于仅能编码128个字符;又如1981年中国推出的GB2312编码,在兼容ASCII的基础上增加了对日文、俄文等的支持,但仍存在编码范围有限的问题,无法表示古汉语、繁体字及更多书写系统的字符。
Unicode编码标准于1991年推出,目前已迭代至第11版,能够编码146种书写系统中的130000个字符,可谓无所不包,名副其实的“国际码”。Unicode本质上是一个二进制字符集,建立了从书写字符到唯一数字的映射关系。然而,由于各系统平台对字符的理解存在差异,且出于节省存储空间的考虑,Unicode编码还需经过一次转换,生成新的二进制数字作为实际存储及网络传输时的编码格式。
这种转换方式被称为Unicode转换格式(Unicode Transformation Format,简称UTF),细分为UTF-8、UTF-16、UTF-32等。其中,UTF-8最为常用,因为它是可变长度编码方案,针对不同字符使用不同字节数进行编码:例如英文字母仅需一个字节(8个比特),而复杂的汉字则需三个字节(24个比特)。

二进制编码串主要用于机器阅读,为方便人类理解,通常将其转换为十六进制表示。例如,“中”字的Unicode编码可表示为0x4e2d,其UTF-8编码可表示为0xe4b8ad,前缀“0x”表示十六进制,使表达更为简洁。不过,UTF-8编码结果以字节形式呈现,例如“中”字经UTF-8编码后的字节形式为\xe4\xb8\xad。
在Python中,为了区分Unicode编码与字节码,分别在字符串开头添加“u”和“b”作为标识。在Python 3中,由于Unicode已成为默认编码格式,“u”前缀被省略。
# 字符转Unicode编码
# Python3中,开头的u被省略,b不可省略
hex(ord('中')) >>> '0x4e2d'
hex(ord('A')) >>> '0x41'
# 字符转UTF-8编码(encode)
'中'.encode('utf-8') >>> b'\xe4\xb8\xad'
'A'.encode('utf-8') >>> b'A'
# Unicode编码还原成字符
chr(0x4e2d) >>> '中'
chr(0x41) >>> 'A'
# UTF-8编码还原成字符(decode)
b'\xe4\xb8\xad'.decode('utf-8') >>> '中'
b'A'.decode('utf-8') >>> 'A'
总结而言,Python 3中的字符串是由Unicode码点组成的不可变序列,即由采用Unicode标准编码的字符构成的不可变序列。Unicode编码将书写字符映射为计算机二进制数字,通常显示为十六进制;在内存运算中,字符以Unicode编码形式存在,而在写入磁盘或网络传输时,则通常采用UTF-8编码。
在Python 2中,由于历史遗留问题(Python诞生早于Unicode编码标准),编码问题曾是一大难题。鉴于Python 2已被逐步淘汰,此处不再对其进行介绍或对比。
2 Python字符串 VS Java字符串
虽然本文不讨论纵向版本差异,但我希望将Python字符串与其他编程语言进行横向对比。我认为这颇具趣味,通过跨语言比较,或许能加深我们对字符串这一概念的理解,甚至引发对“编程语言”及“编程哲学”的思考。
限于个人学识,本文仅说明两点表面特性,欢迎读者指正和补充。
(1)字符串的定义方式
Python的字符串是内置类型,使用便捷,主要有以下三种定义方式:
str_0 = '''Python字符串可以写在用三引号对内,表示多行字符串。 还可以写在单引号对内, 当然还可以写在双引号对内。 ''' str_1 = 'Python猫是一只猫' str_2 = "Python猫是一个微信公众号"
Java的字符串并非内置类型,而是属于对象,需通过String类创建。不过,鉴于字符串使用频率极高,Java特意预定义了String类,使得程序员可以简化定义:String name = "Python猫"; ,而不必这样写: String name = new String("Python猫"); 。
Java中的字符串必须使用双引号,缺乏Python中单双引号混用的灵活性。至于三引号表示的多行字符串,Java程序员曾对此羡慕不已,那种痛苦只有经历过的人才懂。以下展示Python中多行字符串的写法:
String s = "Java 的多行字符串很麻烦,\n" + "既要使用换行符,\n" + "还需要使用加号拼接";
为何Java不支持多行字符串?何时支持?此类问题在Python程序员看来或许难以理解,但它绝对能排进“Java程序员最希望实现的特性”前列。官方曾计划于Java 11实现,但在今年9月发布的Java 11中仍未包含,目前计划已推迟至Java 12。
(2)单个字符与字符序列
Java中其实也使用单引号,用于char类型,例如char c = 'A';。char是一种内置类型,表示单个Unicode编码字符。Python中没有独立的char类型,字符串类型涵盖一切。
前文提到,Python字符串是一种字符序列,而Java字符串并非序列。若要表示类似概念,需使用字符数组或字符串数组,例如:
char[] a = { 'a', 'b', 'c'};
String[] str = new String[]{"1","2","3"};
字符数组和字符串数组属于序列,但并非字符串类型,二者相互转换较为繁琐。此外,尽管Java中也有序列操作,但其能力远不及Python。仅以上述几个基础操作为例,试问Java能否实现?实现起来需耗费多少精力?
最后,关于“Python字符串到底是什么”的讨论至此结束,希望对你有所帮助。下次,我将与大家探讨“Python字符串到底怎么用”,敬请期待。希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对本站网站的支持!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













