Làm cách nào để trình thông dịch python xử lý chính xác các ký tự không phải ASCII trong các hoạt động chuỗi?


104

Tôi có một chuỗi trông giống như vậy:

6 918 417 712

Cách rõ ràng để cắt chuỗi này (theo tôi hiểu về Python) chỉ đơn giản là nói chuỗi nằm trong một biến được gọi là s, chúng ta nhận được:

s.replace('Â ', '')

Điều đó sẽ làm các trick. Nhưng tất nhiên nó phàn nàn rằng ký tự không phải ASCII '\xc2'trong tệp blabla.py không được mã hóa.

Tôi chưa bao giờ hoàn toàn có thể hiểu được cách chuyển đổi giữa các bảng mã khác nhau.

Đây là mã, nó thực sự giống như ở trên, nhưng bây giờ là trong ngữ cảnh. Tệp được lưu dưới dạng UTF-8 trong notepad và có tiêu đề sau:

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

Mật mã:

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)

Nó không xa hơn s.replace...


1
Đã thử tất cả 4 câu trả lời cho đến nay. Không đi. Vẫn nhận được UnicodeDecodeError: 'ascii' giải mã không thể giải mã byte 0xc2 ở vị trí 1: thứ tự không trong phạm vi (128)
adergaard

chuỗi unicode của bạn phải được thêm vào trướcu
SilentGhost

@SilentGhost: như bạn thấy, không có cách nào để chắc chắn rằng đó là một chuỗi unicode. Tôi nhận được một chuỗi có nội dung được hiển thị ở trên, nhưng nó chứa các chuỗi không phải ascii. Đó là vấn đề thực sự. Tôi đoán nó là unicode vì nó không có trong 128 đầu tiên.
adergaard

Lỗi không liên quan gì đến chuỗi đến. Đó là một chuỗi trong mã của bạn gây ra lỗi này!
SilentGhost

2
Tôi cá rằng đây là lý do tại sao Python 3 rất nghiêm ngặt về sự khác biệt giữa chuỗi và chuỗi byte, chỉ để tránh loại nhầm lẫn này.
Mark Ransom

Câu trả lời:


84

Python 2 sử dụng asciilàm mã hóa mặc định cho các tệp nguồn, có nghĩa là bạn phải chỉ định một mã hóa khác ở đầu tệp để sử dụng các ký tự unicode không phải ascii theo nghĩa đen. Python 3 sử dụng utf-8làm mã hóa mặc định cho các tệp nguồn, do đó, điều này ít xảy ra hơn.

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

Để bật mã hóa nguồn utf-8, điều này sẽ nằm ở một trong hai dòng trên cùng:

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

Ở trên là trong tài liệu, nhưng điều này cũng hoạt động:

# coding: utf-8

Cân nhắc bổ sung:

  • Tệp nguồn cũng phải được lưu bằng cách sử dụng mã hóa chính xác trong trình soạn thảo văn bản của bạn.

  • Trong Python 2, ký tự unicode phải có utrước nó, như trong s.replace(u"Â ", u"")Nhưng trong Python 3, chỉ cần sử dụng dấu ngoặc kép. Trong Python 2, bạn có thể from __future__ import unicode_literalslấy hành vi Python 3, nhưng lưu ý rằng điều này ảnh hưởng đến toàn bộ mô-đun hiện tại.

  • s.replace(u"Â ", u"")cũng sẽ không thành công nếu skhông phải là một chuỗi unicode.

  • string.replace trả về một chuỗi mới và không chỉnh sửa tại chỗ, vì vậy hãy đảm bảo rằng bạn cũng đang sử dụng giá trị trả về


4
Bạn thực sự chỉ cần # coding: utf-8. -*-không phải để trang trí, nhưng bạn không bao giờ cần nó. Tôi nghĩ rằng nó đã ở đó cho những chiếc vỏ cũ.
fmalina,

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

chỉnh sửa: xung đầu tiên của tôi luôn là sử dụng bộ lọc, nhưng biểu thức trình tạo bộ nhớ hiệu quả hơn (và ngắn hơn) ...

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

Hãy nhớ rằng điều này được đảm bảo hoạt động với mã hóa UTF-8 (vì tất cả các byte trong các ký tự nhiều byte có bit cao nhất được đặt thành 1).


1
Tôi nhận được: TypeError: ord () mong đợi một ký tự, nhưng chuỗi có độ dài 2 được tìm thấy
Ivelin

@Ivelin đó là do "ký tự" không được hiểu là unicode thích hợp ... hãy kiểm tra xem chuỗi nguồn của bạn có được đặt tiền tố ukhông nếu nó là một chữ.
fortran

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

4
Tôi thấy số phiếu bạn nhận được nhưng khi tôi thử nó lại nói: Không. UnicodeDecodeError: codec 'ascii' không thể giải mã byte 0xc2 ở vị trí 1: thứ tự không trong phạm vi (128). Có thể là chuỗi gốc của tôi không có trong unicode? Tốt trong mọi trường hợp. nó cần
adergaard

2
Tốt, cảm ơn. Tôi có thể đề xuất sử dụng .decode () trên kết quả để đưa nó vào mã hóa ban đầu không?
AkiRoss

Nếu bạn đang nhận được UnicodeDecodeError: 'ascii', hãy thử chuyển đổi chuỗi thành định dạng '' UTF-8 'trước khi áp dụng chức năng mã hóa.
Sateesh

16

Đoạn mã sau sẽ thay thế tất cả các ký tự không phải ASCII bằng dấu chấm hỏi.

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

Vì tò mò, tôi muốn biết điều đó, Có lý do cụ thể nào để thay thế nó bằng dấu chấm hỏi không?
Mohsin

6

Sử dụng Regex:

import re

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

5

Quá muộn để có câu trả lời, nhưng chuỗi ban đầu ở UTF-8 và '\ xc2 \ xa0' là UTF-8 cho KHÔNG GIAN BẮT ĐẦU. Chỉ cần giải mã chuỗi gốc dưới dạng s.decode('utf-8')(\ xa0 hiển thị dưới dạng khoảng trắng khi được giải mã không chính xác thành Windows-1252 hoặc latin-1:

Ví dụ (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

Đầu ra

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

Điều này sẽ in ra 6 918 417 712


Không. UnicodeDecodeError: codec 'ascii' không thể giải mã byte 0xc2 ở vị trí 1: thứ tự không trong phạm vi (128). Có thể là chuỗi gốc của tôi không có trong unicode? Tốt trong mọi trường hợp. Có lẽ tôi đang làm gì đó sai.
adergaard

@adergaard, bạn đã thêm # - - coding: utf-8 - - ở đầu tệp nguồn chưa?
Nadia Alramli

Vâng, hãy xem lại phần đầu của trang này, tôi đã chỉnh sửa questoin và nhập mã và các bình luận tiêu đề. Cảm ơn vì sự hỗ trợ của bạn.
adergaard

Tôi nghĩ bạn sẽ phải tìm ra cách lấy các chuỗi từ tài liệu html hoặc xml trong unicode. Thông tin thêm về điều đó tại đây: lặnintopython.org/xml_processing/unicode.html
Isaiah

2

Tôi biết đó là một chủ đề cũ, nhưng tôi cảm thấy bắt buộc phải đề cập đến phương thức dịch, đây luôn là một cách tốt để thay thế tất cả các mã ký tự trên 128 (hoặc khác nếu cần).

Cách sử dụng : str. dịch ( bảng [, 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'

Bắt đầu với Python 2.6 , bạn cũng có thể thiết lập bảng để None, và sử dụng deletechars để xóa các ký tự mà bạn không muốn như trong ví dụ thể hiện trong các tài liệu tiêu chuẩn tại http://docs.python.org/library/stdtypes. html .

Với chuỗi unicode, bảng dịch không phải là một chuỗi 256 ký tự mà là một dict với ord () các ký tự có liên quan làm khóa. Nhưng dù sao việc lấy một chuỗi ascii thích hợp từ một chuỗi unicode cũng đủ đơn giản, bằng cách sử dụng phương pháp được đề cập bởi truppo ở trên, cụ thể là: unicode_string.encode ("ascii", "ignore")

Tóm lại, nếu vì lý do nào đó mà bạn thực sự cần lấy chuỗi ascii (ví dụ: khi bạn tăng một ngoại lệ tiêu chuẩn với raise Exception, ascii_message), bạn có thể sử dụng hàm sau:

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)

Điều tốt với dịch là bạn thực sự có thể chuyển đổi các ký tự có dấu thành các ký tự ascii không dấu có liên quan thay vì chỉ xóa chúng hoặc thay thế chúng bằng '?'. Điều này thường hữu ích, chẳng hạn cho mục đích lập chỉ mục.


Tôi nhận được: TypeError: ánh xạ ký tự phải trả về số nguyên, Không có hoặc unicode
Ivelin

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

và làm cho .pytệp của bạn unicode.


1

Đây là một vụ hack bẩn thỉu, nhưng có thể hoạt động.

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

0

Đối với những gì nó đáng giá, bộ nhân vật của tôi là utf-8và tôi đã bao gồm dòng cổ điển " # -*- coding: utf-8 -*-".

Tuy nhiên, tôi phát hiện ra rằng tôi không có Dòng mới phổ biến khi đọc dữ liệu này từ một trang web.

Văn bản của tôi có hai từ, được phân tách bằng " \r\n". Tôi chỉ tách trên \nvà thay thế "\n".

Một khi tôi lướt qua và nhìn thấy nhân vật được đề cập, tôi đã nhận ra sai lầm.

Vì vậy, nó cũng có thể nằm trong bộ ký tự ASCII , nhưng là một ký tự mà bạn không mong đợi.

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.