จะตรวจสอบว่าสตริงใน Python อยู่ใน ASCII ได้อย่างไร?


211

ฉันต้องการตรวจสอบว่าสตริงอยู่ใน ASCII หรือไม่

ฉันรู้ord()แต่เมื่อฉันพยายามที่ฉันมีord('é') TypeError: ord() expected a character, but string of length 2 foundฉันเข้าใจว่ามันเกิดจากวิธีที่ฉันสร้าง Python (ดังอธิบายในord()เอกสารประกอบของ )

มีวิธีอื่นในการตรวจสอบหรือไม่


การเข้ารหัสสตริงแตกต่างกันเล็กน้อยระหว่าง Python 2 และ Python 3 ดังนั้นจึงเป็นการดีที่จะทราบว่าคุณกำลังกำหนดเป้าหมายเวอร์ชันใด
florisla

คำตอบ:


188
def is_ascii(s):
    return all(ord(c) < 128 for c in s)

95
ไม่มีประสิทธิภาพอย่างไม่มีจุดหมาย ดีกว่าลอง s.decode ('ascii') และจับ UnicodeDecodeError ตามที่ Vincent Marchetti แนะนำ
ddaa

20
มันไม่มีประสิทธิภาพ all () จะลัดวงจรและคืนค่า False ทันทีที่พบไบต์ที่ไม่ถูกต้อง
John Millikin

10
ไม่มีประสิทธิภาพหรือไม่วิธีไพ ธ อนมากขึ้นคือการลอง / ยกเว้น
Jeremy Cantrell

43
มันไม่มีประสิทธิภาพเทียบกับการลอง / ยกเว้น นี่คือลูปที่อยู่ในล่าม ด้วยรูปแบบลอง / ยกเว้นการวนซ้ำอยู่ในการใช้งานตัวแปลงสัญญาณ C ที่เรียกโดย str.decode ('ascii') และฉันก็เห็นด้วยรูปแบบลอง / ยกเว้นก็เป็นไพ ธ อนมากขึ้นเช่นกัน
ddaa

25
@JohnMachin ord(c) < 128อ่านได้ง่ายกว่าและใช้งานง่ายกว่าc <= "\x7F"
Slater Victoroff

253

ฉันคิดว่าคุณไม่ได้ถามคำถามที่ถูกต้อง -

สตริงในไพ ธ อนไม่มีคุณสมบัติที่สอดคล้องกับ 'ascii', utf-8 หรือการเข้ารหัสอื่น ๆ แหล่งที่มาของสตริงของคุณ (ไม่ว่าคุณจะอ่านจากไฟล์อินพุตจากแป้นพิมพ์ ฯลฯ ) อาจมีการเข้ารหัสสตริง Unicode ใน ASCII เพื่อสร้างสตริงของคุณ แต่นั่นคือสิ่งที่คุณต้องไปหาคำตอบ

บางทีคำถามที่คุณสามารถถามคือ: "สตริงนี้เป็นผลของการเข้ารหัสสตริง Unicode ใน ASCII หรือไม่" - สิ่งนี้คุณสามารถตอบได้โดยลอง:

try:
    mystring.decode('ascii')
except UnicodeDecodeError:
    print "it was not a ascii-encoded unicode string"
else:
    print "It may have been an ascii-encoded unicode string"

28
ใช้การเข้ารหัสดีกว่าเนื่องจากไม่มีวิธีถอดรหัสสตริงใน python 3 ดูความแตกต่างระหว่างการเข้ารหัส / ถอดรหัสคืออะไร (python 2.x)
Jet Guo

@Sri: นั่นเป็นเพราะคุณใช้มันในสตริงที่ไม่ได้เข้ารหัส ( strใน Python 2 bytesใน Python 3)
dotancohen

ใน Python 2 โซลูชันนี้ใช้งานได้กับสตริงUnicodeเท่านั้น A strในการเข้ารหัส ISO ใด ๆ จะต้องมีการเข้ารหัสเป็น Unicode ก่อน คำตอบควรเป็นสิ่งนี้
alexis

@JetGuo: คุณควรใช้ทั้งคู่โดยขึ้นอยู่กับประเภทอินพุต: s.decode('ascii') if isinstance(s, bytes) else s.encode('ascii')ใน Python 3 อินพุตของ OP เป็นการทดสอบ'é'(ไวยากรณ์ Python 2, Python 3 ไม่ได้ถูกปล่อยออกมาในเวลานั้น) และ.decode()ถูกต้อง
jfs

2
@alexis: ผิด strบน Python 2 เป็นการทดสอบ มันถูกต้องที่จะใช้.decode('ascii')เพื่อค้นหาว่าทุกไบต์อยู่ในช่วง ascii หรือไม่
jfs

153

Python 3 วิธี:

isascii = lambda s: len(s) == len(s.encode())

ในการตรวจสอบให้ส่งสตริงทดสอบ:

str1 = "♥O◘♦♥O◘♦"
str2 = "Python"

print(isascii(str1)) -> will return False
print(isascii(str2)) -> will return True

7
นี่เป็นเคล็ดลับเล็กน้อยในการตรวจจับอักขระที่ไม่ใช่ ASCII ในสตริง Unicode ซึ่งใน python3 นั้นเป็นสตริงทั้งหมด เนื่องจากอักขระ ASCII สามารถเข้ารหัสได้โดยใช้เพียง 1 ไบต์ดังนั้นความยาวอักขระ ASCII จะเป็นจริงตามขนาดหลังจากเข้ารหัสเป็นไบต์ ในขณะที่ตัวละครอื่น ๆ ที่ไม่ใช่ ASCII จะถูกเข้ารหัสเป็น 2 ไบต์หรือ 3 ไบต์ตามลำดับซึ่งจะเพิ่มขนาดของพวกเขา
Devy

โดย @far คำตอบที่ดีที่สุด แต่ไม่ใช่ว่าตัวอักษรบางตัวเช่น ... และ - อาจมีลักษณะเป็น ascii ดังนั้นในกรณีที่คุณต้องการใช้ข้อความนี้เพื่อตรวจสอบข้อความภาษาอังกฤษคุณสามารถแทนที่ตัวอักษรดังกล่าวก่อนการตรวจสอบ
Christophe Roussy

1
แต่ใน Python2 มันจะส่ง UnicodeEncodeError ต้องหาทางออกสำหรับทั้ง Py2 และ Py3
alvas

2
สำหรับผู้ที่ไม่คุ้นเคยกับการใช้แลมบ์ดา (เหมือนตอนที่ฉันเคยเจอคำตอบนี้ครั้งแรก) isasciiตอนนี้เป็นฟังก์ชั่นที่คุณส่งผ่านสตริง: isascii('somestring')== Trueและisascii('àéç')==False
rabidang3ls

8
นี่เป็นเรื่องสิ้นเปลืองธรรมดา มันเข้ารหัสสตริงใน UTF-8 สร้างการทดสอบอื่น ๆ ทั้งหมด True Python 3 คือtry: s.encode('ascii'); return True except UnicodeEncodeError: return False(เหมือนด้านบน แต่เป็นการเข้ารหัสเนื่องจากสตริงเป็น Unicode ใน Python 3) คำตอบนี้ยังเพิ่มข้อผิดพลาดในหลาม 3 เมื่อคุณมีอุ้มท้อง (เช่นisascii('\uD800')ทำให้เกิดข้อผิดพลาดแทนที่จะกลับFalse)
Artyer

73

ใหม่ใน Python 3.7 ( bpo32677 )

ไม่มีการตรวจสอบ ascii ที่น่าเบื่อ / ไม่มีประสิทธิภาพอีกต่อไปสตริงในตัวstr/ bytes/ bytearrayวิธีการใหม่ - .isascii()จะตรวจสอบว่าสตริงนั้นเป็น ascii หรือไม่

print("is this ascii?".isascii())
# True

อันนี้สมควรที่จะอยู่ด้านบน!
Salek

"\x03".isascii()เป็นจริงเช่นกัน เอกสารประกอบบอกว่านี่เป็นเพียงการตรวจสอบว่าตัวละครทุกตัวต่ำกว่าจุดรหัส 128 (0-127) text.isascii() and text.isprintable()หากคุณยังต้องการที่จะหลีกเลี่ยงการควบคุมตัวอักษรที่คุณจะต้อง: การใช้งานเพียงอย่างเดียวisprintableนั้นยังไม่เพียงพอเนื่องจากจะพิจารณาว่าอักขระเช่น¿ที่จะพิมพ์ (ถูกต้อง) แต่ไม่อยู่ในส่วนที่พิมพ์ได้ของ ASCII ดังนั้นคุณต้องตรวจสอบทั้งสองอย่างหากคุณต้องการทั้งสองอย่าง gotcha อีกอัน: ช่องว่างนั้นถือว่าสามารถพิมพ์ได้แท็บและบรรทัดใหม่ไม่ใช่
Luc

19

วิ่งเข้าไปหาอะไรแบบนี้เร็ว ๆ นี้ - สำหรับการอ้างอิงในอนาคต

import chardet

encoding = chardet.detect(string)
if encoding['encoding'] == 'ascii':
    print 'string is in ascii'

ซึ่งคุณสามารถใช้กับ:

string_ascii = string.decode(encoding['encoding']).encode('ascii')

7
แน่นอนว่าต้องใช้ไลบรารีchardet
StackExchange saddens dancek

1
ใช่แม้ว่า chardet จะพร้อมใช้งานตามค่าเริ่มต้นในการติดตั้งส่วนใหญ่
Alvin

7
chardetเพียงเดาการเข้ารหัสด้วยความน่าจะเป็นเช่นนี้: {'confidence': 0.99, 'encoding': 'EUC-JP'}(ซึ่งในกรณีนี้ผิดอย่างสมบูรณ์)
Suzana

19

Vincent Marchetti มีความคิดที่ถูกต้อง แต่str.decodeเลิกใช้แล้วใน Python 3 ใน Python 3 คุณสามารถทำการทดสอบเดียวกันกับstr.encode:

try:
    mystring.encode('ascii')
except UnicodeEncodeError:
    pass  # string is not ascii
else:
    pass  # string is ascii

หมายเหตุ: ข้อยกเว้นที่คุณต้องการที่จะจับยังมีการเปลี่ยนแปลงจากการUnicodeDecodeErrorUnicodeEncodeError


อินพุตของ OP เป็นการทดสอบ ( bytesพิมพ์ใน Python 3 ที่ไม่มี.encode()เมธอด) .decode()ใน @Vincent คำตอบมาร์เค็ตเป็นที่ถูกต้อง
jfs

@JFSebastian OP ขอให้ "วิธีการตรวจสอบว่าสตริงใน Python อยู่ใน ASCII?" และไม่ได้ระบุไบต์กับสตริง Unicode ทำไมคุณถึงบอกว่าข้อมูลของเขา / เธอเป็นงานทดสอบ?
drs

1
ดูวันที่ของคำถาม: 'é'เป็นการทดสอบในเวลานั้น
jfs

1
@JFSebastian โอเคการพิจารณาคำตอบนี้ตอบคำถามนี้ราวกับว่ามันถูกถามในวันนี้ฉันคิดว่ามันยังใช้ได้และมีประโยชน์ ผู้คนจำนวนน้อยลงที่มาที่นี่จะมองหาคำตอบราวกับว่าพวกเขาใช้ Python ในปี 2008
drs

2
ฉันพบคำถามนี้เมื่อฉันกำลังค้นหาวิธีแก้ปัญหาสำหรับ python3 และการอ่านคำถามอย่างรวดเร็วไม่ได้ทำให้ฉันสงสัยว่านี่เป็น python 2 specfic แต่คำตอบนี้มีประโยชน์จริง ๆ - การขึ้นลง!
josch

17

คำถามของคุณไม่ถูกต้อง ข้อผิดพลาดที่คุณเห็นไม่ใช่ผลมาจากวิธีที่คุณสร้างไพ ธ อน แต่เกิดความสับสนระหว่างสตริงไบต์และสตริงยูนิโค้ด

สตริงไบต์ (เช่น "foo" หรือ 'bar' ในรูปแบบ python) เป็นลำดับของ octets หมายเลข 0-255 สตริง Unicode (เช่น u "foo" หรือ u'bar ') เป็นลำดับของจุดโค้ดยูนิโค้ด หมายเลข 0-1112064 แต่ดูเหมือนว่าคุณจะสนใจอักขระéซึ่ง (ในเทอร์มินัลของคุณ) เป็นลำดับแบบหลายไบต์ที่แทนอักขระเดี่ยว

แทนที่จะord(u'é')ลองทำสิ่งนี้:

>>> [ord(x) for x in u'é']

ที่บอกให้คุณทราบถึงลำดับของจุดรหัส "é" อาจให้ [233] หรืออาจมอบ [101, 770]

แทนที่จะchr()ทำสิ่งนี้กลับมีunichr():

>>> unichr(233)
u'\xe9'

จริง ๆ แล้วตัวละครนี้อาจเป็นตัวแทน "จุดรหัส" ยูนิโค้ดเดียวหรือหลายซึ่งตัวเองเป็นตัวแทนของกราฟหรืออักขระ มันอาจเป็น "e ที่มีจุดเน้นเสียงแหลม (เช่นรหัสจุด 233)" หรือ "e" (จุดรหัส 101) ตามด้วย "เน้นเสียงแหลมบนตัวอักษรก่อนหน้า" (รหัสจุด 770) ดังนั้นลักษณะเดียวกันนี้ที่แน่นอนอาจจะนำเสนอเป็นโครงสร้างข้อมูลหลามหรือu'e\u0301'u'\u00e9'

เวลาส่วนใหญ่ที่คุณไม่ควรกังวลเกี่ยวกับเรื่องนี้ แต่มันอาจกลายเป็นปัญหาถ้าคุณวนซ้ำสตริง unicode เนื่องจากการวนซ้ำทำงานโดยใช้จุดรหัสไม่ใช่ตัวแยกอักขระ ในคำอื่น ๆและlen(u'e\u0301') == 2 ถ้าเรื่องนี้กับคุณคุณสามารถแปลงระหว่างรูปแบบสงบและย่อยสลายโดยใช้len(u'\u00e9') == 1unicodedata.normalize

อภิธานศัพท์ Unicodeอาจเป็นแนวทางที่เป็นประโยชน์ในการทำความเข้าใจปัญหาเหล่านี้โดยชี้ให้เห็นว่าแต่ละคำที่เจาะจงหมายถึงส่วนต่าง ๆ ของการแทนข้อความซึ่งมีความซับซ้อนกว่าโปรแกรมเมอร์จำนวนมาก


3
'é' ไม่จำเป็นต้องแทนจุดรหัสเดียว อาจเป็นโค้ดสองจุด (U + 0065 + U + 0301)
jfs

2
ตัวละครแต่ละตัวที่เป็นนามธรรมเป็นเสมอแทนด้วยจุดรหัสเดียว อย่างไรก็ตามรหัสจุดอาจถูกเข้ารหัสหลายไบต์ขึ้นอยู่กับรูปแบบการเข้ารหัส นั่นคือ 'é' คือสองไบต์ใน UTF-8 และ UTF-16 และสี่ไบต์ใน UTF-32 แต่ในแต่ละกรณียังคงเป็นรหัสจุดเดียว - U + 00E9
30929 Ben Ben ว่างเปล่า

5
ที่ว่างเปล่า @ Ben: U + 0065 และ U + 0301 เป็นจุดรหัสและพวกเขาทำแทน 'e' ซึ่งสามารถยังจะแสดงโดย U + 00E9 Google "การรวมสำเนียงเฉียบพลัน"
jfs

JF นั้นถูกต้องเกี่ยวกับการรวม U + 0065 และ U + 0301 ให้เป็น 'é' แต่นี่ไม่ใช่ฟังก์ชั่นที่สามารถย้อนกลับได้ คุณจะได้รับ U + 00E9 ตามวิกิพีเดียคะแนนรหัสคอมโพสิตเหล่านี้มีประโยชน์สำหรับความเข้ากันได้แบบย้อนหลัง
Martin Konecny

1
@teehoo - มันเป็นฟังก์ชั่นที่สามารถย้อนกลับได้ในแง่ที่ว่าคุณอาจทำให้ปกติจุดรหัสที่เป็นตัวแทนของตัวละครที่ประกอบขึ้นเป็นลำดับของจุดรหัสที่เป็นตัวแทนของตัวอักษรที่ประกอบกันเหมือนเดิม ใน Python คุณสามารถทำสิ่งนี้ได้เช่น: unicodedata.normalize ('NFD', u '\ xe9')
Glyph

10

วิธีการเกี่ยวกับการทำเช่นนี้?

import string

def isAscii(s):
    for c in s:
        if c not in string.ascii_letters:
            return False
    return True

5
สิ่งนี้จะล้มเหลวหากสตริงของคุณมีอักขระ ASCII ซึ่งไม่ใช่ตัวอักษร สำหรับตัวอย่างโค้ดของคุณที่มีการขึ้นบรรทัดใหม่, ช่องว่าง, จุด, เครื่องหมายจุลภาค, ขีดล่างและวงเล็บ
florisla

9

ฉันพบคำถามนี้ขณะที่พยายามพิจารณาวิธีการใช้ / เข้ารหัส / ถอดรหัสสตริงที่มีการเข้ารหัสฉันไม่แน่ใจ (และวิธีการหลบหนี / แปลงอักขระพิเศษในสตริงนั้น)

ขั้นตอนแรกของฉันควรตรวจสอบชนิดของสตริง - ฉันไม่ทราบว่ามีข้อมูลที่ดีเกี่ยวกับการจัดรูปแบบจากประเภทใด คำตอบนี้มีประโยชน์มากและทำให้รากที่แท้จริงของปัญหาของฉัน

หากคุณได้รับความหยาบคายและต่อเนื่อง

UnicodeDecodeError: ตัวแปลงสัญญาณ 'ascii' ไม่สามารถถอดรหัสไบต์ 0xc3 ในตำแหน่ง 263: ลำดับไม่อยู่ในช่วง (128)

โดยเฉพาะอย่างยิ่งเมื่อคุณกำลังเข้ารหัสตรวจสอบให้แน่ใจว่าคุณไม่ได้พยายามที่จะ unicode () สตริงที่มีอยู่แล้ว unicode- ด้วยเหตุผลบางอย่างที่น่ากลัวคุณจะได้รับข้อผิดพลาด ascii codec (ดูเพิ่มเติมที่สูตร Python Kitchenและเอกสารประกอบของ Python docsเพื่อความเข้าใจที่ดีขึ้นว่ามันจะแย่ขนาดไหน)

ในที่สุดฉันก็ตัดสินใจว่าสิ่งที่ฉันต้องการจะทำคือ:

escaped_string = unicode(original_string.encode('ascii','xmlcharrefreplace'))

ยังมีประโยชน์ในการแก้ไขข้อบกพร่องคือการตั้งค่าการเข้ารหัสเริ่มต้นในไฟล์ของฉันเป็น utf-8 (ใส่ที่จุดเริ่มต้นของไฟล์หลามของคุณ):

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

ที่ช่วยให้คุณทดสอบอักขระพิเศษ ('àéç') โดยไม่ต้องใช้การยกเว้น Unicode (u '\ xe0 \ xe9 \ xe7')

>>> specials='àéç'
>>> specials.decode('latin-1').encode('ascii','xmlcharrefreplace')
'&#224;&#233;&#231;'

4

เพื่อปรับปรุงวิธีแก้ปัญหาของ Alexander จาก Python 2.6 (และใน Python 3.x) คุณสามารถใช้โมดูลตัวช่วย curses.ascii และใช้ curses.ascii.isascii () ฟังก์ชั่นหรืออื่น ๆ : https://docs.python.org/2.6/ ห้องสมุด / curses.ascii.html

from curses import ascii

def isascii(s):
    return all(ascii.isascii(c) for c in s)



2

การต่อย ( str-type) ใน Python เป็นชุดของไบต์ นอกจากนี้ไม่มีทางบอกเพียงจากการดูที่สายไม่ว่าจะเป็นชุดของไบต์นี้แทนสตริง ASCII สตริงใน charset 8 บิตเช่น ISO-8859-1 หรือสตริงเข้ารหัสด้วย UTF-8 หรือ UTF-16 หรืออะไรก็ตาม .

อย่างไรก็ตามหากคุณรู้ว่ามีการเข้ารหัสที่ใช้คุณสามารถdecodestr เข้าไปในสตริง Unicode แล้วใช้นิพจน์ปกติ (หรือลูป) เพื่อตรวจสอบว่ามีอักขระอยู่นอกช่วงที่คุณกังวลหรือไม่


1

ชอบ @ RogerDahl ของคำตอบแต่ก็มีประสิทธิภาพมากขึ้นในการลัดวงจรโดยกวนตัวอักษรชั้นเรียนและการใช้ค้นหาแทนหรือfind_allmatch

>>> import re
>>> re.search('[^\x00-\x7F]', 'Did you catch that \x00?') is not None
False
>>> re.search('[^\x00-\x7F]', 'Did you catch that \xFF?') is not None
True

ฉันจินตนาการว่านิพจน์ทั่วไปนั้นได้รับการปรับให้เหมาะสมสำหรับเรื่องนี้


0
import re

def is_ascii(s):
    return bool(re.match(r'[\x00-\x7F]+$', s))

จะรวมถึงสตริงที่ว่างเปล่าเป็น ASCII เปลี่ยนไป+*


-1

เพื่อป้องกันรหัสของคุณล้มเหลวคุณอาจต้องการใช้ a try-exceptในการจับTypeErrors

>>> ord("¶")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: ord() expected a character, but string of length 2 found

ตัวอย่างเช่น

def is_ascii(s):
    try:
        return all(ord(c) < 128 for c in s)
    except TypeError:
        return False

นี้tryเสื้อคลุมไม่มีจุดหมายสมบูรณ์ ถ้า"¶"เป็น Unicode string ก็ord("¶")จะใช้งานได้และถ้าไม่ใช่ (Python 2) for c in sจะทำการแยกย่อยเป็นไบต์ดังนั้นordจะทำงานต่อไป
Ry-

-5

ฉันใช้ต่อไปนี้เพื่อตรวจสอบว่าสตริงเป็น ascii หรือ unicode:

>> print 'test string'.__class__.__name__
str
>>> print u'test string'.__class__.__name__
unicode
>>> 

จากนั้นใช้บล็อกแบบมีเงื่อนไขเพื่อกำหนดฟังก์ชั่น:

def is_ascii(input):
    if input.__class__.__name__ == "str":
        return True
    return False

4
-1 AARRGGHH นี่คือการรักษาตัวละครทั้งหมดที่มี ord (c) อยู่ในช่วง (128, 256) เป็น ASCII !!!
John Machin

ใช้งานไม่ได้ ลองโทรต่อไปนี้: is_ascii(u'i am ascii'). แม้ว่าตัวอักษรและช่องว่างที่แน่นอน ASCII นี้ผลตอบแทนยังคงเพราะเราบังคับสตริงที่จะFalse unicode
jpmc26
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.