Bagaimana cara membuat juru bahasa python menangani karakter non-ASCII dengan benar dalam operasi string?

104

Saya memiliki string yang terlihat seperti ini:

6 918 417 712

Cara yang jelas untuk memangkas string ini (seperti yang saya mengerti Python) adalah dengan mengatakan string itu dalam variabel yang disebut s, kita dapatkan:

s.replace('Â ', '')

Itu seharusnya berhasil. Tetapi tentu saja ia mengeluh bahwa karakter non-ASCII '\xc2'dalam file blabla.py tidak dikodekan.

Saya tidak pernah bisa mengerti bagaimana beralih di antara pengkodean yang berbeda.

Ini kodenya, benar-benar sama seperti di atas, tetapi sekarang ada dalam konteks. File tersebut disimpan sebagai UTF-8 di notepad dan memiliki header berikut:

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

Kode:

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)

Tidak lebih dari s.replace...

adergaard
sumber
1
Sejauh ini sudah mencoba semua dari 4 jawaban. Tidak pergi. Masih mendapatkan UnicodeDecodeError: codec 'ascii' tidak dapat mendekode byte 0xc2 di posisi 1: ordinal tidak dalam jangkauan (128)
adergaard
string unicode Anda harus diawali denganu
SilentGhost
@SilentGhost: seperti yang Anda lihat, tidak ada cara untuk memastikan itu adalah string unicode. Saya mendapatkan string yang memiliki konten yang ditunjukkan di atas, tetapi berisi string non-ascii. Itulah masalah sebenarnya. Saya menduga ini adalah unicode karena bukan di 128 pertama.
adergaard
Kesalahan tidak ada hubungannya dengan string masuk. Ini adalah string dalam kode Anda yang menimbulkan kesalahan ini!
SilentGhost
2
Saya berani bertaruh inilah mengapa Python 3 sangat ketat tentang perbedaan antara string dan urutan byte, hanya untuk menghindari kebingungan semacam ini.
Mark Ransom

Jawaban:

84

Python 2 digunakan asciisebagai pengkodean default untuk file sumber, yang berarti Anda harus menentukan pengkodean lain di bagian atas file untuk menggunakan karakter unicode non-ascii dalam literal. Python 3 digunakan utf-8sebagai pengkodean default untuk file sumber, jadi ini bukan masalah.

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

Untuk mengaktifkan encoding sumber utf-8, ini akan masuk ke salah satu dari dua baris teratas:

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

Di atas ada di dokumen, tetapi ini juga berfungsi:

# coding: utf-8

Pertimbangan tambahan:

  • File sumber harus disimpan menggunakan pengkodean yang benar di editor teks Anda juga.

  • Dalam Python 2, literal unicode harus memiliki a usebelumnya, seperti s.replace(u"Â ", u"")pada Python 3, cukup gunakan tanda kutip. Di Python 2, Anda bisa from __future__ import unicode_literalsmendapatkan perilaku Python 3, tetapi perlu diketahui bahwa ini memengaruhi seluruh modul saat ini.

  • s.replace(u"Â ", u"")juga akan gagal jika sbukan string unicode.

  • string.replace mengembalikan string baru dan tidak mengedit pada tempatnya, jadi pastikan Anda juga menggunakan nilai yang dikembalikan

Jason S
sumber
4
Anda sebenarnya hanya butuh # coding: utf-8. -*-bukan untuk dekorasi, tetapi kemungkinan besar Anda tidak akan membutuhkannya. Saya pikir itu ada di sana untuk cangkang tua.
fmalina
157
def removeNonAscii(s): return "".join(filter(lambda x: ord(x)<128, s))

sunting: dorongan pertama saya selalu menggunakan filter, tetapi ekspresi generator lebih hemat memori (dan lebih pendek) ...

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

Perlu diingat bahwa ini dijamin berfungsi dengan encoding UTF-8 (karena semua byte dalam karakter multi-byte memiliki bit tertinggi yang disetel ke 1).

fortran
sumber
1
Saya mendapatkan: TypeError: ord () mengharapkan karakter, tetapi string dengan panjang 2 ditemukan
Ivelin
@Ivelin itu karena "karakter" tidak diinterpretasikan sebagai unicode yang tepat ... periksa apakah string sumber Anda diawali dengan ujika itu literal.
fortran
35
>>> unicode_string = u"hello aåbäcö"
>>> unicode_string.encode("ascii", "ignore")
'hello abc'
truppo
sumber
4
Saya melihat suara yang Anda peroleh tetapi ketika saya mencobanya, tertulis: Tidak. UnicodeDecodeError: codec 'ascii' tidak dapat mendekode byte 0xc2 di posisi 1: ordinal tidak dalam jangkauan (128). Mungkinkah string orignal saya tidak dalam unicode? Bagaimanapun juga. itu membutuhkan
adergaard
2
Terima kasih banyak. Bolehkah saya menyarankan untuk menggunakan .decode () pada hasil untuk mendapatkannya dalam pengkodean asli?
AkiRoss
Jika Anda mendapatkan UnicodeDecodeError: 'ascii', coba ubah string menjadi format '' UTF-8 'sebelum menerapkan fungsi encoding.
Sateesh
16

Kode berikut akan mengganti semua karakter non ASCII dengan tanda tanya.

"".join([x if ord(x) < 128 else '?' for x in s])
Penglihatan
sumber
Karena penasaran, saya ingin tahu itu, Adakah alasan khusus untuk menggantinya dengan tanda tanya?
Mohsin
6

Menggunakan Regex:

import re

strip_unicode = re.compile("([^-_a-zA-Z0-9!@#%&=,/'\";:~`\$\^\*\(\)\+\[\]\.\{\}\|\?\<\>\\]+|[^\s]+)")
print strip_unicode.sub('', u'6Â 918Â 417Â 712')
Akoi Meexx
sumber
5

Terlambat untuk mendapatkan jawaban, tetapi string aslinya adalah UTF-8 dan '\ xc2 \ xa0' adalah UTF-8 untuk NO-BREAK SPACE. Cukup decode string asli sebagai s.decode('utf-8')(\ xa0 ditampilkan sebagai spasi ketika salah diterjemahkan sebagai Windows-1252 atau latin-1:

Contoh (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

Keluaran

6 918 417 712
6 918 417 712
6_918_417_712
6-918-417-712
Mark Tolonen
sumber
3
#!/usr/bin/env python
# -*- coding: utf-8 -*-

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

Ini akan dicetak 6 918 417 712

Yesaya
sumber
Nggak. UnicodeDecodeError: codec 'ascii' tidak dapat mendekode byte 0xc2 di posisi 1: ordinal tidak dalam jangkauan (128). Mungkinkah string orignal saya tidak dalam unicode? Bagaimanapun juga. Saya mungkin melakukan sesuatu yang salah.
adergaard
@adergaard, apakah Anda menambahkan # - - coding: utf-8 - - di bagian atas file sumber?
Nadia Alramli
Ya, lihat bagian atas halaman ini lagi, saya telah mengedit questoin dan memasukkan kode dan komentar header. Terima kasih atas bantuannya.
adergaard
Saya pikir Anda harus mencari cara untuk mendapatkan string dari dokumen html atau xml di unicode. Info lebih lanjut tentang itu di sini: diveintopython.org/xml_processing/unicode.html
Yesaya
2

Saya tahu ini utas lama, tetapi saya merasa harus menyebutkan metode terjemahan, yang selalu merupakan cara yang baik untuk mengganti semua kode karakter di atas 128 (atau lainnya jika perlu).

Penggunaan : str. terjemahkan ( table [, deletechars] )

>>> 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'

Dimulai dengan Python 2.6 , Anda juga dapat mengatur tabel ke Tidak Ada, dan menggunakan deletechars untuk menghapus karakter yang tidak Anda inginkan seperti pada contoh yang ditampilkan dalam dokumen standar di http://docs.python.org/library/stdtypes. html .

Dengan string unicode, tabel terjemahan bukanlah string 256 karakter tetapi dikt dengan ord () karakter yang relevan sebagai kunci. Tapi bagaimanapun mendapatkan string ascii yang tepat dari string unicode cukup sederhana, menggunakan metode yang disebutkan oleh truppo di atas, yaitu: unicode_string.encode ("ascii", "ignore")

Sebagai ringkasan, jika karena alasan tertentu Anda benar-benar perlu mendapatkan string ascii (misalnya, saat Anda memunculkan pengecualian standar dengan raise Exception, ascii_message), Anda dapat menggunakan fungsi berikut:

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)

Hal yang baik dengan terjemahan adalah Anda benar-benar dapat mengonversi karakter beraksen menjadi karakter ascii non-aksen yang relevan, bukan hanya menghapusnya atau menggantinya dengan '?'. Ini sering berguna, misalnya untuk tujuan pengindeksan.

Louis LC
sumber
Saya mendapatkan: TypeError: pemetaan karakter harus mengembalikan integer, Tidak ada atau unicode
Ivelin
1
s.replace(u'Â ', '')              # u before string is important

dan buat .pyfile Anda menjadi unicode.

SilentGhost
sumber
1

Ini adalah peretasan kotor, tetapi mungkin berhasil.

s2 = ""
for i in s:
    if ord(i) < 128:
        s2 += i
Corey D
sumber
0

Untuk apa nilainya, set karakter saya adalah utf-8dan saya telah memasukkan # -*- coding: utf-8 -*-baris " " klasik .

Namun, saya menemukan bahwa saya tidak memiliki Universal Newlines ketika membaca data ini dari halaman web.

Teks saya terdiri dari dua kata, dipisahkan oleh " \r\n". Saya hanya membelah \ndan mengganti "\n".

Setelah saya mengulang dan melihat karakter yang dipermasalahkan, saya menyadari kesalahannya.

Jadi, bisa juga dalam kumpulan karakter ASCII , tetapi karakter yang tidak Anda harapkan.

Lembah kecil
sumber