Tokenizing non English Text in Python

自古美人都是妖i 提交于 2019-12-10 20:09:27

问题


I have a Persian text file that has some lines like this:

 ذوب 6 خوی 7 بزاق ،آب‌دهان ، یم 10 زهاب، 11 آبرو، حیثیت، شرف

I want to generate a list of words from this line. For me the word borders are numbers, like 6, 7, etc in the above line and also ، character. so the list should be:

[ 'ذوب','خوی','بزاق','آب‌دهان','یم','زهاب','آبرو','حیثیت' ,'شرف'] 

I want to do this in Python 3.3. What is the best way of doing this, I really appreciate any help on this.

EDIT:

I got a number of answers but when I used them for another test case they didn't work. The test case is this:

منهدم کردن : 1 خراب کردن، ویران کردن، تخریب کردن 2 نابود کردن، از بین بردن 

and I expect to have a list of tokens as this:

['منهدم کردن','خراب کردن', 'ویران کردن', 'تخریب کردن','نابود کردن', 'از بین بردن']  

回答1:


Using regex package:

>>> import regex
>>> text = 'ذوب 6 خوی 7 بزاق ،آب‌دهان ، یم 10 زهاب، 11 آبرو، حیثیت، شرف'
>>> regex.findall(r'\p{L}+', text.replace('\u200c', ''))
['ذوب', 'خوی', 'بزاق', 'آبدهان', 'یم', 'زهاب', 'آبرو', 'حیثیت', 'شرف']
  • The text contains ZERO WIDTH NON-JOINER (U+200C). removed the character using str.replace.
  • \p{L} or \p{Letter} matches any kind of letter from any language.

See Regex Tutorial - Unicode Characters and Properties.

UPDATE

To also include U+200C, use [\p{Cf}\p{L}]+ instead (\p{Cf} or \p{Format} matches invisible formatting character):

>>> regex.findall(r'[\p{Cf}\p{L}]+', text)
['ذوب', 'خوی', 'بزاق', 'آب\u200cدهان', 'یم', 'زهاب', 'آبرو', 'حیثیت', 'شرف']

It looks diffent from what you want, but they are equal:

>>> got = regex.findall(r'[\p{Cf}\p{L}]+', text)
>>> want = [ 'ذوب','خوی','بزاق','آب‌دهان','یم','زهاب','آبرو','حیثیت' ,'شرف']
>>> print(want)
['ذوب', 'خوی', 'بزاق', 'آب\u200cدهان', 'یم', 'زهاب', 'آبرو', 'حیثیت', 'شرف']
>>> got == want
>>> got[:3]
['ذوب', 'خوی', 'بزاق']
>>> got[4:]
['یم', 'زهاب', 'آبرو', 'حیثیت', 'شرف']

UPDATE2

Some words in the edited question contains a space.

>>> ' ' in 'منهدم کردن'
True

I added \s in the following code to also match the spaces, then strip the leading, trailing spaces from the matched strings, then filtered out empty strings.

>>> text = 'منهدم کردن : 1 خراب کردن، ویران کردن، تخریب کردن 2 نابود کردن، از بین بردن'
>>> want = ['منهدم کردن','خراب کردن', 'ویران کردن', 'تخریب کردن','نابود کردن', 'از بین بردن']
>>> [x for x  in map(str.strip, regex.findall(r'[\p{Cf}\p{L}\s]+', text)) if x] == want
True



回答2:


Use re.split to split on whitespace (\s), digits (\d) and the ، character.

# python 3
import re
INPUT = 'ذوب 6 خوی 7 بزاق ،آب‌دهان ، یم 10 زهاب، 11 آبرو، حیثیت، شرف'
EXPECTED = [ 'ذوب','خوی','بزاق','آب‌دهان','یم','زهاب','آبرو','حیثیت' ,'شرف'] 

OUTPUT = re.split('[\s\d،]+', INPUT)
assert OUTPUT == EXPECTED
print('\n'.join(OUTPUT))

Note the \u200c you are seeing in the output array is a non-printing character, and is actually contained in the original string. Python is escaping it as it is showing the representation of the array and contained strings, not printing the string for display. Here's the difference:

INPUT = 'ذوب 6 خوی 7 بزاق ،آب‌دهان ، یم 10 زهاب، 11 آبرو، حیثیت، شرف'
print(INPUT)
ذوب 6 خوی 7 بزاق ،آب‌دهان ، یم 10 زهاب، 11 آبرو، حیثیت، شرف

print(repr(INPUT)) # notice the \u200c below
'ذوب 6 خوی 7 بزاق ،آب\u200cدهان ، یم 10 زهاب، 11 آبرو، حیثیت، شرف'

print(['in', 'an', 'array', INPUT]) # the \u200c is also shown when printing an array
['in', 'an', 'array', 'ذوب 6 خوی 7 بزاق ،آب\u200cدهان ، یم 10 زهاب، 11 آبرو، حیثیت، شرف']

This is similar to how python handles newline characters:

>>> 'new\nline'
'new\nline'
>>> print 'new\nline'
new
line

Edit:

Here is the regex for your updated sample that uses falsetru's findall strategy, but uses the built-in re module:

OUTPUT = [s.strip() for s in re.findall(r'(?:[^\W\d_]|[\s])+', INPUT) if s.strip()]

The pattern (?:[^\W\d_]|[\s])+ is a little strange, as Python's re module has no equivalent to regex's "Letters" \p{L}, so instead we use the solution proposed here https://stackoverflow.com/a/8923988/66349

[^\W\d_] - (not ((not alphanumeric) or digits or underscore))

So in summary, match one or more characters (+) that are either (|): Unicode letters [^\W\d_, or whitespace \s.

falsetru's method is probably more readable, but requires the 3rd party library.



来源:https://stackoverflow.com/questions/21011638/tokenizing-non-english-text-in-python

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!