Як я можу видалити символи, що не належать до ASCII, але залишити періоди та пробіли за допомогою Python?


100

Я працюю з файлом .txt. Я хочу, щоб рядок тексту з файла не містив символів, що не належать до ASCII. Однак я хочу залишити пробіли та періоди. В даний час я їх також знімаю. Ось код:

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

Як слід змінити onlyascii (), щоб залишити пробіли та періоди? Я думаю, що це не надто складно, але я не можу цього зрозуміти.


Дякую (щиро) за роз'яснення Джон. Я зрозумів, що пробіли та періоди - це символи ASCII. Однак я видалив їх обох ненавмисно, намагаючись видалити лише символи, що не належать до ASCII. Я бачу, як моє запитання могло означати інакше.

@PoliticalEconomist: Ваша проблема ще недостатньо визначена. Дивіться мою відповідь.
— Джон Махін

Відповіді:


187

Ви можете відфільтрувати всі символи з рядка, який не можна друкувати , використовуючи string.printable , наприклад:

>>> 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 на моїй машині містить:

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

EDIT: На Python 3 фільтр поверне ітерабельний номер. Правильний спосіб повернути рядок назад:

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

2
що з тими символами для друку, які знаходяться нижче порядкових 48?
— Хоакін

38
Єдина проблема використання filter- це те, що він повертає ітерабельний номер. Якщо вам потрібна тятива (як я зробив , тому що мені потрібно було це робити , коли стиснення списку) , то зробити це: ''.join(filter(lambda x: x in string.printable, s).
— cjbarth

5
@cjbarth - коментар python 3 специфічний, але дуже корисний. Дякую!
— підкреслення

7
Чому б не використати регулярний вираз: re.sub(r'[^\x00-\x7f]',r'', your-non-ascii-string). Дивіться цю тему stackoverflow.com/a/20079244/658497
— Ноам Манос

1
@NoamManos це було в 4-5 разів швидше для мене, що приєднався ... фільтр ... лямбда-рішення, дякую.
— artfulrobot

95

Найпростіший спосіб перейти на інший кодек - це за допомогою encode () або decode (). У вашому випадку ви хочете перетворитись у ASCII та проігнорувати всі символи, які не підтримуються. Наприклад, шведська літера å не є символом 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

Редагувати:

Python3: str -> bytes -> 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 (декодує та кодує у зворотному порядку)

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

16
Я отримуюUnicodeDecodeError: 'ascii' codec can't decode byte 0xc2 in position 27
— Xodarap777

2
Я отримав цю помилку, коли я вставив фактичний символ Unicode в рядок за допомогою копіювальної пасти. Якщо ви вказали рядок як u'thestring ', кодер працює правильно.
— Бен Ліянаж

2
Працює лише на Py3, але це елегантно.
— габоровий

7
Для тих, хто отримує ту саму помилку, що і @ Xodarap777: спочатку слід .decode () рядок і лише після цього кодування. Наприкладs.decode('utf-8').encode('ascii', errors='ignore')
— Spc_555

30

За словами @artfulrobot, це має бути швидше, ніж фільтр та лямбда:

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

Дивіться більше прикладів тут http://stackoverflow.com/questions/20078816/replace-non-ascii-characters-with-a-single-space/20079244#20079244


1
Це рішення відповідає на заявлене запитання ОП, але майте на увазі, що воно не видалить символи, що не друкуються, включені до ASCII, які, на мою думку, мають намір задати ОП.
— Даніло Суза Мораш

6

Ваше запитання неоднозначне; перші два речення, взяті разом, означають, що ви вважаєте, що пробіл та "період" - це символи, що не належать до ASCII. Це неправильно. Усі символи такі, що ord (char) <= 127 є символами ASCII. Наприклад, ваша функція виключає цих символів! "# $% & \ '() * +, -. /, Але включає декілька інших, наприклад [] {}.

Відійдіть, подумайте трохи та відредагуйте своє запитання, щоб сказати нам, що ви намагаєтеся зробити, не згадуючи слово ASCII, і чому ви вважаєте, що символи, такі як ord (char)> = 128, не враховують. Також: яка версія Python? Що таке кодування вхідних даних?

Зверніть увагу, що ваш код читає весь вхідний файл як один рядок, а ваш коментар ("чудове рішення") до іншої відповіді означає, що вам не байдуже новинки у ваших даних. Якщо ваш файл містить два рядки:

this is line 1
this is line 2

результат був би 'this is line 1this is line 2'... це те, що ти насправді хочеш?

Більш широке рішення буде включати:

  1. краща назва функції фільтра, ніж onlyascii
  2. визнання, що функції фільтра потрібно просто повернути триєдне значення, якщо аргумент потрібно зберегти:

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

Ця відповідь дуже корисна тим, хто з нас заходить, щоб запитати щось подібне до ОП, і запропонована Вами відповідь є корисно піфонічною. Однак мені здається дивним, що немає більш ефективного рішення проблеми, як ви її інтерпретували (що я часто стикаюся) - характер за характером, це займає дуже великий час у дуже великому файлі.
— Xodarap777

5

Ви можете використовувати такий код, щоб видалити неанглійські букви:

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

Це повернеться

123456790 ABC #%? . ()


1

Якщо ви хочете друкувати символи ascii, ви, ймовірно, повинні виправити свій код:

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

це еквівалентно string.printable(відповідь від @jterrace), за винятком відсутності повернень та вкладок ('\ t', '\ n', '\ x0b', '\ x0c' та '\ r'), але не відповідає діапазон вашого запитання


1
Трохи простіше: лямбда x: 32 <= ord (x) <= 126
— jterrace

це не те саме, що string.printable, оскільки він залишає string.whitespace, хоча це може бути те, чого хоче ОП, залежить від речей, таких як \ n та \ t.
— jterrace

@jterrace праворуч, містить пробіл (порядк 32), але немає повернень та вкладок
— joaquin

так, просто коментуючи "це еквівалентно string.printable", але не відповідає дійсності
— jterrace

Я відредагував відповідь, дякую! питання ОП є оманливим, якщо ви не прочитаєте його уважно.
— Жоакін

1

Попрацювати через Fluent Python (Ramalho) - дуже рекомендується. Перелічіть розуміння одного-лінійки, натхненого Главою 2:

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

Це не дозволить використовувати стандартні символи ASCII, такі як точки кулі, символ градусів, символ авторського права, символ єни тощо. Також ваш перший приклад включає символи, що не друкуються, наприклад, BELL, що небажано.
— SherylHohman
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.