Làm cách nào để xóa các ký tự không phải ASCII nhưng để lại dấu chấm và khoảng trắng bằng Python?


100

Tôi đang làm việc với tệp .txt. Tôi muốn một chuỗi văn bản từ tệp không có ký tự không phải ASCII. Tuy nhiên, tôi muốn để lại khoảng trắng và dấu chấm. Hiện tại, tôi cũng đang tước bỏ chúng. Đây là mã:

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

Tôi nên sửa đổi onlyascii () như thế nào để để lại khoảng trắng và dấu chấm? Tôi tưởng tượng nó không quá phức tạp nhưng tôi không thể hình dung ra được.


Cảm ơn (chân thành) vì John đã làm rõ. Tôi hiểu rằng dấu cách và dấu chấm là các ký tự ASCII. Tuy nhiên, tôi đã vô tình xóa cả hai trong số chúng trong khi cố gắng chỉ xóa các ký tự không phải ASCII. Tôi thấy câu hỏi của tôi có thể ngụ ý khác như thế nào.

@PoliticalEconomist: Vấn đề của bạn vẫn chưa được chỉ rõ. Hãy xem câu trả lời của tôi.
John Machin,

Câu trả lời:


187

Bạn có thể lọc tất cả các ký tự từ chuỗi không in được bằng string.printable , như sau:

>>> 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 trên máy của tôi chứa:

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

CHỈNH SỬA: Trên Python 3, bộ lọc sẽ trả về một tệp có thể lặp lại. Cách chính xác để lấy lại một chuỗi sẽ là:

''.join(filter(lambda x: x in printable, s))

2
Chuyện gì xảy ra với những ký tự có thể in được nằm dưới thứ tự 48?
joaquin,

38
Vấn đề duy nhất khi sử dụng filterlà nó trả về một giá trị có thể lặp lại. Nếu bạn cần một chuỗi trở lại (như tôi đã làm bởi vì tôi cần này khi thực hiện nén danh sách) thì làm như sau: ''.join(filter(lambda x: x in string.printable, s).
cjbarth

5
@cjbarth - nhận xét là python 3 cụ thể, nhưng rất hữu ích. Cảm ơn!
undershock

7
Tại sao không sử dụng biểu thức chính quy: re.sub(r'[^\x00-\x7f]',r'', your-non-ascii-string). Xem chủ đề này stackoverflow.com/a/20079244/658497
Noam Manos,

1
@NoamManos điều này nhanh hơn 4-5 lần đối với tôi đó là giải pháp tham gia ... bộ lọc ... lambda, cảm ơn.
artfulrobot

95

Một cách dễ dàng để thay đổi sang codec khác là sử dụng encode () hoặc decode (). Trong trường hợp của bạn, bạn muốn chuyển đổi sang ASCII và bỏ qua tất cả các ký hiệu không được hỗ trợ. Ví dụ: chữ cái Thụy Điển å không phải là một ký tự 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

Biên tập:

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 (giải mã và mã hóa theo thứ tự ngược lại)

>>> "hej d\xe5".decode("ascii", errors="ignore").encode()
'hej d'

16
Tôi nhận đượcUnicodeDecodeError: 'ascii' codec can't decode byte 0xc2 in position 27
Xodarap777

2
Tôi gặp lỗi đó khi đặt ký tự unicode thực tế trong chuỗi thông qua dán sao chép. Khi bạn chỉ định một chuỗi là mã hóa u'thestring 'hoạt động chính xác.
Ben Liyanage

2
Chỉ hoạt động trên Py3, nhưng nó rất thanh lịch.
gaborous

7
Đối với những người đang gặp lỗi tương tự như @ Xodarap777: trước tiên bạn nên .decode () chuỗi và chỉ sau mã hóa đó. Ví dụs.decode('utf-8').encode('ascii', errors='ignore')
Spc_555

30

Theo @artfulrobot, điều này sẽ nhanh hơn bộ lọc và lambda:

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

Xem thêm các ví dụ khác tại đây http://stackoverflow.com/questions/20078816/replace-non-ascii-characters-with-a-single-space/20079244#20079244


1
Giải pháp này trả lời câu hỏi đã nêu của OP, nhưng hãy cẩn thận rằng nó sẽ không loại bỏ các ký tự không thể in được có trong ASCII mà tôi nghĩ đó là những gì OP định hỏi.
Danilo Souza Morães

6

Câu hỏi của bạn là mơ hồ; hai câu đầu tiên được ghép với nhau ngụ ý rằng bạn tin rằng dấu cách và "dấu chấm" là các ký tự không phải ASCII. Điều này là không đúng. Tất cả các ký tự như ord (char) <= 127 đều là ký tự ASCII. Ví dụ: hàm của bạn loại trừ các ký tự này! "# $% & \ '() * +, -. / Nhưng bao gồm một số ký tự khác, ví dụ như [] {}.

Vui lòng lùi lại, suy nghĩ một chút và chỉnh sửa câu hỏi của bạn để cho chúng tôi biết bạn đang cố gắng làm gì mà không đề cập đến từ ASCII và tại sao bạn cho rằng các ký tự như ord (char)> = 128 là không thể bỏ qua. Ngoài ra: phiên bản Python nào? Mã hóa dữ liệu đầu vào của bạn là gì?

Xin lưu ý rằng mã của bạn đọc toàn bộ tệp đầu vào dưới dạng một chuỗi duy nhất và nhận xét của bạn ("giải pháp tuyệt vời") cho một câu trả lời khác ngụ ý rằng bạn không quan tâm đến các dòng mới trong dữ liệu của mình. Nếu tệp của bạn có hai dòng như thế này:

this is line 1
this is line 2

kết quả sẽ là 'this is line 1this is line 2'... đó có phải là điều bạn thực sự muốn?

Một giải pháp tốt hơn sẽ bao gồm:

  1. một tên tốt hơn cho chức năng bộ lọc hơn onlyascii
  2. thừa nhận rằng một hàm bộ lọc chỉ cần trả về một giá trị trung thực nếu đối số được giữ lại:

    def filter_func(char):
        return char == '\n' or 32 <= ord(char) <= 126
    # and later:
    filtered_data = filter(filter_func, data).lower()
    

Câu trả lời này rất hữu ích cho những người trong chúng tôi đến để hỏi điều gì đó tương tự như OP, và câu trả lời được đề xuất của bạn rất hữu ích. Tuy nhiên, tôi thấy lạ là không có giải pháp nào hiệu quả hơn cho vấn đề như bạn đã diễn giải (mà tôi thường gặp phải) - từng ký tự, điều này mất nhiều thời gian trong một tệp rất lớn.
Xodarap777

5

Bạn có thể sử dụng mã sau để xóa các chữ cái không phải tiếng Anh:

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

Điều này sẽ trở lại

123456790 ABC #%? . ()


1

Nếu bạn muốn các ký tự ascii có thể in được, bạn có thể nên sửa mã của mình thành:

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

điều này tương đương với string.printable(câu trả lời từ @jterrace), ngoại trừ việc không có trả về và các tab ('\ t', '\ n', '\ x0b', '\ x0c' và '\ r') nhưng không tương ứng với phạm vi câu hỏi của bạn


1
Hơi đơn giản: lambda x: 32 <= ord (x) <= 126
jterrace

điều đó không giống với string.printable vì nó để lại không gian string.whitespace, mặc dù đó có thể là những gì OP muốn, phụ thuộc vào những thứ như \ n và \ t.
jterrace

@jterrace phải, bao gồm không gian (ord 32) nhưng không có lợi nhuận và các tab
Joaquin

yeah, chỉ bình luận về "này tương đương với string.printable", nhưng không đúng sự thật
jterrace

Tôi đã chỉnh sửa câu trả lời, cảm ơn! câu hỏi OP sẽ gây hiểu lầm nếu bạn không đọc kỹ.
joaquin,

1

Làm việc theo cách của tôi thông qua Fluent Python (Ramalho) - rất được khuyến khích. Liệt kê dễ hiểu một-ish-liner lấy cảm hứng từ Chương 2:

onlyascii = ''.join([s for s in data if ord(s) < 127])
onlymatch = ''.join([s for s in data if s in
              'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'])

Điều này sẽ không cho phép các ký hiệu ASCII tiêu chuẩn, chẳng hạn như dấu đầu dòng, ký hiệu độ, ký hiệu bản quyền, ký hiệu Yên, v.v. Ngoài ra, ví dụ đầu tiên của bạn bao gồm các ký hiệu không thể in được, chẳng hạn như BELL, điều này là không mong muốn.
SherylHohman
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.