一直遇到python编码的问题。常常抓取的网页数据信息,邮件收发信息,涉及到文字处理的,都时不时出现编码的问题。终于,觉得有必要认真了解下这个编码过程和出现对应的问题如何解决,在网上找了很多解释和文章,整理了下,并结合实践了一些,记录在这里。


1、常见字符集

ASCII及其扩展字符集
作用:表语英语及西欧语言。
位数:ASCII是用7位表示的,能表示128个字符;其扩展使用8位表示,表示256个字符。
范围:ASCII从00到7F,扩展从00到FF。
ISO-8859-1字符集
作用:扩展ASCII,表示西欧、希腊语等。
位数:8位,
范围:从00到FF,兼容ASCII字符集。
GB2312字符集
作用:国家简体中文字符集,兼容ASCII。
位数:使用2个字节表示,能表示7445个符号,包括6763个汉字,几乎覆盖所有高频率汉字。
范围:高字节从A1到F7, 低字节从A1到FE。将高字节和低字节分别加上0XA0即可得到编码。

GB2312码是中华人民共和国国家汉字信息交换用码,19815月实施,通行于大陆。新加坡等地也是用此编码。

GB 2312规定任意一个图形字符都采用两个字节表示,每个字节均采用7位编码表示,习惯上称第一个字节为高字节,第二个字节为低字节。

GB 2312将代码分别表示为94个区,对应第一个字节;每个区94个位,对应第二个字节,两个字节的值分别为区号值和位号值加32(20H),因此也称为区位码。019符号,数字区,16-87区为汉字区,10-15区,88-94区是有待进一步标准化的空白区。GB 2312最多能表示6763个汉字。编码范围为2121H-777EH,与ASCII有重叠,通行的方法是将GB码两个字节的最高位置1以示区别。


BIG5字符集
作用:统一繁体字编码。
位数:使用2个字节表示,表示13053个汉字。
范围:高字节从A1到F9,低字节从40到7E,A1到FE。

BIG-5 码是通行于台湾、香港地区的一个繁体字编码方案,俗称大五码。它并不是一个法定的编码方案,但它广泛地被应用于电脑业,尤其是国际互联网中,从而成为一种事实上的行业标准。它包括440个符号,一级汉字5401个,二级汉字7652个,共计13060个汉字。BIG5是一个双字节编码方案,其第一字节的值在16进制的A0-7E之间,第二字节在A1-FE之间。一次其第一个字节的最高位是1,第二字节的最高位可能是1,也可能是0


GBK字符集
作用:它是GB2312的扩展,加入对繁体字的支持,兼容GB2312。
位数:使用2个字节表示,可表示21886个字符。
范围:高字节从81到FE,低字节从40到FE。

GBK 向下与GB 2312编码兼容,向上支持ISO 10646.1国际标准,是前者向后者过渡过程中的一个承上启下的标准,共收录汉字21003个,符号883个,并提供1894个造字码位,简,繁体字融为一库。


GB18030字符集
作用:它解决了中文、日文、朝鲜语等的编码,兼容GBK。
位数:它采用变字节表示(1 ASCII,2,4字节)。可表示27484个文字。
范围:1字节从00到7F; 2字节高字节从81到FE,低字节从40到7E和80到FE;4字节第一三字节从81到FE,第二四字节从30到39。

GB 18030编码标准由信息产业部和国家质量技术监督局在20003月日联合发布,并作为国家标准在2001年的1月强制执行。

GB 18030是在原来GB2312-1980标准和GBK编码标准的基础上进行的扩充,增加了四字节部分的编码。它可以完全映射ISO 10646的基本平面和所有辅助平面,共有150多万码位。在ISO 10646的基本平面内,它在原来的2万多汉字的基础上增加了7000多个汉字的码位和字形。它主要目的是为了解决一些生,难,偏字的问题,以及适应出版,邮政,户政,金融,地理信息系统等迫切需要的人名,地名用字问题。

GB 18030-2000编码标准收录的字符分别一单字节,双字节和四字节编码。


UCS字符集
作用:国际标准 ISO 10646 定义了通用字符集 (Universal Character Set)。它是与UNICODE同类的组织,UCS-2和UNICODE兼容。
位数:它有UCS-2和UCS-4两种格式,分别是2字节和4字节。
范围:目前,UCS-4只是在UCS-2前面加了0×0000。
UNICODE字符集
作用:为世界650种语言进行统一编码,兼容ISO-8859-1。
位数:UNICODE字符集有多个编码方式,分别是UTF-8,UTF-16和UTF-32。
2 ,按所表示的文字分类
语言                                 字符集                                     正式名称
英语、西欧语                     ASCII,ISO-8859-1                MBCS多字节
简体中文                             GB2312                                   MBCS 多字节
繁体中文                             BIG5                                         MBCS多字节
简繁中文                             GBK                                         MBCS多字节
中文、日文及朝鲜语         GB18030                                 MBCS 多字节
各国语言                             UNICODE,UCS                    DBCS宽字节

重点说下UTF-8,这个用的最多

UTF-88-bit UnicodeTransformation Format)是一种针对Unicode的可变长度字符编码,也是一种前缀码。它可以用来表示Unicode标准中的任何字符,且其编码中的第一个字节仍与ASCII兼容,这使得原来处理ASCII字符的软件无须或只须做少部份修改,即可继续使用。因此,它逐渐成为电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。ascii编码是UTF-8的子集。

UTF-8使用一至六个字节为每个字符编码:

  1. 128个US-ASCII字符只需一个字节编码(Unicode范围由U+0000至U+007F)。
  2. 带有附加符号的拉丁文、希腊文、西里尔字母、亚美尼亚语、希伯来文、阿拉伯文、叙利亚文及它拿字母则需要二个字节编码(Unicode范围由U+0080至U+07FF)。
  3. 其他基本多文种平面(BMP)中的字符(这包含了大部分常用字)使用三个字节编码(Unicode范围由U+0800至U+FFFF)。
其他极少使用的Unicode 辅助平面的字符使用四至六字节编码(Unicode范围由U+10000至U+1FFFFF使用四字节,Unicode范围由U+200000至U+3FFFFFF使用五字节,Unicode范围由U+4000000至U+7FFFFFFF使用六字节)。

UTF- 8是UNICODE的一种变长字符编码。现在已经标准化为RFC3629。UTF-8用1到6个字节 编码UNICODE字符。如果UNICODE字符由2个字节表示,则编码成UTF-8很可能需要3个字节,而如果UNICODE字符由4个字节表示,则编 码成UTF-8可能需要6个字节。在UTF-8中,如果一个字符的字节小于0x80(128)则为ASCII字符,占一个字节,可以不用转换,因为UTF-8兼容ASCII编码

下表所列的字符串用来表示一个字符。用到那个串取决于该字符在Unicode中的序号。

U-00000000  - U-0000007F:  0xxxxxxx

U-00000080  - U-000007FF:  110xxxxx  10xxxxxx

U-00000800  - U-0000FFFF:    1110xxxx 10xxxxxx

Xxx的位置由字符编码数的二进制表示的位填入。越靠右的x具有越少的特殊意义。只用最短的那个足够表达一个字符编码的多字节串。注意在多字节串中,第一个字节的开头“1”的数目就是整个串中字节的数目。

例如:Unicode字符U+00A9=1010 1001UTF-8里的编码为:

11000010 10101001 = 0xC2   0xA9

而字符 U+2260= 0010 0010 0110 0000编码为:

11100010  10001001 10100000 = 0xE2  0x89 0xA0

这种编码的官方名字拼写为 UTF-8,其中UTF代表UCSTransformation Format


2、python 中字符串类型转换

Definitions


• Unicode string: sequence of Unicode characters
• Unicode字符串:Unicode字符序列的集合


• Python bytestring: a series of bytes which represent a sequence of characters. It's default encoding is ASCII. This is the "normal", non-Unicode string in Python <3.0.
• Python字节串:一连串代表字符串序列的字节集合.默认以ascii编码.在python3.0以下,这是一种常见的非unicode字符串.


• encoding: a code that pairs a sequence of characters with a series of bytes
• 编码:使用一连串字节来表示字符序列对的符号.


• ASCII: an encoding which handles 128 English characters
• Ascii:一种处理128个英语字符的编码方式


• UTF-8: a popular encoding used for Unicode strings which is backwards compatible with ASCII for the first 128 characters. It uses one to four bytes for each character.
• UTF-8:一种用于向后兼容ASCII前128个字符的unicode 字符串的流行编码.每个字符由一到四个字节组成.


Operations related to str and unicode objects


• unicode.encode() - converts to str
• 转换为str类型


• str.decode() - converts to Unicode
• 转换为unicode类型


• unicode(str, encoding) - converts to Unicode
• 转换str为unicode类型


• ord(c) - returns the Unicode code point of the character
• 返回字符的unicode 编码编号


• chr(i) - returns a str object for the given ASCII code (inverse of ord() for 8-bit strings)
• 对给定的ascii编码返回一个str 对象(对8-bit的字符串进行ord()反转)


• unichr(i) - returns a unicode object for the given Unicode code (inverse of ord() for Unicode strings)
• 对给定的unicode 编码返回一个unicode 对象(对unicode 字符串进行ord()反转)


类型操作表:





Python有两种字符串str object 和 Unicode object, 都可以存放字符的字节编码,但是不同的类型
转换方式:
Unicode ——》encode ——》得到str
Str ——》decode ——》得到 Unicode

decode的作用是将其他编码的字符串转换成unicode编码,如str1.decode('gb2312'),表示将gb2312编码的字符串转换成unicode编码。

encode的作用是将unicode编码转换成其他编码的字符串,如str2.encode('gb2312'),表示将unicode编码的字符串转换成gb2312编码。


Unicode编码是一种抽象编码,不能直接表示,需要转换为对应的编码才能保存到磁盘上。
函数 decode( char_set )可以实现 其它编码到 Unicode 的转换,函数 encode( char_set )实现 Unicode 到其它编码方式的转换。
除上以上的编码方法,在读写文件时还可以使用codecs的open方法在读写时进行转换



获取系统默认字符编码

>>> import sys
>>> print sys.getdefaultencoding()
ascii


3、python三种基本编码格式:

1, 源文件编码
在文件头部使用coding声明。告诉python解释器该代码文件所使用的字符集。

#/usr/bin/python
#coding: utf8

2, 内部编码
代码文件中的字符串,经过decode以后,被转换为统一的unicode格式的内部数据,类似于u'*'。unicode数据可以使用encode函数,再自由转换为其他格式的数据,相当于一个统一的平台。

字符串在Python内部的表示是unicode编码,因此,在做编码转换时,通常需要以unicode作为中间编码,即先将其他编码的字符串解码(decode)成unicode,再从unicode编码(encode)成另一种编码

在windows的idle下

>>> s = u'我是中国人'
>>> s
u'\xce\xd2\xca\xc7\xd6\xd0\xb9\xfa\xc8\xcb'
>>> type(s)
<type 'unicode'>
>>> 
>>> t = '我是中国人'
>>> t
'\xce\xd2\xca\xc7\xd6\xd0\xb9\xfa\xc8\xcb'
>>> type(t)
<type 'str'>
>>> print t
我是中国人
>>> print s
ÎÒÊÇÖйúÈË
>>> 

以文件方式运行:

#_*_encoding:utf-8 _*_


s = u'我是中国人'
t = '我是中国人'
print s
print type(s)

print t
print type(t)

>>> ================================ RESTART ================================
>>> 
我是中国人
<type 'unicode'>
我是中国人
<type 'str'>
>>> 

s的类型是unicode,t 的类型是str并可以直接print 输出,可以看出, 有时候python的idle会出现乱码,问题见:http://bugs.python.org/issue15809

>>> s
u'\xce\xd2\xca\xc7\xd6\xd0\xb9\xfa\xc8\xcb'
>>> t
'\xce\xd2\xca\xc7\xd6\xd0\xb9\xfa\xc8\xcb'
根据转换方式:

转换方式:
Unicode ——》encode ——》得到str
Str ——》decode ——》得到 Unicode

先对unicode 类型的s进行编码:

>>> s.encode('utf-8')
'\xc3\x8e\xc3\x92\xc3\x8a\xc3\x87\xc3\x96\xc3\x90\xc2\xb9\xc3\xba\xc3\x88\xc3\x8b'
对str 类型进行解码,将gb2312编码的字符串转换为unicode编码
>>> t.decode('gb2312')
u'\u6211\u662f\u4e2d\u56fd\u4eba'

则该字符串的编码就被指定为unicode了,即python的内部编码,而与代码文件本身的编码无关。因此,对于这种情况做编码转换,只需要直接使用encode方法将其转换成指定编码即可。

使用gb312作为代码文件编码

#_*_encoding:gb2312 _*_

s = u'我是中国人'
t = '我是中国人'

print s
print type(s)

print t
print type(t)

print s.encode('utf-8')
print t.decode('gb2312')
>>> ================================ RESTART ================================
>>> 
我是中国人
<type 'unicode'>
我是中国人
<type 'str'>
我是中国人
我是中国人
>>> 
使用utf-8作为代码文件编码
#_*_encoding:utf-8 _*_

s = u'我是中国人'
t = '我是中国人'

print s
print type(s)

print t
print type(t)

print s.encode('utf-8')
print t.decode('utf-8')
>>> ================================ RESTART ================================
>>> 
我是中国人
<type 'unicode'>
我是中国人
<type 'str'>
我是中国人
我是中国人
在有文件编码的情况下str 也能进行decode输出可读文字了。


如果一个字符串已经是unicode了,再进行解码则将出错,因此通常要对其编码方式是否为unicode进行判断:

isinstance(s, unicode) #用来判断是否为unicode

print语句的实现是将要输出的内容传送给操作系统,操作系统会根据系统的编码对输入的字节流进行编码,这就解释了为什么utf-8格式的字符串

s = u'我是中国人'
print s.encode('utf-8')
脦脪脢脟脰脨鹿煤脠脣
,输出的是乱码,因为

>>> s
u'\xce\xd2\xca\xc7\xd6\xd0\xb9\xfa\xc8\xcb'
用GB2312去解释,其显示的出来就是“乱码”。这里再强调一下,str记录的是字节数组,只是某种编码的存储格式,至于输出到文件或是打印出来是什么格式,完全取决于其解码的编码将它解码成什么样子。

这里再对print进行一点补充说明:将一个unicode对象传给print时,在内部会将该unicode对象进行一次转换,转换成本地的默认编码。




4、类型转换常见问题实练

1、获取当前Idle下的默认编码:

python 2.7.3 (default, Apr 202012, 22:44:07)

[GCC 4.6.3] on linux2

Type "help","copyright", "credits" or "license" for moreinformation.

>>> import sys

>>> printsys.getdefaultencoding()

Ascii


2、不同字符文件的写入

 

1)普通英文写入文件中

#!/bin/env python

#coding:utf-8

newtext = 'this is a new linefor test!\n'

fp = open('file.txt','w')

fp.write(newtext.encode('utf-8'))

fp.close()

英文写入正常如下:

lijy@loongson-desktop:~/python$python write_text.py

lijy@loongson-desktop:~/python$cat file.txt

this is a new line for test!

2)写入中文

#!/bin/env python

#coding:utf-8

newtext = '我是中国人'

fp = open('file.txt','w')

fp.write(newtext.encode('utf-8'))

fp.close()

报错:

lijy@loongson-desktop:~/python$python write_text.py

Traceback (most recent calllast):

  File "write_text.py", line 8, in<module>

    fp.write(newtext.encode('utf-8'))
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe6 in position 0: ordinal not in range(128)


修改为:去掉encode(’utf-8’),根据类型转换规则,str类型没有encode方法

#!/bin/env python

#coding:utf-8

newtext = '我是中国人\n'

fp = open('file.txt','w')

fp.write(newtext)

fp.close()

输出正常:

lijy@loongson-desktop:~/python$python write_text.py

lijy@loongson-desktop:~/python$cat file.txt

我是中国人
结论:对非U开头的汉字不需要encode(’utf-8’)


3)写入中英文混合的内容

情况一:有encode(’utf-8’)

#!/bin/env python

#coding:utf-8

newtext = 'This is a test, 我是中国人\n'

fp = open('file.txt','w')

fp.write(newtext.encode('utf-8'))

fp.close()

 
lijy@loongson-desktop:~/python$python write_text.py

Traceback (most recent calllast):

  File "write_text.py", line 9, in<module>

    fp.write(newtext.encode('utf-8'))
unicodeDecodeError: 'ascii'codec can't decode byte 0xe6 in position 16: ordinal not in range(128)

结果:报错不支持decode,根据转换规则,unicode 类型没有decode方法

 

情况二:不需要encode(’utf-8’)

#!/bin/env python

#coding:utf-8

newtext = 'This is a test, 我是中国人\n'

fp = open('file.txt','w')

fp.write(newtext)

fp.close()

lijy@loongson-desktop:~/python$python write_text.py

lijy@loongson-desktop:~/python$cat file.txt

This is a test, 我是中国人

lijy@loongson-desktop:~/python$
结果:中英文都输出正常

 

综上,对 str 类型的字符串,英文字符可以encoding(’utf-8’),或直接写入;汉字直接写入;中英文混合,直接写入;

假如去掉文本的编码方式呢?

#!/bin/env python

#coding:

newtext = 'This is a test, 我是中国人\n'

 

fp = open('file.txt','w')

fp.write(newtext)

fp.close()

 

lijy@loongson-desktop:~/python$python write_text.py

  File "write_text.py", line 5

SyntaxError: Non-ASCII character '\xe6' in file write_text.py on line 5, but no encoding declared; see http://www.python.org/peps/pep-0263.html for details

报错没有指定编码格式,中文为非ASCII字符

将coding指定为ascii,gb2312也会出现对应的报错,原因主要可能是utf-8支持中英文,而ascii不支持中文,gb2312不支持英文



四、操作不同文件的编码格式的文件

建立一个文件test.txt,文件格式用ANSI,内容为:

abc中文

用python来读取

# coding=gbk

print open("Test.txt").read()

结果:abc中文

把文件格式改成UTF-8:

结果:abc涓枃

显然,这里需要解码:

# coding=gbk

import codecs

print open("Test.txt").read().decode("utf-8")

结果:abc中文

上面的test.txt我是用Editplus来编辑的,但当我用Windows自带的记事本编辑并存成UTF-8格式时,

运行时报错:

Traceback (most recent call last):

File "ChineseTest.py", line 3, in 

print open("Test.txt").read().decode("utf-8")

UnicodeEncodeError: 'gbk' codec can't encode character u'\ufeff' in position 0: illegal multibyte sequence

原来,某些软件,如notepad,在保存一个以UTF-8编码的文件时,会在文件开始的地方插入三个不可见的字符(0xEF 0xBB 0xBF,即BOM)。

因此我们在读取时需要自己去掉这些字符,python中的codecs module定义了这个常量:

# coding=gbk

import codecs

data = open("Test.txt").read()

if data[:3] == codecs.BOM_UTF8:

data = data[3:]

print data.decode("utf-8")

结果:abc中文

五、文件的编码格式和编码声明的作用

源文件的编码格式对字符串的声明有什么作用呢?这个问题困扰一直困扰了我好久,现在终于有点眉目了,文件的编码格式决定了在该源文件中声明的字符串的编码格式,例如:

str = '哈哈'

print repr(str)

a.如果文件格式为utf-8,则str的值为:'\xe5\x93\x88\xe5\x93\x88'(哈哈的utf-8编码)

b.如果文件格式为gbk,则str的值为:'\xb9\xfe\xb9\xfe'(哈哈的gbk编码)

在第一节已经说过,python中的字符串,只是一个字节数组,所以当把a情况的str输出到gbk编码的控制台时,就将显示为乱码:鍝堝搱;而当把b情况下的str输出utf-8编码的控制台时,也将显示乱码的问题,是什么也没有,也许'\xb9\xfe\xb9\xfe'用utf-8解码显示,就是空白吧。>_<

说完文件格式,现在来谈谈编码声明的作用吧,每个文件在最上面的地方,都会用# coding=gbk 类似的语句声明一下编码,但是这个声明到底有什么用呢?到止前为止,我觉得它的作用也就是三个:

  1. 声明源文件中将出现非ascii编码,通常也就是中文;
  2. 在高级的IDE中,IDE会将你的文件格式保存成你指定编码格式。
  3. 决定源码中类似于u'哈'这类声明的将‘哈'解码成unicode所用的编码格式,也是一个比较容易让人迷惑的地方,看示例:

#coding:gbk

ss = u'哈哈'

print repr(ss)

print 'ss:%s' % ss

将这个些代码保存成一个utf-8文本,运行,你认为会输出什么呢?大家第一感觉肯定输出的肯定是:

u'\u54c8\u54c8'

ss:哈哈

但是实际上输出是:

u'\u935d\u581d\u6431'

ss:鍝堝搱

为什么会这样,这时候,就是编码声明在作怪了,在运行ss = u'哈哈'的时候,整个过程可以分为以下几步:

1) 获取'哈哈'的编码:由文件编码格式确定,为'\xe5\x93\x88\xe5\x93\x88'(哈哈的utf-8编码形式)

2) 转成 unicode编码的时候,在这个转换的过程中,对于'\xe5\x93\x88\xe5\x93\x88'的解码,不是用utf-8解码,而是用声明编码处指定的编码GBK,将'\xe5\x93\x88\xe5\x93\x88'按GBK解码,得到就是''鍝堝搱'',这三个字的unicode编码就是u'\u935d\u581d\u6431',至止可以解释为什么print repr(ss)输出的是u'\u935d\u581d\u6431' 了。

好了,这里有点绕,我们来分析下一个示例:

#-*- coding:utf-8 -*-

ss = u'哈哈'
print repr(ss)
print 'ss:%s' % ss
将这个示例这次保存成GBK编码形式,运行结果,竟然是:

UnicodeDecodeError: 'utf8' codec can't decode byte 0xb9 in position 0: unexpected code byte

这里为什么会有utf8解码错误呢?想想上个示例也明白了,转换第一步,因为文件编码是GBK,得到的是'哈哈'编码是GBK的编码'\xb9\xfe\xb9\xfe',当进行第二步,转换成 unicode的时候,会用UTF8对'\xb9\xfe\xb9\xfe'进行解码,而大家查utf-8的编码表会发现,utf8编码表(关于UTF- 8解释可参见字符编码笔记:ASCII、UTF-8、UNICODE)中根本不存在,所以会报上述错误。

二、python源文件的编码与解码,我们写的python程序从产生到执行的过程如下:
编辑器---->源代码---->解释器---->输出结果
2.1、编辑器决定源代码的编码格式(在编辑器中设定)
2.2、也必须要解释器知道源代码的编码格式(很遗憾很难从编码的数据获知源文件的编码格式)
2.3、补充:在Windows下当用UltraEdit把源代码存成UTF-8时,会在文件中记录BOM标志(不必祥究)这样ActivePython解释器会自动识别源文件是UTF-8格式,但是如果用eclipse编辑源文件,虽然在编辑器中指定文件编码为UTF-8,但是因为没有记入BOM标志,所以必须在源文件开始处加上#coding=utf-8,用注释来提示解释器源文件的编码方式挺有意思。
2.4、举例:例如我们要向终端输出"我是中国人"。
 代码如下:
#coding=utf-8                                  告诉python解释器用的是utf-8编码
print "我是中国人"                          #源文件本身也要存成UTF-8编码

三、编码的转换,两种编码的转换要用UTF-16作为中转站。
举例:如果有一个文本文件jap.txt,里面有内容 "私は中国人です。",编码格式是日文编码SHIFT_JIS,
还有一个文本文件chn.txt,内容是"中华人民共和国",编码格式是中文编码GB2312。
我们如何把两个文件里的内容合并到一起并存储到utf.txt中并且不显示乱码呢,可以采用把两个文件的内容都转成UTF-8格式,因为UTF-8里包含了中文编码和日文编码。
 代码如下:

#coding=utf-8
try:
JAP=open("e:/jap.txt","r")
CHN=open("e:/chn.txt","r")
UTF=open("e:/utf.txt","w")
jap_text=JAP.readline()
chn_text=CHN.readline()
#先decode成UTF-16,再encode成UTF-8
jap_text_utf8=jap_text.decode("SHIFT_JIS").encode("UTF-8") #不转成utf-8也可以
chn_text_utf8=chn_text.decode("GB2312").encode("UTF-8")#编码方式大小写都行utf-8也一样
UTF.write(jap_text_utf8)
UTF.write(chn_text_utf8)
except IOError,e:
print "open file error",e 


待修改和续











http://www.360doc.com/content/11/0213/01/2563193_92593666.shtml

http://www.jb51.net/article/26543.htm

http://www.jb51.net/article/26541.htm

http://www.jb51.net/article/26542.htm








posted on 2022-07-05 18:12  我在全球村  阅读(220)  评论(0编辑  收藏  举报