String işlemlerinde python yorumlayıcısının ASCII olmayan karakterleri doğru şekilde işlemesini nasıl sağlayabilirim?


104

Şuna benzeyen bir dizem var:

6 918 417 712

Bu dizeyi kırpmanın net yolu (Python'u anladığım kadarıyla), dizenin adı verilen bir değişkende olduğunu söylemektir s, şunu elde ederiz:

s.replace('Â ', '')

Bu hile yapmalı. Ancak elbette '\xc2'blabla.py dosyasındaki ASCII olmayan karakterin kodlanmadığından şikayet ediyor .

Farklı kodlamalar arasında nasıl geçiş yapılacağını asla tam olarak anlayamadım.

İşte kod, gerçekten yukarıdakinin aynısı, ama şimdi bağlam içinde. Dosya, not defterinde UTF-8 olarak kaydedilir ve aşağıdaki başlığa sahiptir:

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

Kod:

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)

Daha ötesine geçemez s.replace...


1
Şimdiye kadar 4 cevabın hepsini denedim. Gitme. Hala UnicodeDecodeError alınıyor: 'ascii' codec'i konum 1'de 0xc2 baytının kodunu çözemiyor: ordinal aralıkta değil (128)
adergaard

senin Unicode dize gerekir ile başına eklenecektiru
SilentGhost

@SilentGhost: Gördüğünüz gibi bunun bir unicode dizesi olduğundan emin olmanın bir yolu yok. Yukarıda gösterilen içeriğe sahip, ancak ascii olmayan dizeler içeren bir dize alıyorum. Gerçek sorun bu. İlk 128'de olmadığı için unicode olduğunu tahmin ediyorum.
adergaard

Hatanın gelen dizeyle ilgisi yoktur. Kodunuzda bu hatayı ortaya çıkaran bir dizedir!
SilentGhost

2
Bahse girerim Python 3'ün dizeler ve bayt dizileri arasındaki fark konusunda bu kadar katı olmasının nedeni budur, sırf bu tür bir karışıklığı önlemek için.
Mark Ransom

Yanıtlar:


84

Python 2, asciikaynak dosyalar için varsayılan kodlama olarak kullanır ; bu, değişmez değerlerde ascii olmayan unicode karakterleri kullanmak için dosyanın üstünde başka bir kodlama belirtmeniz gerektiği anlamına gelir. Python 3, utf-8kaynak dosyalar için varsayılan kodlama olarak kullanır , bu nedenle bu daha az sorun teşkil eder.

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

Utf-8 kaynak kodlamasını etkinleştirmek için, bu en üstteki iki satırdan birine girecektir:

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

Yukarıdakiler dokümanlardadır, ancak bu da işe yarar

# coding: utf-8

Ek hususlar:

  • Kaynak dosya, metin düzenleyicinizde de doğru kodlama kullanılarak kaydedilmelidir.

  • Python 2'de, unicode değişmezinin uönünde olması gerekir s.replace(u"Â ", u""), tıpkı Python 3'te olduğu gibi, sadece tırnak kullanın. Python 2'de, from __future__ import unicode_literalsPython 3 davranışını elde edebilirsiniz , ancak bunun tüm mevcut modülü etkilediğini unutmayın.

  • s.replace(u"Â ", u"")sunicode dizesi değilse de başarısız olur .

  • string.replace yeni bir dize döndürür ve yerinde düzenleme yapmaz, bu nedenle dönüş değerini de kullandığınızdan emin olun


4
Aslında sadece ihtiyacınız var # coding: utf-8. -*-dekorasyon için değil, ama buna hiç ihtiyacınız olmayacak. Sanırım eski deniz kabukları için oradaydı.
fmalina

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

düzenleme: ilk dürtülerim her zaman bir filtre kullanmaktır, ancak üretici ifadesi daha verimli (ve daha kısa) ...

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

Bunun UTF-8 kodlamasıyla çalışmasının garantili olduğunu unutmayın (çünkü çok baytlı karakterlerdeki tüm baytlar en yüksek bit 1'e ayarlıdır).


1
Anlıyorum: TypeError: ord () bir karakter bekliyordu, ancak uzunluk 2 dizesi bulundu
Ivelin

@Ivelin, bunun nedeni "karakterin" doğru unicode olarak yorumlanmamasıdır ... kaynak dizgenizin, ubirebir ise , öneki olup olmadığını kontrol edin .
fortran

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

4
Aldığınız oyları görüyorum ama denediğimde diyor ki: Hayır. UnicodeDecodeError: 'ascii' codec bileşeni, 1 konumundaki 0xc2 baytının kodunu çözemez: ordinal, aralık (128) içinde değil. Benim orijinal dizem unicode'da olmayabilir mi? Her durumda. it need
adergaard

2
Güzel, teşekkürler. Orijinal kodlamada elde etmek için sonuçta .decode () kullanmayı önerebilir miyim?
AkiRoss

UnicodeDecodeError: 'ascii' alıyorsanız, kodlama işlevini uygulamadan önce dizeyi '' UTF-8 'biçimine dönüştürmeyi deneyin.
Sateesh

16

Aşağıdaki kod, ASCII olmayan tüm karakterleri soru işaretleriyle değiştirecektir.

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

Merak ettiğim için şunu bilmek istedim: Soru işaretiyle değiştirmek için belirli bir neden var mı?
Mohsin

6

Regex kullanarak:

import re

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

5

Bir yanıt için çok geç, ancak orijinal dize UTF-8'deydi ve '\ xc2 \ xa0', NO-BREAK SPACE için UTF-8'dir. Orijinal dizeyi şu şekilde s.decode('utf-8')çözün: (\ xa0, Windows-1252 veya latin-1 olarak yanlış bir şekilde kodu çözüldüğünde boşluk olarak görüntülenir:

Örnek (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

Çıktı

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

Bu çıktı alacaktır 6 918 417 712


Hayır! UnicodeDecodeError: 'ascii' codec bileşeni, 1 konumundaki 0xc2 baytının kodunu çözemez: ordinal, aralık (128) içinde değil. Benim orijinal dizem unicode'da olmayabilir mi? Her durumda. Muhtemelen yanlış bir şey yapıyorum.
adergaard

@adergaard, kaynak dosyanın en üstüne # - - kodlama: utf-8 - - eklediniz mi?
Nadia Alramli

Evet, bu sayfanın en üstüne tekrar bakın, questoini düzenledim ve kodu ve başlık yorumlarını ekledim. Yardımınız için teşekkürler.
adergaard

Sanırım dizeleri html veya xml belgesinden unicode'da nasıl elde edeceğinizi bulmanız gerekecek. Bununla
Isaiah

2

Bunun eski bir iş parçacığı olduğunu biliyorum, ancak 128'in üzerindeki tüm karakter kodlarını (veya gerekirse diğerlerini) değiştirmek için her zaman iyi bir yol olan çeviri yönteminden bahsetmeye mecbur hissettim.

Kullanım : str. translate ( tablo [, siler] )

>>> 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'dan başlayarak , tabloyu Yok olarak da ayarlayabilir ve http://docs.python.org/library/stdtypes adresindeki standart belgelerde gösterilen örneklerde olduğu gibi istemediğiniz karakterleri silmek için silme araçlarını kullanabilirsiniz . html .

Unicode dizeleriyle, çeviri tablosu 256 karakterlik bir dize değil, anahtar olarak ilgili karakterlerin ord () olduğu bir diktedir. Ancak yine de bir unicode dizgisinden uygun bir ascii dizesi elde etmek yeterince basittir, yukarıda truppo tarafından belirtilen yöntemi kullanarak, yani: unicode_string.encode ("ascii", "görmezden")

Özet olarak, herhangi bir nedenle kesinlikle bir ascii dizesi almanız gerekiyorsa (örneğin, standart bir istisna oluşturduğunuzda raise Exception, ascii_message), aşağıdaki işlevi kullanabilirsiniz:

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)

Translate ile iyi olan şey , sadece onları silmek veya '?' İle değiştirmek yerine, aksanlı karakterleri ilgili aksansız ascii karakterlerine dönüştürebilmenizdir . Bu genellikle, örneğin indeksleme amacıyla kullanışlıdır.


Anladığım: TypeError: karakter eşleme tamsayı, Yok veya unicode döndürmeli
Ivelin

1
s.replace(u'Â ', '')              # u before string is important

ve .pydosyanızı unicode yapın.


1

Bu kirli bir hack, ancak işe yarayabilir.

s2 = ""
for i in s:
    if ord(i) < 128:
        s2 += i

0

Değeri ne utf-8olursa olsun, benim karakter setim ve klasik " # -*- coding: utf-8 -*-" satırını dahil ettim .

Ancak, bu verileri bir web sayfasından okurken Evrensel Yeni Satırlara sahip olmadığımı keşfettim.

Metnimde " \r\n" ile ayrılmış iki kelime vardı . Ben sadece ikiye ayrılıyordum \nve "\n".

Dönüp dolaşıp söz konusu karakter setini görünce, hatayı fark ettim.

Yani, ASCII karakter setinde de olabilir , ancak beklemediğiniz bir karakter.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.