गैर- ASCII वर्णों को एकल स्थान से बदलें


244

मुझे सभी गैर- ASCII (\ x00- \ x7F) वर्णों को एक स्थान से बदलने की आवश्यकता है। मुझे आश्चर्य है कि पायथन में यह मृत-आसान नहीं है, जब तक कि मैं कुछ याद नहीं कर रहा हूं। निम्न फ़ंक्शन केवल सभी गैर- ASCII वर्णों को निकालता है:

def remove_non_ascii_1(text):

    return ''.join(i for i in text if ord(i)<128)

और यह गैर-ASCII वर्णों को वर्ण कोड बिंदु में बाइट्स की मात्रा के अनुसार रिक्त स्थान की मात्रा के साथ प्रतिस्थापित करता है (अर्थात वर्ण को 3 स्थानों से बदल दिया जाता है):

def remove_non_ascii_2(text):

    return re.sub(r'[^\x00-\x7F]',' ', text)

मैं एक ही स्थान के साथ सभी गैर-एएससीआईआई पात्रों को कैसे बदल सकता हूं?

के असंख्य के समान अतः सवाल , कोई भी पता चरित्र प्रतिस्थापन के रूप में विरोध किया करने के लिए अलग करना , और इसके साथ ही सभी गैर-ascii नहीं वर्ण एक विशिष्ट चरित्र को संबोधित।


46
वाह, आपने वास्तव में इतने सारे लिंक दिखाने के लिए अच्छे प्रयास किए हैं। दिन का नवीनीकरण होते ही +1!
shad0w_wa1k3r

3
आपको लगता है कि यह एक stackoverflow.com/questions/1342000/…
स्टुअर्ट

मुझे एक उदाहरण इनपुट देखने में दिलचस्पी है जिसमें समस्याएं हैं।
dstromberg

5
@ स्टुअर्ट: धन्यवाद, लेकिन यह बहुत पहले वाला है जिसका मैं उल्लेख करता हूं।
dotancohen

1
@ डस्ट्रॉमबर्ग: मैं प्रश्न में एक समस्याग्रस्त उदाहरण चरित्र का उल्लेख करता हूं :। यह आदमी है
डॉटान्कोन

जवाबों:


243

आपकी ''.join()अभिव्यक्ति फ़िल्टरिंग है , गैर-एएससीआईआई को हटाकर; आप इसके बजाय एक सशर्त अभिव्यक्ति का उपयोग कर सकते हैं:

return ''.join([i if ord(i) < 128 else ' ' for i in text])

यह एक-एक करके वर्णों को संभालता है और फिर भी प्रति वर्ण प्रति स्थान एक स्थान का उपयोग करेगा।

आपकी नियमित अभिव्यक्ति को लगातार गैर-ASCII वर्णों को एक स्थान से बदलना चाहिए :

re.sub(r'[^\x00-\x7F]+',' ', text)

+वहाँ ध्यान दें ।


18
@ डस्ट्रोमबर्ग: धीमे; एक सूची की str.join() जरूरत है (यह दो बार मूल्यों से अधिक होगा), और एक जनरेटर अभिव्यक्ति पहले एक में परिवर्तित हो जाएगी। यह एक सूची समझ देना बस तेजी से होता है। इस पोस्ट को देखें ।
मार्टिन पीटर्स

1
यदि आप इसे UTF-8 बाइट स्ट्रिंग फ़ीड करते हैं, तो कोड का पहला टुकड़ा प्रति वर्ण में कई रिक्त स्थान सम्मिलित करेगा।
मार्क रैनसम

@MarkRansom: मैं इस अजगर 3. होने के लिए यह सोचते था
मार्टिन पीटर्स

2
प्रश्न में " वर्ण को 3 स्थानों से प्रतिस्थापित किया गया है" का तात्पर्य है कि इनपुट एक बाइटस्ट्रिंग (यूनिकोड नहीं है) और इसलिए पायथन 2 का उपयोग किया जाता है (अन्यथा ''.joinविफल होगा)। यदि ओपी को यूनिकोड कोडपॉइंट के लिए एकल स्थान चाहिए तो इनपुट को पहले यूनिकोड में डिकोड किया जाना चाहिए।
JFS

इसने मेरी बहुत मदद की!
मुहम्मद हसीब

55

आपके लिए मैं आपके मूल स्ट्रिंग का सबसे समान प्रतिनिधित्व प्राप्त करता हूं जो कि मैं यूनिडोस्कोप मॉड्यूल की सिफारिश करता हूं :

from unidecode import unidecode
def remove_non_ascii(text):
    return unidecode(unicode(text, encoding = "utf-8"))

तो आप इसे एक स्ट्रिंग में उपयोग कर सकते हैं:

remove_non_ascii("Ceñía")
Cenia

दिलचस्प सुझाव है, लेकिन यह मानती है कि उपयोगकर्ता गैर एससीआई बनना चाहते हैं कि यूनिडॉब के नियम क्या हैं। हालांकि, यह पूछने वाले के लिए एक अनुवर्ती प्रश्न बनता है कि वे रिक्त स्थान पर क्यों जोर देते हैं, शायद किसी अन्य चरित्र के साथ बदलने के लिए?
jxramos

धन्यवाद, यह एक अच्छा जवाब है। यह इस प्रश्न के उद्देश्य के लिए काम नहीं करता है क्योंकि अधिकांश डेटा जो मैं काम कर रहा हूं उसमें ASCII जैसा प्रतिनिधित्व नहीं है। इस तरह के रूप में דותן। हालाँकि, सामान्य अर्थ में यह बहुत अच्छा है, धन्यवाद!
डॉटंचोहेन

1
हां, मुझे पता है कि यह इस सवाल के लिए काम नहीं करता है , लेकिन मैं उस समस्या को हल करने की कोशिश कर रहा हूं, इसलिए मैंने सोचा कि मैं सिर्फ अपनी समस्या का समाधान साझा करूंगा, जो मुझे लगता है कि @dotancohen के रूप में लोगों के लिए बहुत आम है जो सौदा करते हैं हर समय गैर-अस्की चरित्र के साथ।
अल्वारो फ्यूएंटेस

अतीत में इस तरह से सामान के साथ कुछ सुरक्षा कमजोरियां रही हैं। बस सावधान रहें कि आप इसे कैसे लागू करते हैं!
डेविडेब

UTF-16 एन्कोडेड टेक्स्ट स्ट्रिंग्स के साथ काम करना प्रतीत नहीं होता है
user5359531

22

के लिए चरित्र प्रसंस्करण, यूनिकोड तार का उपयोग करें:

PythonWin 3.3.0 (v3.3.0:bd8afb90ebf2, Sep 29 2012, 10:57:17) [MSC v.1600 64 bit (AMD64)] on win32.
>>> s='ABC马克def'
>>> import re
>>> re.sub(r'[^\x00-\x7f]',r' ',s)   # Each char is a Unicode codepoint.
'ABC  def'
>>> b = s.encode('utf8')
>>> re.sub(rb'[^\x00-\x7f]',rb' ',b) # Each char is a 3-byte UTF-8 sequence.
b'ABC      def'

लेकिन ध्यान दें कि आपको अभी भी एक समस्या होगी यदि आपके स्ट्रिंग में विघटित यूनिकोड वर्ण हैं (उदाहरण के लिए अलग-अलग वर्ण और संयोजन चिह्न,):

>>> s = 'mañana'
>>> len(s)
6
>>> import unicodedata as ud
>>> n=ud.normalize('NFD',s)
>>> n
'mañana'
>>> len(n)
7
>>> re.sub(r'[^\x00-\x7f]',r' ',s) # single codepoint
'ma ana'
>>> re.sub(r'[^\x00-\x7f]',r' ',n) # only combining mark replaced
'man ana'

धन्यवाद, यह एक महत्वपूर्ण अवलोकन है। यदि आप संयोजन-चिह्नों के मामले को संभालने के लिए एक तार्किक तरीका पाते हैं, तो मैं ख़ुशी से इस प्रश्न का जवाब दूंगा। मुझे लगता है कि बस संयोजन चिह्न को हटा देना अभी तक केवल अपरिष्कृत चरित्र को छोड़ना सबसे अच्छा होगा।
डॉटान्चेन

1
एक आंशिक समाधान ud.normalize('NFC',s)अंकों के संयोजन का उपयोग करना है, लेकिन सभी संयोजन संयोजन एकल कोड बिंदुओं द्वारा प्रतिनिधित्व नहीं किए जाते हैं। आपको ud.category()चरित्र को देखते हुए एक बेहतर समाधान की आवश्यकता होगी ।
मार्क टॉलेनन

1
@dotancohen: यूनिकोड में "उपयोगकर्ता-कथित चरित्र" की एक धारणा है जो कई यूनिकोड कोडप्वाइंट्स को फैला सकती है। \X(eXtended grapheme क्लस्टर) regex ( regexमॉड्यूल द्वारा समर्थित ) इस तरह के पात्रों पर ध्यान केंद्रित करने की अनुमति देता है (ध्यान दें: "अंगूर आवश्यक रूप से वर्ण अनुक्रमों का संयोजन नहीं कर रहे हैं, और वर्ण अनुक्रमों का संयोजन आवश्यक रूप से अंगूर नहीं हैं" )।
jfs

10

यदि प्रतिस्थापन चरित्र 'हो सकता है?' एक जगह के बजाय, तो मैं सुझाव देता हूं result = text.encode('ascii', 'replace').decode():

"""Test the performance of different non-ASCII replacement methods."""


import re
from timeit import timeit


# 10_000 is typical in the project that I'm working on and most of the text
# is going to be non-ASCII.
text = 'Æ' * 10_000


print(timeit(
    """
result = ''.join([c if ord(c) < 128 else '?' for c in text])
    """,
    number=1000,
    globals=globals(),
))

print(timeit(
    """
result = text.encode('ascii', 'replace').decode()
    """,
    number=1000,
    globals=globals(),
))

परिणाम:

0.7208260721400134
0.009975979187503592

बदलो ? जरूरत पड़ने पर किसी अन्य पात्र या स्थान के साथ, और आप अभी भी तेज होंगे।
मोरिट्ज़

7

और इसका क्या?

def replace_trash(unicode_string):
     for i in range(0, len(unicode_string)):
         try:
             unicode_string[i].encode("ascii")
         except:
              #means it's non-ASCII
              unicode_string=unicode_string[i].replace(" ") #replacing it with a single space
     return unicode_string

1
हालांकि यह बहुत अयोग्य है, यह बहुत पठनीय है। धन्यवाद।
dotancohen

1
यूनिकोड से निपटने के लिए +1 ... @dotancohen IMNSHO "पठनीय" का अर्थ "व्यावहारिक" है जो "सुरुचिपूर्ण" में जोड़ता है, इसलिए मैं कहूंगा "थोड़ा
असावधान

3

एक देशी और कुशल दृष्टिकोण के रूप में, आपको ordपात्रों पर किसी लूप का उपयोग करने की आवश्यकता नहीं है । बस asciiत्रुटियों के साथ सांकेतिक शब्दों में बदलना और उपेक्षा करना।

निम्नलिखित गैर-असिसी पात्रों को हटा देगा:

new_string = old_string.encode('ascii',errors='ignore')

यदि आप हटाए गए वर्णों को बदलना चाहते हैं, तो बस निम्नलिखित करें:

final_string = new_string + b' ' * (len(old_string) - len(new_string))

Python3 में, यह encodeबाइटस्ट्रिंग लौटाएगा, इसलिए इसे ध्यान में रखें। इसके अलावा, यह विधि न्यूलाइन जैसे वर्णों को अलग नहीं करेगी।
काइल गिब्सन

-1

संभावित रूप से एक अलग प्रश्न के लिए, लेकिन मैं @ Alvero के उत्तर का अपना संस्करण प्रदान कर रहा हूं (unidecode का उपयोग करके)। मैं अपने स्ट्रिंग्स पर एक "रेगुलर" स्ट्रिप करना चाहता हूं, यानी व्हॉट्सएप के कैरेक्टर के लिए मेरी स्ट्रिंग की शुरुआत और अंत, और उसके बाद दूसरे व्हाट्सएप के कैरेक्टर को "रेगुलर" स्पेस के साथ रिप्लेस करें, यानी

"Ceñíaㅤmañanaㅤㅤㅤㅤ"

सेवा

"Ceñía mañana"

,

def safely_stripped(s: str):
    return ' '.join(
        stripped for stripped in
        (bit.strip() for bit in
         ''.join((c if unidecode(c) else ' ') for c in s).strip().split())
        if stripped)

हम पहले सभी गैर-यूनिकोड रिक्त स्थान को एक नियमित स्थान से बदल देते हैं (और इसे फिर से जोड़ते हैं),

''.join((c if unidecode(c) else ' ') for c in s)

और फिर हम अजगर के सामान्य विभाजन के साथ फिर से विभाजित करते हैं, और प्रत्येक "बिट" को पट्टी करते हैं,

(bit.strip() for bit in s.split())

और अंत में उन वापस फिर से शामिल हो, लेकिन केवल अगर स्ट्रिंग एक ifपरीक्षा पास करता है,

' '.join(stripped for stripped in s if stripped)

और उसके साथ, safely_stripped('ㅤㅤㅤㅤCeñíaㅤmañanaㅤㅤㅤㅤ')सही ढंग से रिटर्न 'Ceñía mañana'

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.