Bagaimana saya bisa menghapus karakter non-ASCII tetapi meninggalkan titik dan spasi menggunakan Python?

100

Saya bekerja dengan file .txt. Saya ingin string teks dari file tanpa karakter non-ASCII. Namun, saya ingin meninggalkan spasi dan titik. Saat ini, saya juga menghapusnya. Berikut kodenya:

def onlyascii(char):
    if ord(char) < 48 or ord(char) > 127: return ''
    else: return char

def get_my_string(file_path):
    f=open(file_path,'r')
    data=f.read()
    f.close()
    filtered_data=filter(onlyascii, data)
    filtered_data = filtered_data.lower()
    return filtered_data

Bagaimana saya harus memodifikasi onlyascii () untuk meninggalkan spasi dan titik? Saya membayangkan itu tidak terlalu rumit tetapi saya tidak bisa memahaminya.

alexwlchan.dll
sumber
Terima kasih (dengan tulus) atas klarifikasi John. Saya memahami bahwa spasi dan titik adalah karakter ASCII. Namun, saya menghapus keduanya secara tidak sengaja saat mencoba menghapus karakter non-ASCII saja. Saya melihat bagaimana pertanyaan saya mungkin tersirat sebaliknya.
@PoliticalEconomist: Masalah Anda masih sangat kurang ditentukan. Lihat jawaban saya.
John Machin

Jawaban:

187

Anda dapat memfilter semua karakter dari string yang tidak dapat dicetak menggunakan string.printable , seperti ini:

>>> s = "some\x00string. with\x15 funny characters"
>>> import string
>>> printable = set(string.printable)
>>> filter(lambda x: x in printable, s)
'somestring. with funny characters'

string.printable di mesin saya berisi:

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c

EDIT: Pada Python 3, filter akan mengembalikan iterable. Cara yang benar untuk mendapatkan string kembali adalah:

''.join(filter(lambda x: x in printable, s))
jterrace
sumber
2
ada apa dengan karakter yang dapat dicetak di bawah ordinal 48?
joaquin
38
Satu-satunya masalah dengan menggunakan filteradalah ia mengembalikan iterable. Jika Anda membutuhkan kembali tali (seperti yang saya lakukan karena saya membutuhkan ini ketika melakukan daftar kompresi) kemudian melakukan hal ini: ''.join(filter(lambda x: x in string.printable, s).
cjbarth
5
@ cjbarth - komentar spesifik untuk python 3, tetapi sangat berguna. Terima kasih!
gempa bawah
7
Mengapa tidak menggunakan ekspresi reguler: re.sub(r'[^\x00-\x7f]',r'', your-non-ascii-string). Lihat utas ini stackoverflow.com/a/20079244/658497
Noam Manos
1
@NoamManos ini 4-5 kali lebih cepat bagi saya daripada bergabung ... filter ... solusi lambda, terima kasih.
artfulrobot
95

Cara mudah untuk mengubah ke codec yang berbeda, adalah dengan menggunakan encode () atau decode (). Dalam kasus Anda, Anda ingin mengonversi ke ASCII dan mengabaikan semua simbol yang tidak didukung. Misalnya, huruf Swedia å bukan merupakan karakter ASCII:

    >>>s = u'Good bye in Swedish is Hej d\xe5'
    >>>s = s.encode('ascii',errors='ignore')
    >>>print s
    Good bye in Swedish is Hej d

Edit:

Python3: str -> byte -> str

>>>"Hej då".encode("ascii", errors="ignore").decode()
'hej d'

Python2: unicode -> str -> unicode

>>> u"hej då".encode("ascii", errors="ignore").decode()
u'hej d'

Python2: str -> unicode -> str (decode dan encode dalam urutan terbalik)

>>> "hej d\xe5".decode("ascii", errors="ignore").encode()
'hej d'
Zweedeend
sumber
16
Saya mendapatkanUnicodeDecodeError: 'ascii' codec can't decode byte 0xc2 in position 27
Xodarap777
2
Saya mendapat kesalahan itu ketika saya meletakkan karakter unicode yang sebenarnya dalam string melalui salin tempel. Saat Anda menentukan string sebagai enkode u'thestring 'berfungsi dengan benar.
Ben Liyanage
2
Bekerja hanya pada Py3, tapi elegan.
rajin
7
Bagi mereka yang mendapatkan kesalahan yang sama seperti @ Xodarap777: Anda harus terlebih dahulu .decode () string, dan hanya setelah itu encode. Misalnyas.decode('utf-8').encode('ascii', errors='ignore')
Spc_555
30

Menurut @artfulrobot, ini seharusnya lebih cepat daripada filter dan lambda:

re.sub(r'[^\x00-\x7f]',r'', your-non-ascii-string) 

Lihat contoh lain di sini http://stackoverflow.com/questions/20078816/replace-non-ascii-characters-with-a-single-space/20079244#20079244

Noam Manos
sumber
1
Solusi ini menjawab pertanyaan yang dinyatakan OP, tetapi berhati-hatilah karena itu tidak akan menghapus karakter yang tidak dapat dicetak yang termasuk dalam ASCII yang menurut saya adalah yang ingin ditanyakan oleh OP.
Danilo Souza Morães
6

Pertanyaan Anda ambigu; dua kalimat pertama yang digabungkan menyiratkan bahwa Anda yakin bahwa spasi dan "titik" adalah karakter non-ASCII. Ini salah Semua karakter seperti ord (char) <= 127 adalah karakter ASCII. Misalnya, fungsi Anda mengecualikan karakter ini! "# $% & \ '() * +, -. / Tetapi menyertakan beberapa karakter lain misalnya [] {}.

Harap mundur, berpikir sedikit, dan edit pertanyaan Anda untuk memberi tahu kami apa yang Anda coba lakukan, tanpa menyebutkan kata ASCII, dan mengapa Anda berpikir bahwa karakter seperti ord (char)> = 128 dapat diabaikan. Juga: versi Python yang mana? Apa pengkodean data masukan Anda?

Harap perhatikan bahwa kode Anda membaca seluruh file input sebagai string tunggal, dan komentar Anda ("solusi hebat") untuk jawaban lain menyiratkan bahwa Anda tidak peduli dengan baris baru dalam data Anda. Jika file Anda berisi dua baris seperti ini:

this is line 1
this is line 2

hasilnya akan seperti itu 'this is line 1this is line 2' ... apakah itu yang kamu inginkan?

Solusi yang lebih besar akan mencakup:

  1. nama yang lebih baik untuk fungsi filter daripada onlyascii
  2. pengakuan bahwa fungsi filter hanya perlu mengembalikan nilai kebenaran jika argumennya akan dipertahankan:

    def filter_func(char):
        return char == '\n' or 32 <= ord(char) <= 126
    # and later:
    filtered_data = filter(filter_func, data).lower()
John Machin
sumber
Jawaban ini sangat membantu bagi kami yang datang untuk menanyakan sesuatu yang mirip dengan OP, dan jawaban yang Anda usulkan sangat membantu pythonic. Namun, saya merasa aneh bahwa tidak ada solusi yang lebih efisien untuk masalah saat Anda menafsirkannya (yang sering saya temui) - karakter demi karakter, ini membutuhkan waktu yang sangat lama dalam file yang sangat besar.
Xodarap777
5

Anda dapat menggunakan kode berikut untuk menghapus huruf non-Inggris:

import re
str = "123456790 ABC#%? .(朱惠英)"
result = re.sub(r'[^\x00-\x7f]',r'', str)
print(result)

Ini akan kembali

123456790 ABC #%? . ()

Noha Elprince
sumber
1

Jika Anda menginginkan karakter ascii yang dapat dicetak, Anda mungkin harus memperbaiki kode Anda menjadi:

if ord(char) < 32 or ord(char) > 126: return ''

ini setara, dengan string.printable(jawaban dari @jterrace), kecuali tidak adanya pengembalian dan tab ('\ t', '\ n', '\ x0b', '\ x0c' dan '\ r') tetapi tidak sesuai dengan kisaran pertanyaan Anda

joaquin
sumber
1
Sedikit lebih sederhana: lambda x: 32 <= ord (x) <= 126
jterrace
itu tidak sama dengan string.printable karena meninggalkan string.whitespace, meskipun itu mungkin yang diinginkan OP, bergantung pada hal-hal seperti \ n dan \ t.
jterrace
@jterrace benar, termasuk spasi (ord 32) tetapi tidak ada pengembalian dan tab
joaquin
ya, hanya mengomentari "ini setara dengan string.printable", tetapi tidak benar
jterrace
Saya mengedit jawabannya, terima kasih! pertanyaan OP menyesatkan jika Anda tidak membacanya dengan cermat.
joaquin
1

Bekerja dengan cara saya melalui Fluent Python (Ramalho) - sangat disarankan. Pemahaman daftar one-ish-liners yang terinspirasi oleh Bab 2:

onlyascii = ''.join([s for s in data if ord(s) < 127])
onlymatch = ''.join([s for s in data if s in
              'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'])
Matthew Dunn
sumber
Ini tidak memungkinkan untuk simbol ASCII standar, seperti poin peluru, simbol derajat, simbol hak cipta, simbol Yen, dll. Selain itu, contoh pertama Anda menyertakan simbol yang tidak dapat dicetak, seperti BELL, yang tidak diinginkan.
SherylHohman