วิธีทำให้ตัวแปล python จัดการอักขระที่ไม่ใช่ ASCII ในการดำเนินการสตริงได้อย่างถูกต้อง?


103

ฉันมีสตริงที่ดูเหมือน:

6 918 417 712

วิธีตัดที่ชัดเจนในการตัดแต่งสตริงนี้ (ตามที่ฉันเข้าใจ Python) คือการบอกว่าสตริงอยู่ในตัวแปรที่เรียกว่าsเราได้รับ:

s.replace('Â ', '')

ที่ควรทำเคล็ดลับ แต่แน่นอนมันบ่นว่าอักขระที่ไม่ใช่ ASCII '\xc2'ในไฟล์ blabla.py ไม่ได้เข้ารหัส

ฉันไม่เคยเข้าใจวิธีการสลับระหว่างการเข้ารหัสต่างๆ

นี่คือรหัสจริงๆมันเหมือนกับด้านบน แต่ตอนนี้มันอยู่ในบริบท ไฟล์จะถูกบันทึกเป็น UTF-8 ในแผ่นจดบันทึกและมีส่วนหัวต่อไปนี้:

#!/usr/bin/python2.4
# -*- coding: utf-8 -*-

รหัส:

f = urllib.urlopen(url)

soup = BeautifulSoup(f)

s = soup.find('div', {'id':'main_count'})

#making a print 's' here goes well. it shows 6Â 918Â 417Â 712

s.replace('Â ','')

save_main_count(s)

ไม่ไกลเกินs.replace...


1
ลองคำตอบทั้ง 4 ข้อแล้ว ไม่ไป. ยังคงได้รับ UnicodeDecodeError: ตัวแปลงสัญญาณ 'ascii' ไม่สามารถถอดรหัสไบต์ 0xc2 ในตำแหน่ง 1: ลำดับไม่อยู่ในช่วง (128)
adergaard

สตริง Unicode ของคุณต้องนำหน้าu
SilentGhost

@SilentGhost: อย่างที่คุณเห็นไม่มีทางแน่ใจได้ว่าเป็นสตริงยูนิโคด ฉันได้รับสตริงที่มีเนื้อหาดังที่แสดงด้านบน แต่มีสตริงที่ไม่ใช่ ascii นั่นคือปัญหาที่แท้จริง ฉันเดาว่ามันเป็น Unicode เนื่องจากมันไม่ได้อยู่ใน 128 ตัวแรก
adergaard

ข้อผิดพลาดไม่เกี่ยวข้องกับสตริงขาเข้า เป็นสตริงในรหัสของคุณที่ทำให้เกิดข้อผิดพลาดนี้!
SilentGhost

2
ฉันจะพนันได้เลยว่านี่คือสาเหตุที่ Python 3 เข้มงวดเกี่ยวกับความแตกต่างระหว่างสตริงและลำดับไบต์เพียงเพื่อหลีกเลี่ยงความสับสนแบบนี้
Mark Ransom

คำตอบ:


83

Python 2 ใช้asciiเป็นการเข้ารหัสเริ่มต้นสำหรับไฟล์ต้นทางซึ่งหมายความว่าคุณต้องระบุการเข้ารหัสอื่นที่ด้านบนของไฟล์เพื่อใช้อักขระ unicode ที่ไม่ใช่ ascii ในตัวอักษร Python 3 ใช้utf-8เป็นการเข้ารหัสเริ่มต้นสำหรับซอร์สไฟล์ดังนั้นจึงเป็นปัญหาน้อยกว่า

ดู: http://docs.python.org/tutorial/interpreter.html#source-code-encoding

ในการเปิดใช้งานการเข้ารหัสแหล่งที่มา utf-8 สิ่งนี้จะอยู่ในหนึ่งในสองบรรทัดแรก:

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

ข้างต้นอยู่ในเอกสาร แต่ก็ใช้ได้เช่นกัน:

# coding: utf-8

ข้อควรพิจารณาเพิ่มเติม:

  • ไฟล์ต้นฉบับจะต้องถูกบันทึกโดยใช้การเข้ารหัสที่ถูกต้องในโปรแกรมแก้ไขข้อความของคุณด้วย

  • ใน Python 2 ลิเทอรัลยูนิโคดต้องมีเครื่องหมายนำuหน้าเช่นเดียวกับs.replace(u"Â ", u"")ใน Python 3 เพียงแค่ใช้เครื่องหมายคำพูด ใน Python 2 คุณสามารถfrom __future__ import unicode_literalsรับลักษณะการทำงานของ Python 3 ได้ แต่โปรดทราบว่าสิ่งนี้มีผลต่อโมดูลปัจจุบันทั้งหมด

  • s.replace(u"Â ", u"")จะล้มเหลวเช่นกันหากsไม่ใช่สตริงยูนิโคด

  • string.replace ส่งคืนสตริงใหม่และไม่ได้แก้ไขในสถานที่ดังนั้นตรวจสอบให้แน่ใจว่าคุณใช้ค่าส่งคืนด้วย


4
# coding: utf-8จริงๆแล้วคุณต้องการเพียง -*-ไม่ได้มีไว้เพื่อการตกแต่ง แต่คุณไม่น่าจะต้องการมัน ฉันคิดว่ามันมีไว้สำหรับหอยเก่า
fmalina

157
def removeNonAscii(s): return "".join(filter(lambda x: ord(x)<128, s))

แก้ไข: แรงกระตุ้นแรกของฉันคือการใช้ตัวกรองเสมอ แต่การแสดงออกของตัวสร้างนั้นมีประสิทธิภาพหน่วยความจำมากกว่า (และสั้นกว่า) ...

def removeNonAscii(s): return "".join(i for i in s if ord(i)<128)

โปรดทราบว่าสิ่งนี้รับประกันว่าจะใช้งานได้กับการเข้ารหัส UTF-8 (เนื่องจากไบต์ทั้งหมดในอักขระแบบหลายไบต์มีบิตสูงสุดที่กำหนดไว้ที่ 1)


1
ฉันได้รับ: TypeError: ord () คาดหวังอักขระ แต่พบสตริงความยาว 2
Ivelin

@Ivelin นั่นเป็นเพราะ "อักขระ" ไม่ได้ถูกตีความว่าเป็น Unicode ที่เหมาะสม ... ตรวจสอบว่าสตริงต้นทางของคุณมีคำนำหน้าuว่าเป็นตัวอักษรหรือไม่
fortran

35
>>> unicode_string = u"hello aåbäcö"
>>> unicode_string.encode("ascii", "ignore")
'hello abc'

4
ฉันเห็นคะแนนโหวตที่คุณได้รับ แต่เมื่อฉันลองมันกลับบอกว่า: ไม่ UnicodeDecodeError: ตัวแปลงสัญญาณ 'ascii' ไม่สามารถถอดรหัสไบต์ 0xc2 ในตำแหน่ง 1: ลำดับไม่อยู่ในช่วง (128) เป็นไปได้ไหมว่าสายอักขระต้นกำเนิดของฉันไม่อยู่ใน Unicode ไม่ว่าในกรณีใด ๆ มันต้องการ
adergaard

2
ดีขอบคุณ ฉันขอแนะนำให้ใช้. decode () กับผลลัพธ์เพื่อรับมันในการเข้ารหัสดั้งเดิมได้ไหม
AkiRoss

หากคุณได้รับ UnicodeDecodeError: 'ascii' ให้ลองแปลงสตริงเป็นรูปแบบ '' UTF-8 'ก่อนที่จะใช้ฟังก์ชันการเข้ารหัส
Sateesh

16

รหัสต่อไปนี้จะแทนที่อักขระที่ไม่ใช่ ASCII ทั้งหมดด้วยเครื่องหมายคำถาม

"".join([x if ord(x) < 128 else '?' for x in s])

ด้วยความอยากรู้อยากเห็นฉันอยากรู้ว่ามีเหตุผลอะไรที่จะแทนที่ด้วยเครื่องหมายคำถามหรือไม่?
Mohsin


5

สายเกินไปสำหรับคำตอบ แต่สตริงเดิมเป็น UTF-8 และ "\ xc2 \ xa0" คือ UTF-8 สำหรับ NO-BREAK SPACE เพียงแค่ถอดรหัสสตริงเดิมเป็นs.decode('utf-8')(\ xa0 แสดงเป็นช่องว่างเมื่อถอดรหัสไม่ถูกต้องเป็น Windows-1252 หรือ latin-1:

ตัวอย่าง (Python 3)

s = b'6\xc2\xa0918\xc2\xa0417\xc2\xa0712'
print(s.decode('latin-1')) # incorrectly decoded
u = s.decode('utf8') # correctly decoded
print(u)
print(u.replace('\N{NO-BREAK SPACE}','_'))
print(u.replace('\xa0','-')) # \xa0 is Unicode for NO-BREAK SPACE

เอาต์พุต

6 918 417 712
6 918 417 712
6_918_417_712
6-918-417-712

3
#!/usr/bin/env python
# -*- coding: utf-8 -*-

s = u"6Â 918Â 417Â 712"
s = s.replace(u"Â", "") 
print s

สิ่งนี้จะพิมพ์ออกมา 6 918 417 712


ไม่ UnicodeDecodeError: ตัวแปลงสัญญาณ 'ascii' ไม่สามารถถอดรหัสไบต์ 0xc2 ในตำแหน่ง 1: ลำดับไม่อยู่ในช่วง (128) เป็นไปได้ไหมว่าสายอักขระต้นกำเนิดของฉันไม่อยู่ใน Unicode ไม่ว่าในกรณีใด ๆ ฉันคงทำอะไรผิด
adergaard

@adergaard คุณเพิ่ม # - - coding: utf-8 - - ที่ด้านบนของไฟล์ต้นฉบับหรือไม่
Nadia Alramli

ใช่ดูด้านบนของหน้านี้อีกครั้งฉันได้แก้ไข Questoin และใส่รหัสและความคิดเห็นส่วนหัว ขอบคุณสำหรับความช่วยเหลือ
adergaard

ฉันคิดว่าคุณจะต้องหาวิธีรับสตริงจากเอกสาร html หรือ xml ใน unicode ข้อมูลเพิ่มเติมที่นี่: diveintopython.org/xml_processing/unicode.html
อิสยาห์

2

ฉันรู้ว่ามันเป็นกระทู้เก่า แต่ฉันรู้สึกว่าจำเป็นต้องพูดถึงวิธีการแปลซึ่งเป็นวิธีที่ดีในการแทนที่รหัสอักขระทั้งหมดที่สูงกว่า 128 (หรืออื่น ๆ หากจำเป็น)

การใช้งาน : str. แปล ( ตาราง [, ผู้ลบ] )

>>> trans_table = ''.join( [chr(i) for i in range(128)] + [' '] * 128 )

>>> 'Résultat'.translate(trans_table)
'R sultat'
>>> '6Â 918Â 417Â 712'.translate(trans_table)
'6  918  417  712'

เริ่มต้นด้วยPython 2.6คุณยังสามารถตั้งค่าตารางเป็นไม่มีและใช้deleteecharsเพื่อลบอักขระที่คุณไม่ต้องการดังตัวอย่างที่แสดงในเอกสารมาตรฐานที่http://docs.python.org/library/stdtypes HTML

ด้วยสตริง Unicode ตารางการแปลไม่ใช่สตริง 256 อักขระ แต่เป็นคำสั่งที่มี ord () ของอักขระที่เกี่ยวข้องเป็นคีย์ แต่อย่างไรก็ตามการรับสตริง ascii ที่เหมาะสมจากสตริงยูนิโคดนั้นง่ายพอโดยใช้วิธีการที่ truppo กล่าวถึงข้างต้น ได้แก่ : unicode_string.encode ("ascii", "ไม่สนใจ")

โดยสรุปแล้วหากคุณจำเป็นต้องรับสตริง ascii ด้วยเหตุผลบางประการ (ตัวอย่างเช่นเมื่อคุณเพิ่มข้อยกเว้นมาตรฐานraise Exception, ascii_message) คุณสามารถใช้ฟังก์ชันต่อไปนี้:

trans_table = ''.join( [chr(i) for i in range(128)] + ['?'] * 128 )
def ascii(s):
    if isinstance(s, unicode):
        return s.encode('ascii', 'replace')
    else:
        return s.translate(trans_table)

สิ่งที่ดีในการแปลคือคุณสามารถแปลงอักขระเน้นเสียงเป็นอักขระascii ที่ไม่เน้นเสียงที่เกี่ยวข้องได้แทนที่จะลบออกหรือแทนที่ด้วย "?" สิ่งนี้มักมีประโยชน์เช่นเพื่อวัตถุประสงค์ในการจัดทำดัชนี


ฉันได้รับ: TypeError: การแมปอักขระต้องส่งคืนจำนวนเต็มไม่มีหรือยูนิโค้ด
Ivelin



0

สำหรับสิ่งที่คุ้มค่าชุดอักขระของฉันคือutf-8และฉันได้รวม# -*- coding: utf-8 -*-บรรทัด " " แบบคลาสสิกไว้ด้วย

อย่างไรก็ตามฉันพบว่าฉันไม่มี Universal Newlines เมื่ออ่านข้อมูลนี้จากหน้าเว็บ

ข้อความของฉันมีสองคำคั่นด้วย " \r\n" ฉันแค่แยก\nและเปลี่ยนไฟล์"\n".

เมื่อฉันวนซ้ำและเห็นตัวละครที่เป็นปัญหาฉันก็ตระหนักถึงความผิดพลาด

ดังนั้นอาจอยู่ในชุดอักขระASCIIแต่เป็นอักขระที่คุณไม่คาดคิด

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.