# 字符编码方法

大多数程序员把字符串看作是用来表示文本数据的一系列字符。但是，根据必须记录何种字符集，计算机内存中存储字符的方式有所不同。

本文仅仅涉及 Python 3.X

在 Python 3.X 中，有 $3$ 种字符串类型：

- `str`：用于 Unicode 文本 (ASCII 或其他更宽的);（ASCII 看作 Unicode 的一种简单类型）
- `bytes`：用于二进制数据 (包括编码的文本);
- `bytearray`：是一种可变的 `bytes` 类型。

## raw字符串（原始字符串)
所见即所得，例如


```
print('w\ne')
```

    w
    e
    


```
print(r'w\ne')
```

    w\ne
    


```
len('\n')   # 转义字符仅仅占用 1 个长度
```




    1




```
len(r'\n')  # 此时，将 `\` 当成一个字符
```




    2



## Unicode 字符串

- ASCII码：每个字符都是以 $7$ 位二进制数的方式存储在计算机内，ASCI 字符只能表示 $95$ 个可打印字符。
- Unicode：通过使用一个或多个字节来表示一个字符的方式突破了 ASCII 码的限制。

ASCII 标准在美国创建，并且定义了大多数美国程序员使用的文本字符串表示法。ASCII 定义了从 $0$ 到 $127$ 的字符代码，并且允许每个字符存储在一个 $8$ 位的字节中（实际上，只有其中的 $7$ 位真正用到）。例如，ASCII 标准把字符 `'a'` 映射为整数值 `97`（十六进制中 的 `0x61`），它存储在内存和文件的一个单个字节中。如果想要看到这是如何工作的， Python 的内置函数 `ord` 给出了一个字符的二进制值，并且 `chr` 针对一个给定的整数代码值 返回其字符：


```
ord('a')
```




    97




```
hex(97)
```




    '0x61'




```
chr(97)
```




    'a'



为了容纳特殊字符，一些标准允许一个8位字节中的所有可能 的值（即 $0\sim 255$）来表示字符，并且把（ASCII 范围之外的）值 $128$ 到 $255$ 分配给特殊字符。这样的一个标准叫做 Latin-1，广泛地用于西欧地区。在 Latin-1 中，$127$ 以上的字符 代码分配给了重音和其他特殊字符。例如，分配给字节值 `196` 的字符，是一个特殊标记 的非 ASCII 字符：


```
chr(196)
```




    'Ä'




```
hex(196)
```




    '0xc4'



然而，一些字母表定义了如此多的字符，以至于无法把其中的每一个都表示成一个字节。Unicode 考虑到更多的灵活性。Unicode 文 本通常叫做“宽字符”字符串，因为每个字符可能表示为多个字节。Unicode 通常用在国际化的程序中，以表示欧洲和亚洲的字符集，它们往往拥有比 $8$ 位字节所能表示的更多的字符。

更程序化地说，字节和字符串之间的来回转换由两个术语定义：

- **编码**: 是根据一个想要的编码名称，把一个字符串翻译为其原始字节形式。
- **解码**: 是根据其编码名称，把一个原始字节串翻译为字符串形式的过程。

也就是说，我们从字符串编码为原始字节，并且从原始字节解码为字符串。

由于编码的字符映射把字符分配给同样的代码以保持兼容性，因此 ASCII 是 Latin-1 和 UTF-8 的子集。也就是说，一个有效的 ASCII 字符串也是一个有效的 Latin-1 和 UTF-8 编码字符串。当数据存储到文件中的时候，这也是成立的：每个 ASCII 文件也是有效的 UTF-8 文件，因为 ASCII 是 UTF-8 的一个 $7$ 位的子集。 ASCII、Latin-1、UTF-8 以及很多其他的编码，都被认为是 Unicode。


```
import encodings

help(encodings); # 查看 Python 支持的编码方式
```


```
S = 'ni' 
S.encode('ascii'), S.encode('latin1'), S.encode('utf8')
```




    (b'ni', b'ni', b'ni')




```
S.encode('utf16'), len(S.encode('utf16')) 
```




    (b'\xff\xfen\x00i\x00', 6)




```
S.encode('utf32'), len(S.encode('utf32')) 
```




    (b'\xff\xfe\x00\x00n\x00\x00\x00i\x00\x00\x00', 12)



## 文本和二进制文件 

- 文 本 文 件 当一个文件以文本模式打开的时候，读取其数据会自动将其内容解码（每个平台一个默认的或一个提供的编码名称），并且将其返回为一个`str`，写入会接受一个 `str`，并且在将其传输到文件之间自动编码它。文本模式的文件还支持统一的行尾 转换和额外的编码特定参数。根据编码名称，文本文件也自动处理文件开始处的字节顺序标记序列。
- 二 进 制 文 件 通过在内置的`open` 调用的模式字符串参数添加一个 `b`（只能小写），以二进制模式 打开一个文件的时候，读取其数据不会以任何方式解码它，而是直接返回其内容 `raw` 并且未经修改，作为一个 `bytes` 对象；写入类似地接受一个 `bytes` 对象，并且将其传送到文件中而未经修改。二进制模式文件也接受一个bytearray对象作为写入 文件中的内容。

由于 `str` 和 `bytes` 之间的语言差距明显，所以必须确定数据本质上是文本或二进制，并且 在脚本中相应地使用 `str` 或 `bytes` 对象来表示其内容。最终，以何种模式打开一个文件将 决定脚本使用何种类型的对象来表示其内容：

- 如果正在处理图像文件，其他程序创建的、而且必须解压的打包数据，或者一些设备数据流，则使用 `bytes` 和二进制模式文件处理它更合适。
- 如果想要更新数据而不在内存中产生其副本，也可以选择使用 `bytearray`。 
- 如果你要处理的内容实质是文本的内容，例如程序输出、HTML、国际化文本或 CSV 或 XML 文件，可能要使用 `str` 和文本模式文件。

注意，内置函数 `open` 的模式字符串参数（函数的第二个参数）在 Python 3.X 中变得至关重要，因为其内容不仅指定了一个文件处理模式，而且暗示了一个 Python 对象类型。 通过给模式字符串添加一个 `b`，我们可以指定二进制模式，并且当读取或写入的时候， 将要接收或者必须提供一个 `bytes` 对象来表示文件的内容。没有 `b`，我们的文件将以文本模式处理，并且将使用 `str` 对象在脚本中表示其内容。例如，模式 `rb`、`wb` 和 `rb+` 暗示 `bytes`，而 `r`、`w+` 和 `rt` 暗示 `str`。 

文本模式文件也处理在某种编码方案下可能出现在文件开始处的字节顺序标记（byte order marker，BOM）序列。例如，在 UTF-16 和 UTF-32 编码中，BOM指定大尾还是小尾格式（基本上，是确定一个位字符串的哪一端最重要）。


```
B = b'spam'               # 3.X bytes literal make a bytes object (8-bit bytes) 
S = 'eggs'                # 3.X str literal makes a Unicode text string
type(B), type(S) 
```




    (bytes, str)




```
B
```




    b'spam'




```
S
```




    'eggs'



`bytes` 对象实际上是较小的整数的一个序列，尽管它尽可能地将自己的内容打印为字符：


```
 B[0], S[0] 
```




    (115, 'e')




```
 B[1:], S[1:]  # Slicing makes another bytes or str object
```




    (b'pam', 'ggs')




```
list(B), list(S) 
```




    ([115, 112, 97, 109], ['e', 'g', 'g', 's'])



# Python转义字符
在需要在字符中使用特殊字符时，python用反斜杠(`\`)转义字符。如下表：

|转义字符|描述|
|:-|:-|
|`\`(在行尾时)|续行符|
|`\\`|反斜杠符号|
|`\'`|单引号|
|`\"`|双引号|
|`\a`|响铃|
|`\b`|退格(Backspace)|
|`\e`|转义|
|`\000`|空|
|`\n`|换行|
|`\v`|纵向制表符|
|`\t`|横向制表符|
|`\r`|回车|
|`\f`|换页|
|`\oyy`|八进制数，yy代表的字符，例如：`\o12`代表换行|
|`\xyy`|十六进制数，yy代表的字符，例如：`\x0a`代表换行|

# 格式化操作

python字符串格式化符号:

|符   号|描述|
|:-|:-|
|`%c`|转换成字符（ASCII码值或长度为1的字符串）|
|`%s`|优先使用`str()`函数进行字符串转换| 
|`%r`|优先使用`repr()`函数进行字符串转换| 
|`%u`|转换为无符号十进制|
|`%d`|转换为有符号十进制|
|`%o`|转换为无符号八进制数|
|`%x`/`%X`|转换为无符号十六进制数|
|`%f`|转换为浮点数字，可指定小数点后的精度|
|`%e`/`%E`|用科学计数法格式化浮点数|
|`%g`|`%f和%e`的简写|
|`%G`|`%f` 和 `%E` 的简写|
|`%p`|用十六进制数格式化变量的地址|
|`%%`|输出`%`|


----

格式化操作符辅助指令:

|符号|功能|
|:-|:-|
|`*`|定义宽度或者小数点精度|
|`-`|用做左对齐|
|`+`|在正数前面显示加号(`+` )| 
|`<sp>`|在正数前面显示空格| 
|`#`|在八进制数前面显示零(`'0'`)，在十六进制前面显示`'0x'`或者`'0X'`(取决于用的是`'x'`还是`'X'`)|
|`0`|显示的数字前面填充`'0'`而不是默认的空格| 
|`%`|`'%%'`输出一个单一的`'%'` |
|`(var)`|映射变量(字典参数)| 
|`m.n.`|`m` 是显示的最小总宽度,`n` 是小数点后的位数(如果可用的话)|

形式：     
`format%values`

values的输入形式：     
- 元组形式
- 字典形式（键作为format出现，字典作为values存在）


```
print('hello %s, %s enough!'%('world','happy'))
```


    hello world, happy enough!
    





```
print('int：%d,str:%s,str:%s'%(1.0,['in list','i am list'],'i am str'))
```


    int：1,str:['in list', 'i am list'],str:i am str
    





```
'%x'%100
```


    '64'







```
'%X'%110
```


    '6E'







```
'we are at %d%%'%100
```


    'we are at 100%'







```
'%s is %d years old'%('Li',20)
```


    'Li is 20 years old'







```
for i in range(1000,10000):
    a=int(i/1000)
    b=int(i/100)%10
    c=(int(i/10))%10
    d=i%10
    if a**4+b**4+c**4+d**4==i:
        print('%d=%d^4+%d^4+%d^4+%d^4'%(i,a,b,c,d))
```


    1634=1^4+6^4+3^4+4^4
    8208=8^4+2^4+0^4+8^4
    9474=9^4+4^4+7^4+4^4
    



## m.n 宽度与精度


```
'%.3f'%123.12345
```


    '123.123'







```
'%.5s'%'hello world'
```


    'hello'







```
'%+d'%4
```


    '+4'







```
'%+d'%-4
```


    '-4'







```
from math import pi
'%-10.2f'%pi
```


    '3.14      '







```
'%10.4f'%pi
```


    '    3.1416'







```
'My name is %(name)s,age is %(age)d,gender is %(gender)s'%{'name':'LiMing','age':28,'gender':'male'}
```


    'My name is LiMing,age is 28,gender is male'





# 字符串模板
字符串对象Template对象存在与string模块中：
  + 使用美元符号`$`定义`代替换的参数`
  + 使用`substitute()`方法(缺少参数时会报错，KeyError异常) & `safe_substitute()`方法（缺少key时，直接显示参数字符串）进行参数替换      

示例：


```
from string import Template
s=Template('There are ${how_many} nodes in the ${tree}')
print(s.substitute(how_many=32,tree='splay_tree'))
```


    There are 32 nodes in the splay_tree
    





```
print(s.substitute(how_many=32))
```


    ---------------------------------------------------------------------------

    KeyError                                  Traceback (most recent call last)

    <ipython-input-4-6c5e84463638> in <module>()
    ----> 1 print(s.substitute(how_many=32))
    

    D:\ProgramData\Anaconda3\lib\string.py in substitute(*args, **kws)
        124             raise ValueError('Unrecognized named group in pattern',
        125                              self.pattern)
    --> 126         return self.pattern.sub(convert, self.template)
        127 
        128     def safe_substitute(*args, **kws):
    

    D:\ProgramData\Anaconda3\lib\string.py in convert(mo)
        117             named = mo.group('named') or mo.group('braced')
        118             if named is not None:
    --> 119                 return str(mapping[named])
        120             if mo.group('escaped') is not None:
        121                 return self.delimiter
    

    KeyError: 'tree'






```
from string import Template
s=Template('There are ${how_many} nodes in the ${tree}')
print(s.safe_substitute(how_many=32))
```


    There are 32 nodes in the ${tree}
    



# Python 的字符串常用内建函数
## 1 `S.capitalize()` 返回一个首字母大写的字符串(`str`)：


```
a = "this is string example from runoob...wow!!!"
print ("a.capitalize() : ", a.capitalize())
```


    a.capitalize() :  This is string example from runoob...wow!!!
    



## 2 `S.lower ->  Return a copy of the string S converted to lowercase`


```
'HGKFKF'.lower()
```


    'hgkfkf'





## 3 `S.center(width[, fillchar])`
参数：
   - `width`:字符串的总宽度
   - `fillchar`填充字符(默认为空格)
   
返回值：   
- 一个指定的宽度`width`居中的字符串
- 如果`width`小于字符串宽度直接返回字符串，否则使用`fillchar`去填充


```
st = "[www.runoob.com]"
print ("st.center(40, '%') : ", st.center(40, '%'))
```


    st.center(40, '%') :  %%%%%%%%%%%%[www.runoob.com]%%%%%%%%%%%%
    



## 4 `S.count(sub[, start[, end]]) ` 该方法返回子字符串在字符串中出现的次数
- sub -- 搜索的子字符串
- start -- 字符串开始搜索的位置。默认为第一个字符。
- end -- 字符串中结束搜索的位置。默认为字符串的最后一个位置。


```
st="www.runoob.com"
sub='o'
print ("st.count('o') : ", st.count(sub))

sub='run'
print ("st.count('run', 0, 10) : ", st.count(sub,0,10))
```


    st.count('o') :  3
    st.count('run', 0, 10) :  1
    



## 5 `bytes.decode(self, /, encoding='utf-8', errors='strict')`
- encoding -- 要使用的编码，如"UTF-8"。
- errors -- 设置不同错误的处理方案。默认为`'strict'`,意为编码错误引起一个`UnicodeError`。 其他可能得值有 `'ignore'`, `'replace'`, `'xmlcharrefreplace'`, `'backslashreplace'` 以及通过 `codecs.register_error()` 注册的任何值。

返回该方法返回解码后的字符串。

### 6 `S.encode(encoding='utf-8', errors='strict') -> bytes`
Python3 中`str`没有`decode`方法,但是可以使用`bytes`对象的`decode()`方法来解码给定的`bytes`对象，这个`bytes`对象可以由`str.encode()`来编码返回。


```
s = "中国"
s_utf8 = s.encode("UTF-8")
s_gbk = s.encode("GBK")

print(s)

print("UTF-8 编码：", s_utf8)
print("GBK 编码：", s_gbk)

print("UTF-8 解码：", s_utf8.decode('UTF-8','strict'))
print("GBK 解码：", s_gbk.decode('GBK','strict'))
```


    中国
    UTF-8 编码： b'\xe4\xb8\xad\xe5\x9b\xbd'
    GBK 编码： b'\xd6\xd0\xb9\xfa'
    UTF-8 解码： 中国
    GBK 解码： 中国
    



## 7 `S.find(sub[, start[, end]]) -> int`
如果包含子字符串返回开始的索引值，否则返回-1。


```
str1 = "Runoob example....wow!!!"
str2 = "exam";
 
print (str1.find(str2))
print (str1.find(str2, 5))
print (str1.find(str2, 10))
```


    7
    7
    -1
    





```
info = 'abca'
print(info.find('a'))      # 从下标0开始，查找在字符串里第一个出现的子串，返回结果：0

print(info.find('a', 1))   # 从下标1开始，查找在字符串里第一个出现的子串：返回结果3

print(info.find('3'))      # 查找不到返回-1
```


    0
    3
    -1
    



## 8 `S.join(iterable) -> str`  
Return a string which is the concatenation of the strings in the
iterable.

关于`iterable`参考[python——聊聊iterable，sequence和iterators](http://www.cnblogs.com/Simon-xm/p/3979161.html)


```
s1 = "-"
s2 = ""
seq = ("r", "u", "n", "o", "o", "b") # 字符串序列
print (s1.join( seq ))
print (s2.join( seq ))
```


    r-u-n-o-o-b
    runoob
    



## 9 `S.strip([chars]) -> str`
- Return a copy of the string S with leading and trailing whitespace removed.
- If chars is given and not None, remove characters in chars instead.


```
st = "     this is string example....wow!!!     ";
print( st.lstrip() );
st = "88888888this is string example....wow!!!8888888";
print( st.lstrip('8') );
print( st.strip('8') );
```


    this is string example....wow!!!     
    this is string example....wow!!!8888888
    this is string example....wow!!!
    



##  10 `S.replace(old, new[, count]) -> str`
- Return a copy of S with all occurrences of substring old replaced by new.  
- If the optional argument count is given, only the first count occurrences are replaced.


```
st = "www.w3cschool.cc"
print ("菜鸟教程新地址：", st)
print ("菜鸟教程新地址：", st.replace("w3cschool.cc", "runoob.com"))

st = "this is string example....wow!!!"
print (st.replace("is", "was", 3))
```


    菜鸟教程新地址： www.w3cschool.cc
    菜鸟教程新地址： www.runoob.com
    thwas was string example....wow!!!
    



## 11 `S.split(sep=None, maxsplit=-1) -> list of strings`
- Return a list of the words in S, using sep as the delimiter string.  
- If maxsplit is given, at most maxsplit splits are done. 
- If sep is not specified or is None, any whitespace string is a separator and empty strings are removed from the result.


```
st = "this is string example....wow!!!"
print (st.split( ))
print (st.split('i',1))
print (st.split('w'))
```

['this', 'is', 'string', 'example....wow!!!']
    ['th', 's is string example....wow!!!']
    ['this is string example....', 'o', '!!!']