From 735f5fabcc945aa9ce0c0ccbfbd7b0a02a88b146 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 14 Nov 2018 17:14:31 +0800 Subject: [PATCH 01/30] Update README.md for beta 0.3 --- README.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d980293..71f3fc4 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,23 @@ Inspired from package [profanity](https://github.com/ben174/profanity) of [Ben F ## Requirements To make use of Python static tying, this package only works with `Python 3.6+`. +## Installation + +### 1. *Stable* version: +``` +$ pip install better_profanity +``` + +### 2. *Beta* version +``` +$ pip install better-profanity==0.3b0 +``` + ## Unicode characters A huge thanks to [@Derfirm](https://github.com/Derfirm) for adding support for Unicode characters. +Currently, the Unicode support is only available in *beta* release `0.3-beta.0`. + For release `0.3-beta.0`, only Unicode characters from categories `Ll`, `Lu`, `Mc` and `Mn` are added. More on Unicode categories can be found [here][unicode category link]. [unicode category link]: https://en.wikipedia.org/wiki/Template:General_Category_(Unicode) @@ -95,7 +109,7 @@ if __name__ == "__main__": # Have a **** day! :) ``` -### 6. Censor Unicode characters +### 6. Censor Unicode characters (*beta*) ``` from better_profanity import profanity From ca5085459fd06f372523c870f6be1b8304f93c89 Mon Sep 17 00:00:00 2001 From: snguyenthanh <35214933+snguyenthanh@users.noreply.github.com> Date: Wed, 14 Nov 2018 17:15:33 +0800 Subject: [PATCH 02/30] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 71f3fc4..4eb18a1 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ $ pip install better-profanity==0.3b0 ## Unicode characters A huge thanks to [@Derfirm](https://github.com/Derfirm) for adding support for Unicode characters. -Currently, the Unicode support is only available in *beta* release `0.3-beta.0`. +Currently, the Unicode support is only available in [*beta* release `0.3-beta.0`](./README.md#2-beta-version). For release `0.3-beta.0`, only Unicode characters from categories `Ll`, `Lu`, `Mc` and `Mn` are added. More on Unicode categories can be found [here][unicode category link]. From 7481b99ec751edded8a6551af04528683d3f3e6d Mon Sep 17 00:00:00 2001 From: snguyenthanh <35214933+snguyenthanh@users.noreply.github.com> Date: Wed, 14 Nov 2018 22:46:14 +0800 Subject: [PATCH 03/30] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4eb18a1..b84cc06 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ $ pip install better-profanity==0.3b0 ## Unicode characters A huge thanks to [@Derfirm](https://github.com/Derfirm) for adding support for Unicode characters. -Currently, the Unicode support is only available in [*beta* release `0.3-beta.0`](./README.md#2-beta-version). +Currently, the Unicode support is only available in [*beta* release `0.3-beta.0`](https://pypi.org/project/better-profanity/0.3b0/). For release `0.3-beta.0`, only Unicode characters from categories `Ll`, `Lu`, `Mc` and `Mn` are added. More on Unicode categories can be found [here][unicode category link]. @@ -51,7 +51,7 @@ if __name__ == "__main__": # You **** of ****. ``` -### 2. Censor doesn't care about word dividers +### 2. Censor doesn't care about word dividers (*beta*) The function `.censor()` also hide words separated not just by an empty space ` ` but also other dividers, such as `_`, `,` and `.`. Except for `@, $, ^, *, &, \, \`. ``` From 9944efa8acc5f0626ccba5667ed09c0b8d7d2a39 Mon Sep 17 00:00:00 2001 From: snguyenthanh <35214933+snguyenthanh@users.noreply.github.com> Date: Wed, 14 Nov 2018 22:49:09 +0800 Subject: [PATCH 04/30] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b84cc06..57234f4 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ if __name__ == "__main__": ``` ### 2. Censor doesn't care about word dividers (*beta*) -The function `.censor()` also hide words separated not just by an empty space ` ` but also other dividers, such as `_`, `,` and `.`. Except for `@, $, ^, *, &, \, \`. +The function `.censor()` also hide words separated not just by an empty space ` ` but also other dividers, such as `_`, `,` and `.`. Except for `@, $, ^, *, &, ", '`. ``` from better_profanity import profanity From 908130e6c7d28763f11ad2b3514abacd4958807b Mon Sep 17 00:00:00 2001 From: snguyenthanh <35214933+snguyenthanh@users.noreply.github.com> Date: Wed, 14 Nov 2018 22:50:45 +0800 Subject: [PATCH 05/30] Update utils.py --- better_profanity/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/better_profanity/utils.py b/better_profanity/utils.py index 45d14e4..692513b 100644 --- a/better_profanity/utils.py +++ b/better_profanity/utils.py @@ -10,7 +10,7 @@ ## GLOBAL VARIABLES ## ALLOWED_CHARACTERS = set(ascii_letters) ALLOWED_CHARACTERS.update(set(digits)) -ALLOWED_CHARACTERS.update({'@', '$', '^', '*', '&', '\"', '\''}) +ALLOWED_CHARACTERS.update({'@', '$', '*', '&', '\"', '\''}) def get_complete_path_of_file(filename: str) -> str: From b5be10ba66bc75e7f0ebc3092764c044add43661 Mon Sep 17 00:00:00 2001 From: snguyenthanh <35214933+snguyenthanh@users.noreply.github.com> Date: Wed, 14 Nov 2018 22:51:12 +0800 Subject: [PATCH 06/30] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 57234f4..9fa67cf 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ if __name__ == "__main__": ``` ### 2. Censor doesn't care about word dividers (*beta*) -The function `.censor()` also hide words separated not just by an empty space ` ` but also other dividers, such as `_`, `,` and `.`. Except for `@, $, ^, *, &, ", '`. +The function `.censor()` also hide words separated not just by an empty space ` ` but also other dividers, such as `_`, `,` and `.`. Except for `@, $, *, &, ", '`. ``` from better_profanity import profanity From 33b43e5411249c2ed8d7c77ba376d0aca3acfb80 Mon Sep 17 00:00:00 2001 From: snguyenthanh <35214933+snguyenthanh@users.noreply.github.com> Date: Thu, 15 Nov 2018 15:32:22 +0800 Subject: [PATCH 07/30] Fix a minor typo in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9fa67cf..08b40e8 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ better_profanity Inspired from package [profanity](https://github.com/ben174/profanity) of [Ben Friedland](https://github.com/ben174), this library is much faster than the original one, by using string comparison instead of regex. ## Requirements -To make use of Python static tying, this package only works with `Python 3.6+`. +To make use of Python static typing, this package only works with `Python 3.6+`. ## Installation From f4fbf68959c083fd5ef812d5b131fd6c74b2fd6b Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 16 Nov 2018 20:15:30 +0800 Subject: [PATCH 08/30] Release 0.3.0 --- README.md | 44 +++++++++++++++++++---------------- better_profanity/__init__.py | 2 +- better_profanity/profanity.py | 2 +- better_profanity/utils.py | 2 +- setup.py | 4 +++- tests.py | 8 +++---- 6 files changed, 34 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 08b40e8..98475df 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ better_profanity --- -*A Python library to clean swear words in strings.* +*A Python library to clean swear words (and their leetspeak) in strings* [![release](https://img.shields.io/badge/dynamic/json.svg?label=release&url=https%3A%2F%2Fpypi.org%2Fpypi%2Fbetter-profanity%2Fjson&query=%24.info.version&colorB=blue)](https://github.com/snguyenthanh/better_profanity/releases/latest) [![Build Status](https://travis-ci.com/snguyenthanh/better_profanity.svg?branch=master)](https://travis-ci.com/snguyenthanh/better_profanity) @@ -10,32 +10,34 @@ better_profanity Inspired from package [profanity](https://github.com/ben174/profanity) of [Ben Friedland](https://github.com/ben174), this library is much faster than the original one, by using string comparison instead of regex. +It partially supports [modified spellings](https://en.wikipedia.org/wiki/Leet) (such as `p0rn`, `h4ndjob` and `handj0b`). + ## Requirements To make use of Python static typing, this package only works with `Python 3.6+`. ## Installation - -### 1. *Stable* version: ``` $ pip install better_profanity ``` -### 2. *Beta* version -``` -$ pip install better-profanity==0.3b0 -``` - ## Unicode characters A huge thanks to [@Derfirm](https://github.com/Derfirm) for adding support for Unicode characters. -Currently, the Unicode support is only available in [*beta* release `0.3-beta.0`](https://pypi.org/project/better-profanity/0.3b0/). - -For release `0.3-beta.0`, only Unicode characters from categories `Ll`, `Lu`, `Mc` and `Mn` are added. More on Unicode categories can be found [here][unicode category link]. +Only Unicode characters from categories `Ll`, `Lu`, `Mc` and `Mn` are added. More on Unicode categories can be found [here][unicode category link]. [unicode category link]: https://en.wikipedia.org/wiki/Template:General_Category_(Unicode) +However, this library has not supported all languages yet, such as *Chinese*. + ## Usage -By default, on the first `.censor()` call, `profanity` initializes a set of words, from [profanity_wordlist.txt](./better_profanity/profanity_wordlist.txt), to be used to compare against the input texts. This set of words will be stored in memory (~5MB+). +By default, on the first `.censor()` call, function `.load_censor_words()` generates all possible [leetspeak](https://en.wikipedia.org/wiki/Leet) words, from [profanity_wordlist.txt](./better_profanity/profanity_wordlist.txt), to be used to compare against the input texts. The full mapping of the library can be found in [profanity.py](./better_profanity/profanity.py#L9-L18). + +For example, the word `handjob` would be loaded into: +``` +'h@ndjob', 'handj0b', 'handj@b', 'h*ndj*b', 'h*ndjob', 'h@ndj0b', 'h@ndj*b', 'h4ndj*b', 'h@ndj@b', 'handjob', 'h4ndj0b', 'h4ndjob', 'h4ndj@b', 'h*ndj0b', 'handj*b', 'h*ndj@b' +``` + +This set of words will be stored in memory (~5MB+). ### 1. Censor swear words from a text By default, `profanity` replaces each swear words with 4 asterisks `****`. @@ -51,14 +53,14 @@ if __name__ == "__main__": # You **** of ****. ``` -### 2. Censor doesn't care about word dividers (*beta*) -The function `.censor()` also hide words separated not just by an empty space ` ` but also other dividers, such as `_`, `,` and `.`. Except for `@, $, *, &, ", '`. +### 2. Censor doesn't care about word dividers +The function `.censor()` also hide words separated not just by an empty space ` ` but also other dividers, such as `_`, `,` and `.`. Except for `@, $, *, ", '`. ``` from better_profanity import profanity if __name__ == "__main__": - text = "...shit...hello_cat_fuck,,,,123" + text = "...sh1t...hello_cat_fuck,,,,123" censored_text = profanity.censor(text) print(censored_text) @@ -67,6 +69,7 @@ if __name__ == "__main__": ### 3. Censor swear words with custom character 4 instances of the character in second parameter in `.censor()` will be used to replace the swear words. + ``` from better_profanity import profanity @@ -79,6 +82,8 @@ if __name__ == "__main__": ``` ### 4. Check if the string contains any swear words +Function `.contains_profanity()` return `True` if any words in the given string has a word existing in the wordlist. + ``` from better_profanity import profanity @@ -90,15 +95,13 @@ if __name__ == "__main__": ``` ### 5. Censor swear words with a custom wordlist -The provided list of words will replace the default wordlist. +Function `.load_censor_words()` takes a `List` of strings as censored words. +The provided list will replace the default wordlist. -4 instances of the character in second parameter in `.censor()` will be used to replace the swear words. ``` from better_profanity import profanity if __name__ == "__main__": - text = "You p1ec3 of sHit." - custom_badwords = ['happy', 'jolly', 'merry'] profanity.load_censor_words(custom_badwords) @@ -109,7 +112,8 @@ if __name__ == "__main__": # Have a **** day! :) ``` -### 6. Censor Unicode characters (*beta*) +### 6. Censor Unicode characters +No extra steps needed! ``` from better_profanity import profanity diff --git a/better_profanity/__init__.py b/better_profanity/__init__.py index 74540fc..8682292 100644 --- a/better_profanity/__init__.py +++ b/better_profanity/__init__.py @@ -1,2 +1,2 @@ name = 'better_profanity' -__version__ = '0.3-beta.0' +__version__ = '0.3.0' diff --git a/better_profanity/profanity.py b/better_profanity/profanity.py index 4870e5c..feabdc5 100644 --- a/better_profanity/profanity.py +++ b/better_profanity/profanity.py @@ -7,7 +7,7 @@ ## GLOBAL VARIABLES ## CENSOR_WORDSET = set() CHARS_MAPPING = { - 'a': ('a', '@', '*', '4', '&'), + 'a': ('a', '@', '*', '4'), 'i': ('i', '*', 'l', '1'), 'o': ('o', '*', '0', '@'), 'u': ('u', '*', 'v'), diff --git a/better_profanity/utils.py b/better_profanity/utils.py index 692513b..039c983 100644 --- a/better_profanity/utils.py +++ b/better_profanity/utils.py @@ -10,7 +10,7 @@ ## GLOBAL VARIABLES ## ALLOWED_CHARACTERS = set(ascii_letters) ALLOWED_CHARACTERS.update(set(digits)) -ALLOWED_CHARACTERS.update({'@', '$', '*', '&', '\"', '\''}) +ALLOWED_CHARACTERS.update({'@', '$', '*', '\"', '\''}) def get_complete_path_of_file(filename: str) -> str: diff --git a/setup.py b/setup.py index 647b04f..fc5fc39 100644 --- a/setup.py +++ b/setup.py @@ -10,12 +10,14 @@ version=__version__, author="Son Nguyen Thanh", author_email="thanhson16198@gmail.com", - description="A Python library to clean English swear words in strings", + description="A Python library to clean swear words (and their leetspeak) in strings", long_description=long_description, long_description_content_type="text/markdown", url="https://github.com/snguyenthanh/better_profanity", classifiers=[ "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", ], diff --git a/tests.py b/tests.py index d4ec88d..5f7c138 100644 --- a/tests.py +++ b/tests.py @@ -70,7 +70,7 @@ def test_custom_wordlist(self): self.assertTrue(profanity.contains_profanity("Have a merry day! :)")) def test_censorship_without_spaces(self): - bad_text = "...penis...hello_cat_vagina,,,,qew" + bad_text = "...pen1s...hello_cat_vagina,,,,qew" censored_text = "...****...hello_cat_****,,,,qew" self.assertEqual(profanity.censor(bad_text), censored_text) @@ -119,9 +119,9 @@ def setUp(self): profanity.load_censor_words() def test_unicode_vietnamese_1(self): - bad_text = "Chào con cặc.Thằng lồn." - censored_text = "Chào con ****.Thằng ****." - profanity.load_censor_words(["cặc", "lồn"]) + bad_text = "Đây là 1 câu nói bậy." + censored_text = "Đây là 1 **** nói ****." + profanity.load_censor_words(["câu", "bậy"]) self.assertEqual(profanity.censor(bad_text), censored_text) def test_unicode_vietnamese_2(self): From a862ebdcd8c36db316bf3c89983eac20da36aa0d Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 24 Feb 2019 12:34:46 +0800 Subject: [PATCH 09/30] Version 0.3.1 - Remove unused dependencies --- CONTRIBUTING.md | 80 ++++++++ README.md | 36 +++- better_profanity.egg-info/PKG-INFO | 167 ++++++++++++++++ better_profanity.egg-info/SOURCES.txt | 12 ++ .../dependency_links.txt | 1 + better_profanity.egg-info/top_level.txt | 1 + better_profanity/__init__.py | 2 +- better_profanity/utils.py | 2 - build/lib/better_profanity/__init__.py | 2 + .../better_profanity/alphabetic_unicode.json | 1 + build/lib/better_profanity/profanity.py | 179 ++++++++++++++++++ .../better_profanity/profanity_wordlist.txt | 140 ++++++++++++++ build/lib/better_profanity/utils.py | 95 ++++++++++ dist/better_profanity-0.3.1-py3-none-any.whl | Bin 0 -> 39944 bytes dist/better_profanity-0.3.1.tar.gz | Bin 0 -> 23443 bytes setup.py | 2 +- tests.py | 4 + 17 files changed, 713 insertions(+), 11 deletions(-) create mode 100644 CONTRIBUTING.md create mode 100644 better_profanity.egg-info/PKG-INFO create mode 100644 better_profanity.egg-info/SOURCES.txt create mode 100644 better_profanity.egg-info/dependency_links.txt create mode 100644 better_profanity.egg-info/top_level.txt create mode 100644 build/lib/better_profanity/__init__.py create mode 100644 build/lib/better_profanity/alphabetic_unicode.json create mode 100644 build/lib/better_profanity/profanity.py create mode 100644 build/lib/better_profanity/profanity_wordlist.txt create mode 100644 build/lib/better_profanity/utils.py create mode 100644 dist/better_profanity-0.3.1-py3-none-any.whl create mode 100644 dist/better_profanity-0.3.1.tar.gz diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..2ba5e61 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,80 @@ +# Contributing to better_profanity + +It is so nice you wanna contribute to this repository. Thank you very much. + +You may contribute in several ways like: + +* Creating new features +* Fixing bugs +* Improving documentation and examples + +## Table of contents +* [Develop](#develop) +* [Run tests](#run-tests) +* [Report a bug](#report-a-bug) +* [Request a feature](#request-a-feature) +* [Commit message](#commit-message) +* [Code style](#code-style) + + +## Develop + +The main API interface is in [profanity.py](./better_profanity/profanity.py), which will call +the complexed processing functions in [utils.py](./better_profanity/utils.py) + +The [alphabetic_unicode.json](./better_profanity/alphabetic_unicode.json) contains the Unicode characters (from categories `Ll`, `Lu`, `Mc` and `Mn`). More on Unicode categories can be found [here][unicode category link]. + +[unicode category link]: https://en.wikipedia.org/wiki/Template:General_Category_(Unicode) + +The [profanity_wordlist.txt](./better_profanity/profanity_wordlist.txt) contains all the swear words to be censored. + +The [tests.py](./tests.py) is for now the only unit test file in the project. + +## Run tests + +This package uses [unittest](https://docs.python.org/3/library/unittest.html) for testing. + +### 1. Run all tests +``` +$ python tests.py +``` + +### 2. Run all tests in a class +For example, run all test cases in class [ProfanityTest]((./tests.py#L6): +``` +$ python -m unittest tests.ProfanityTest +``` + +### 3. Run a specific test case +For example, run the test [test_censorship_empty_text](./tests.py#L53) in class [ProfanityTest]((./tests.py#L6): +``` +$ python -m unittest tests.ProfanityTest.test_censorship_empty_text +``` + +## Report a bug + +Use the [GitHub issue tracker](https://github.com/snguyenthanh/better_profanity/issues) to report any bug you find. +Bugs description should include: + +* How to reproduce the bug; +* Easy to understand title; + +Would be nice to have some code showing how to reproduce the code, you may use [gist](https://gist.github.com) for uploading your example code. + +## Request a feature + +Use the [GitHub issue tracker](https://github.com/snguyenthanh/better_profanity/issues) to request a new feature. + +## Commit message + +Commit messages should includes GitHub number reference and a imperative easy to understand sentence. + +## Code style + +This project follows [PEP 8](https://www.python.org/dev/peps/pep-0008/) and [PEP 484](https://www.python.org/dev/peps/pep-0484/) for *Python*. + +--- + +Thank you for reading this. + +Give this repo a star and/or share it with your friends. diff --git a/README.md b/README.md index 98475df..5c7fab9 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,18 @@ -better_profanity ---- +# better_profanity *A Python library to clean swear words (and their leetspeak) in strings* [![release](https://img.shields.io/badge/dynamic/json.svg?label=release&url=https%3A%2F%2Fpypi.org%2Fpypi%2Fbetter-profanity%2Fjson&query=%24.info.version&colorB=blue)](https://github.com/snguyenthanh/better_profanity/releases/latest) [![Build Status](https://travis-ci.com/snguyenthanh/better_profanity.svg?branch=master)](https://travis-ci.com/snguyenthanh/better_profanity) -![python](https://img.shields.io/badge/python-3.6%20%7C%203.7-blue.svg) +![python](https://img.shields.io/badge/python-3.5%2B-blue.svg) [![license](https://img.shields.io/github/license/mashape/apistatus.svg?style=popout)](https://github.com/snguyenthanh/better_profanity/blob/master/LICENSE) Inspired from package [profanity](https://github.com/ben174/profanity) of [Ben Friedland](https://github.com/ben174), this library is much faster than the original one, by using string comparison instead of regex. -It partially supports [modified spellings](https://en.wikipedia.org/wiki/Leet) (such as `p0rn`, `h4ndjob` and `handj0b`). +It supports [modified spellings](https://en.wikipedia.org/wiki/Leet) (such as `p0rn`, `h4ndjob` and `handj0b`). ## Requirements -To make use of Python static typing, this package only works with `Python 3.6+`. +To make use of Python static typing, this package only works with `Python 3.5+`. ## Installation ``` @@ -21,7 +20,6 @@ $ pip install better_profanity ``` ## Unicode characters -A huge thanks to [@Derfirm](https://github.com/Derfirm) for adding support for Unicode characters. Only Unicode characters from categories `Ll`, `Lu`, `Mc` and `Mn` are added. More on Unicode categories can be found [here][unicode category link]. @@ -34,7 +32,8 @@ By default, on the first `.censor()` call, function `.load_censor_words()` gener For example, the word `handjob` would be loaded into: ``` -'h@ndjob', 'handj0b', 'handj@b', 'h*ndj*b', 'h*ndjob', 'h@ndj0b', 'h@ndj*b', 'h4ndj*b', 'h@ndj@b', 'handjob', 'h4ndj0b', 'h4ndjob', 'h4ndj@b', 'h*ndj0b', 'handj*b', 'h*ndj@b' +'h@ndjob', 'handj0b', 'handj@b', 'h*ndj*b', 'h*ndjob', 'h@ndj0b', 'h@ndj*b', 'h4ndj*b', +'h@ndj@b', 'handjob', 'h4ndj0b', 'h4ndjob', 'h4ndj@b', 'h*ndj0b', 'handj*b', 'h*ndj@b' ``` This set of words will be stored in memory (~5MB+). @@ -126,3 +125,26 @@ if __name__ == "__main__": print(censored_text) # Эффекти́вного **** от я́да фу́гу не существу́ет до сих пор ``` + +## Testing +``` +$ python tests.py +``` + +## Versions +- [v0.3.1](https://github.com/snguyenthanh/better_profanity/releases/tag/0.3.1) - Remove unused dependencies. +- [v0.3.0](https://github.com/snguyenthanh/better_profanity/releases/tag/0.3.0) - Add support for Unicode characters (Categories: Ll, Lu, Mc and Mn) [#2](https://github.com/snguyenthanh/better_profanity/pull/2). +- [v0.2.0](https://github.com/snguyenthanh/better_profanity/releases/tag/0.2) - Bug fix + faster censoring +- [v0.1.0](https://github.com/snguyenthanh/better_profanity/releases/tag/v0.1) - Initial release + +## Contributing +Please read [CONTRIBUTING.md](./CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to us. + +## License +This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details + +## Special thanks to +- [Andrew Grinevich](https://github.com/Derfirm) - Add support for Unicode characters. + +## Acknowledgments +- [Ben Friedland](https://github.com/ben174) - For the inspiring package [profanity](https://github.com/ben174/profanity). diff --git a/better_profanity.egg-info/PKG-INFO b/better_profanity.egg-info/PKG-INFO new file mode 100644 index 0000000..4815361 --- /dev/null +++ b/better_profanity.egg-info/PKG-INFO @@ -0,0 +1,167 @@ +Metadata-Version: 2.1 +Name: better-profanity +Version: 0.3.1 +Summary: A Python library to clean swear words (and their leetspeak) in strings +Home-page: https://github.com/snguyenthanh/better_profanity +Author: Son Nguyen Thanh +Author-email: thanhson16198@gmail.com +License: UNKNOWN +Description: # better_profanity + *A Python library to clean swear words (and their leetspeak) in strings* + + [![release](https://img.shields.io/badge/dynamic/json.svg?label=release&url=https%3A%2F%2Fpypi.org%2Fpypi%2Fbetter-profanity%2Fjson&query=%24.info.version&colorB=blue)](https://github.com/snguyenthanh/better_profanity/releases/latest) + [![Build Status](https://travis-ci.com/snguyenthanh/better_profanity.svg?branch=master)](https://travis-ci.com/snguyenthanh/better_profanity) + ![python](https://img.shields.io/badge/python-3.5%2B-blue.svg) + [![license](https://img.shields.io/github/license/mashape/apistatus.svg?style=popout)](https://github.com/snguyenthanh/better_profanity/blob/master/LICENSE) + + + Inspired from package [profanity](https://github.com/ben174/profanity) of [Ben Friedland](https://github.com/ben174), this library is much faster than the original one, by using string comparison instead of regex. + + It supports [modified spellings](https://en.wikipedia.org/wiki/Leet) (such as `p0rn`, `h4ndjob` and `handj0b`). + + ## Requirements + To make use of Python static typing, this package only works with `Python 3.5+`. + + ## Installation + ``` + $ pip install better_profanity + ``` + + ## Unicode characters + + Only Unicode characters from categories `Ll`, `Lu`, `Mc` and `Mn` are added. More on Unicode categories can be found [here][unicode category link]. + + [unicode category link]: https://en.wikipedia.org/wiki/Template:General_Category_(Unicode) + + However, this library has not supported all languages yet, such as *Chinese*. + + ## Usage + By default, on the first `.censor()` call, function `.load_censor_words()` generates all possible [leetspeak](https://en.wikipedia.org/wiki/Leet) words, from [profanity_wordlist.txt](./better_profanity/profanity_wordlist.txt), to be used to compare against the input texts. The full mapping of the library can be found in [profanity.py](./better_profanity/profanity.py#L9-L18). + + For example, the word `handjob` would be loaded into: + ``` + 'h@ndjob', 'handj0b', 'handj@b', 'h*ndj*b', 'h*ndjob', 'h@ndj0b', 'h@ndj*b', 'h4ndj*b', + 'h@ndj@b', 'handjob', 'h4ndj0b', 'h4ndjob', 'h4ndj@b', 'h*ndj0b', 'handj*b', 'h*ndj@b' + ``` + + This set of words will be stored in memory (~5MB+). + + ### 1. Censor swear words from a text + By default, `profanity` replaces each swear words with 4 asterisks `****`. + + ``` + from better_profanity import profanity + + if __name__ == "__main__": + text = "You p1ec3 of sHit." + + censored_text = profanity.censor(text) + print(censored_text) + # You **** of ****. + ``` + + ### 2. Censor doesn't care about word dividers + The function `.censor()` also hide words separated not just by an empty space ` ` but also other dividers, such as `_`, `,` and `.`. Except for `@, $, *, ", '`. + + ``` + from better_profanity import profanity + + if __name__ == "__main__": + text = "...sh1t...hello_cat_fuck,,,,123" + + censored_text = profanity.censor(text) + print(censored_text) + # "...****...hello_cat_****,,,,123" + ``` + + ### 3. Censor swear words with custom character + 4 instances of the character in second parameter in `.censor()` will be used to replace the swear words. + + ``` + from better_profanity import profanity + + if __name__ == "__main__": + text = "You p1ec3 of sHit." + + censored_text = profanity.censor(text, '-') + print(censored_text) + # You ---- of ----. + ``` + + ### 4. Check if the string contains any swear words + Function `.contains_profanity()` return `True` if any words in the given string has a word existing in the wordlist. + + ``` + from better_profanity import profanity + + if __name__ == "__main__": + dirty_text = "That l3sbi4n did a very good H4ndjob." + + profanity.contains_profanity(dirty_text) + # True + ``` + + ### 5. Censor swear words with a custom wordlist + Function `.load_censor_words()` takes a `List` of strings as censored words. + The provided list will replace the default wordlist. + + ``` + from better_profanity import profanity + + if __name__ == "__main__": + custom_badwords = ['happy', 'jolly', 'merry'] + profanity.load_censor_words(custom_badwords) + + print(profanity.contains_profanity("Fuck you!")) + # Fuck you + + print(profanity.contains_profanity("Have a merry day! :)")) + # Have a **** day! :) + ``` + + ### 6. Censor Unicode characters + No extra steps needed! + + ``` + from better_profanity import profanity + + if __name__ == "__main__": + bad_text = "Эффекти́вного противоя́дия от я́да фу́гу не существу́ет до сих пор" + profanity.load_censor_words(["противоя́дия"]) + + censored_text = profanity.censor(text) + print(censored_text) + # Эффекти́вного **** от я́да фу́гу не существу́ет до сих пор + ``` + + ## Testing + ``` + $ python tests.py + ``` + + ## Versions + - [v0.3.1](https://github.com/snguyenthanh/better_profanity/releases/tag/0.3.1) - Remove unused dependencies. + - [v0.3.0](https://github.com/snguyenthanh/better_profanity/releases/tag/0.3.0) - Add support for Unicode characters (Categories: Ll, Lu, Mc and Mn) [#2](https://github.com/snguyenthanh/better_profanity/pull/2). + - [v0.2.0](https://github.com/snguyenthanh/better_profanity/releases/tag/0.2) - Bug fix + faster censoring + - [v0.1.0](https://github.com/snguyenthanh/better_profanity/releases/tag/v0.1) - Initial release + + ## Contributing + Please read [CONTRIBUTING.md](./CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to us. + + ## License + This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details + + ## Special thanks to + - [Andrew Grinevich](https://github.com/Derfirm) - Add support for Unicode characters. + + ## Acknowledgments + - [Ben Friedland](https://github.com/ben174) - For the inspiring package [profanity](https://github.com/ben174/profanity). + +Platform: UNKNOWN +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Requires-Python: >3.6 +Description-Content-Type: text/markdown diff --git a/better_profanity.egg-info/SOURCES.txt b/better_profanity.egg-info/SOURCES.txt new file mode 100644 index 0000000..f2df9bd --- /dev/null +++ b/better_profanity.egg-info/SOURCES.txt @@ -0,0 +1,12 @@ +MANIFEST.in +README.md +setup.py +better_profanity/__init__.py +better_profanity/alphabetic_unicode.json +better_profanity/profanity.py +better_profanity/profanity_wordlist.txt +better_profanity/utils.py +better_profanity.egg-info/PKG-INFO +better_profanity.egg-info/SOURCES.txt +better_profanity.egg-info/dependency_links.txt +better_profanity.egg-info/top_level.txt \ No newline at end of file diff --git a/better_profanity.egg-info/dependency_links.txt b/better_profanity.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/better_profanity.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/better_profanity.egg-info/top_level.txt b/better_profanity.egg-info/top_level.txt new file mode 100644 index 0000000..2a46797 --- /dev/null +++ b/better_profanity.egg-info/top_level.txt @@ -0,0 +1 @@ +better_profanity diff --git a/better_profanity/__init__.py b/better_profanity/__init__.py index 8682292..b4e57f1 100644 --- a/better_profanity/__init__.py +++ b/better_profanity/__init__.py @@ -1,2 +1,2 @@ name = 'better_profanity' -__version__ = '0.3.0' +__version__ = '0.3.1' diff --git a/better_profanity/utils.py b/better_profanity/utils.py index 039c983..42463b8 100644 --- a/better_profanity/utils.py +++ b/better_profanity/utils.py @@ -1,5 +1,3 @@ -import sys -import unicodedata import json import os.path from collections import defaultdict diff --git a/build/lib/better_profanity/__init__.py b/build/lib/better_profanity/__init__.py new file mode 100644 index 0000000..b4e57f1 --- /dev/null +++ b/build/lib/better_profanity/__init__.py @@ -0,0 +1,2 @@ +name = 'better_profanity' +__version__ = '0.3.1' diff --git a/build/lib/better_profanity/alphabetic_unicode.json b/build/lib/better_profanity/alphabetic_unicode.json new file mode 100644 index 0000000..8e75875 --- /dev/null +++ b/build/lib/better_profanity/alphabetic_unicode.json @@ -0,0 +1 @@ +["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "\u00b5", "\u00df", "\u00e0", "\u00e1", "\u00e2", "\u00e3", "\u00e4", "\u00e5", "\u00e6", "\u00e7", "\u00e8", "\u00e9", "\u00ea", "\u00eb", "\u00ec", "\u00ed", "\u00ee", "\u00ef", "\u00f0", "\u00f1", "\u00f2", "\u00f3", "\u00f4", "\u00f5", "\u00f6", "\u00f8", "\u00f9", "\u00fa", "\u00fb", "\u00fc", "\u00fd", "\u00fe", "\u00ff", "\u0101", "\u0103", "\u0105", "\u0107", "\u0109", "\u010b", "\u010d", "\u010f", "\u0111", "\u0113", "\u0115", "\u0117", "\u0119", "\u011b", "\u011d", "\u011f", "\u0121", "\u0123", "\u0125", "\u0127", "\u0129", "\u012b", "\u012d", "\u012f", "\u0131", "\u0133", "\u0135", "\u0137", "\u0138", "\u013a", "\u013c", "\u013e", "\u0140", "\u0142", "\u0144", "\u0146", "\u0148", "\u0149", "\u014b", "\u014d", "\u014f", "\u0151", "\u0153", "\u0155", "\u0157", "\u0159", "\u015b", "\u015d", "\u015f", "\u0161", "\u0163", "\u0165", "\u0167", "\u0169", "\u016b", "\u016d", "\u016f", "\u0171", "\u0173", "\u0175", "\u0177", "\u017a", "\u017c", "\u017e", "\u017f", "\u0180", "\u0183", "\u0185", "\u0188", "\u018c", "\u018d", "\u0192", "\u0195", "\u0199", "\u019a", "\u019b", "\u019e", "\u01a1", "\u01a3", "\u01a5", "\u01a8", "\u01aa", "\u01ab", "\u01ad", "\u01b0", "\u01b4", "\u01b6", "\u01b9", "\u01ba", "\u01bd", "\u01be", "\u01bf", "\u01c6", "\u01c9", "\u01cc", "\u01ce", "\u01d0", "\u01d2", "\u01d4", "\u01d6", "\u01d8", "\u01da", "\u01dc", "\u01dd", "\u01df", "\u01e1", "\u01e3", "\u01e5", "\u01e7", "\u01e9", "\u01eb", "\u01ed", "\u01ef", "\u01f0", "\u01f3", "\u01f5", "\u01f9", "\u01fb", "\u01fd", "\u01ff", "\u0201", "\u0203", "\u0205", "\u0207", "\u0209", "\u020b", "\u020d", "\u020f", "\u0211", "\u0213", "\u0215", "\u0217", "\u0219", "\u021b", "\u021d", "\u021f", "\u0221", "\u0223", "\u0225", "\u0227", "\u0229", "\u022b", "\u022d", "\u022f", "\u0231", "\u0233", "\u0234", "\u0235", "\u0236", "\u0237", "\u0238", "\u0239", "\u023c", "\u023f", "\u0240", "\u0242", "\u0247", "\u0249", "\u024b", "\u024d", "\u024f", "\u0250", "\u0251", "\u0252", "\u0253", "\u0254", "\u0255", "\u0256", "\u0257", "\u0258", "\u0259", "\u025a", "\u025b", "\u025c", "\u025d", "\u025e", "\u025f", "\u0260", "\u0261", "\u0262", "\u0263", "\u0264", "\u0265", "\u0266", "\u0267", "\u0268", "\u0269", "\u026a", "\u026b", "\u026c", "\u026d", "\u026e", "\u026f", "\u0270", "\u0271", "\u0272", "\u0273", "\u0274", "\u0275", "\u0276", "\u0277", "\u0278", "\u0279", "\u027a", "\u027b", "\u027c", "\u027d", "\u027e", "\u027f", "\u0280", "\u0281", "\u0282", "\u0283", "\u0284", "\u0285", "\u0286", "\u0287", "\u0288", "\u0289", "\u028a", "\u028b", "\u028c", "\u028d", "\u028e", "\u028f", "\u0290", "\u0291", "\u0292", "\u0293", "\u0295", "\u0296", "\u0297", "\u0298", "\u0299", "\u029a", "\u029b", "\u029c", "\u029d", "\u029e", "\u029f", "\u02a0", "\u02a1", "\u02a2", "\u02a3", "\u02a4", "\u02a5", "\u02a6", "\u02a7", "\u02a8", "\u02a9", "\u02aa", "\u02ab", "\u02ac", "\u02ad", "\u02ae", "\u02af", "\u0371", "\u0373", "\u0377", "\u037b", "\u037c", "\u037d", "\u0390", "\u03ac", "\u03ad", "\u03ae", "\u03af", "\u03b0", "\u03b1", "\u03b2", "\u03b3", "\u03b4", "\u03b5", "\u03b6", "\u03b7", "\u03b8", "\u03b9", "\u03ba", "\u03bb", "\u03bc", "\u03bd", "\u03be", "\u03bf", "\u03c0", "\u03c1", "\u03c2", "\u03c3", "\u03c4", "\u03c5", "\u03c6", "\u03c7", "\u03c8", "\u03c9", "\u03ca", "\u03cb", "\u03cc", "\u03cd", "\u03ce", "\u03d0", "\u03d1", "\u03d5", "\u03d6", "\u03d7", "\u03d9", "\u03db", "\u03dd", "\u03df", "\u03e1", "\u03e3", "\u03e5", "\u03e7", "\u03e9", "\u03eb", "\u03ed", "\u03ef", "\u03f0", "\u03f1", "\u03f2", "\u03f3", "\u03f5", "\u03f8", "\u03fb", "\u03fc", "\u0430", "\u0431", "\u0432", "\u0433", "\u0434", "\u0435", "\u0436", "\u0437", "\u0438", "\u0439", "\u043a", "\u043b", "\u043c", "\u043d", "\u043e", "\u043f", "\u0440", "\u0441", "\u0442", "\u0443", "\u0444", "\u0445", "\u0446", "\u0447", "\u0448", "\u0449", "\u044a", "\u044b", "\u044c", "\u044d", "\u044e", "\u044f", "\u0450", "\u0451", "\u0452", "\u0453", "\u0454", "\u0455", "\u0456", "\u0457", "\u0458", "\u0459", "\u045a", "\u045b", "\u045c", "\u045d", "\u045e", "\u045f", "\u0461", "\u0463", "\u0465", "\u0467", "\u0469", "\u046b", "\u046d", "\u046f", "\u0471", "\u0473", "\u0475", "\u0477", "\u0479", "\u047b", "\u047d", "\u047f", "\u0481", "\u048b", "\u048d", "\u048f", "\u0491", "\u0493", "\u0495", "\u0497", "\u0499", "\u049b", "\u049d", "\u049f", "\u04a1", "\u04a3", "\u04a5", "\u04a7", "\u04a9", "\u04ab", "\u04ad", "\u04af", "\u04b1", "\u04b3", "\u04b5", "\u04b7", "\u04b9", "\u04bb", "\u04bd", "\u04bf", "\u04c2", "\u04c4", "\u04c6", "\u04c8", "\u04ca", "\u04cc", "\u04ce", "\u04cf", "\u04d1", "\u04d3", "\u04d5", "\u04d7", "\u04d9", "\u04db", "\u04dd", "\u04df", "\u04e1", "\u04e3", "\u04e5", "\u04e7", "\u04e9", "\u04eb", "\u04ed", "\u04ef", "\u04f1", "\u04f3", "\u04f5", "\u04f7", "\u04f9", "\u04fb", "\u04fd", "\u04ff", "\u0501", "\u0503", "\u0505", "\u0507", "\u0509", "\u050b", "\u050d", "\u050f", "\u0511", "\u0513", "\u0515", "\u0517", "\u0519", "\u051b", "\u051d", "\u051f", "\u0521", "\u0523", "\u0525", "\u0527", "\u0529", "\u052b", "\u052d", "\u052f", "\u0561", "\u0562", "\u0563", "\u0564", "\u0565", "\u0566", "\u0567", "\u0568", "\u0569", "\u056a", "\u056b", "\u056c", "\u056d", "\u056e", "\u056f", "\u0570", "\u0571", "\u0572", "\u0573", "\u0574", "\u0575", "\u0576", "\u0577", "\u0578", "\u0579", "\u057a", "\u057b", "\u057c", "\u057d", "\u057e", "\u057f", "\u0580", "\u0581", "\u0582", "\u0583", "\u0584", "\u0585", "\u0586", "\u0587", "\u13f8", "\u13f9", "\u13fa", "\u13fb", "\u13fc", "\u13fd", "\u1c80", "\u1c81", "\u1c82", "\u1c83", "\u1c84", "\u1c85", "\u1c86", "\u1c87", "\u1c88", "\u1d00", "\u1d01", "\u1d02", "\u1d03", "\u1d04", "\u1d05", "\u1d06", "\u1d07", "\u1d08", "\u1d09", "\u1d0a", "\u1d0b", "\u1d0c", "\u1d0d", "\u1d0e", "\u1d0f", "\u1d10", "\u1d11", "\u1d12", "\u1d13", "\u1d14", "\u1d15", "\u1d16", "\u1d17", "\u1d18", "\u1d19", "\u1d1a", "\u1d1b", "\u1d1c", "\u1d1d", "\u1d1e", "\u1d1f", "\u1d20", "\u1d21", "\u1d22", "\u1d23", "\u1d24", "\u1d25", "\u1d26", "\u1d27", "\u1d28", "\u1d29", "\u1d2a", "\u1d2b", "\u1d6b", "\u1d6c", "\u1d6d", "\u1d6e", "\u1d6f", "\u1d70", "\u1d71", "\u1d72", "\u1d73", "\u1d74", "\u1d75", "\u1d76", "\u1d77", "\u1d79", "\u1d7a", "\u1d7b", "\u1d7c", "\u1d7d", "\u1d7e", "\u1d7f", "\u1d80", "\u1d81", "\u1d82", "\u1d83", "\u1d84", "\u1d85", "\u1d86", "\u1d87", "\u1d88", "\u1d89", "\u1d8a", "\u1d8b", "\u1d8c", "\u1d8d", "\u1d8e", "\u1d8f", "\u1d90", "\u1d91", "\u1d92", "\u1d93", "\u1d94", "\u1d95", "\u1d96", "\u1d97", "\u1d98", "\u1d99", "\u1d9a", "\u1e01", "\u1e03", "\u1e05", "\u1e07", "\u1e09", "\u1e0b", "\u1e0d", "\u1e0f", "\u1e11", "\u1e13", "\u1e15", "\u1e17", "\u1e19", "\u1e1b", "\u1e1d", "\u1e1f", "\u1e21", "\u1e23", "\u1e25", "\u1e27", "\u1e29", "\u1e2b", "\u1e2d", "\u1e2f", "\u1e31", "\u1e33", "\u1e35", "\u1e37", "\u1e39", "\u1e3b", "\u1e3d", "\u1e3f", "\u1e41", "\u1e43", "\u1e45", "\u1e47", "\u1e49", "\u1e4b", "\u1e4d", "\u1e4f", "\u1e51", "\u1e53", "\u1e55", "\u1e57", "\u1e59", "\u1e5b", "\u1e5d", "\u1e5f", "\u1e61", "\u1e63", "\u1e65", "\u1e67", "\u1e69", "\u1e6b", "\u1e6d", "\u1e6f", "\u1e71", "\u1e73", "\u1e75", "\u1e77", "\u1e79", "\u1e7b", "\u1e7d", "\u1e7f", "\u1e81", "\u1e83", "\u1e85", "\u1e87", "\u1e89", "\u1e8b", "\u1e8d", "\u1e8f", "\u1e91", "\u1e93", "\u1e95", "\u1e96", "\u1e97", "\u1e98", "\u1e99", "\u1e9a", "\u1e9b", "\u1e9c", "\u1e9d", "\u1e9f", "\u1ea1", "\u1ea3", "\u1ea5", "\u1ea7", "\u1ea9", "\u1eab", "\u1ead", "\u1eaf", "\u1eb1", "\u1eb3", "\u1eb5", "\u1eb7", "\u1eb9", "\u1ebb", "\u1ebd", "\u1ebf", "\u1ec1", "\u1ec3", "\u1ec5", "\u1ec7", "\u1ec9", "\u1ecb", "\u1ecd", "\u1ecf", "\u1ed1", "\u1ed3", "\u1ed5", "\u1ed7", "\u1ed9", "\u1edb", "\u1edd", "\u1edf", "\u1ee1", "\u1ee3", "\u1ee5", "\u1ee7", "\u1ee9", "\u1eeb", "\u1eed", "\u1eef", "\u1ef1", "\u1ef3", "\u1ef5", "\u1ef7", "\u1ef9", "\u1efb", "\u1efd", "\u1eff", "\u1f00", "\u1f01", "\u1f02", "\u1f03", "\u1f04", "\u1f05", "\u1f06", "\u1f07", "\u1f10", "\u1f11", "\u1f12", "\u1f13", "\u1f14", "\u1f15", "\u1f20", "\u1f21", "\u1f22", "\u1f23", "\u1f24", "\u1f25", "\u1f26", "\u1f27", "\u1f30", "\u1f31", "\u1f32", "\u1f33", "\u1f34", "\u1f35", "\u1f36", "\u1f37", "\u1f40", "\u1f41", "\u1f42", "\u1f43", "\u1f44", "\u1f45", "\u1f50", "\u1f51", "\u1f52", "\u1f53", "\u1f54", "\u1f55", "\u1f56", "\u1f57", "\u1f60", "\u1f61", "\u1f62", "\u1f63", "\u1f64", "\u1f65", "\u1f66", "\u1f67", "\u1f70", "\u1f71", "\u1f72", "\u1f73", "\u1f74", "\u1f75", "\u1f76", "\u1f77", "\u1f78", "\u1f79", "\u1f7a", "\u1f7b", "\u1f7c", "\u1f7d", "\u1f80", "\u1f81", "\u1f82", "\u1f83", "\u1f84", "\u1f85", "\u1f86", "\u1f87", "\u1f90", "\u1f91", "\u1f92", "\u1f93", "\u1f94", "\u1f95", "\u1f96", "\u1f97", "\u1fa0", "\u1fa1", "\u1fa2", "\u1fa3", "\u1fa4", "\u1fa5", "\u1fa6", "\u1fa7", "\u1fb0", "\u1fb1", "\u1fb2", "\u1fb3", "\u1fb4", "\u1fb6", "\u1fb7", "\u1fbe", "\u1fc2", "\u1fc3", "\u1fc4", "\u1fc6", "\u1fc7", "\u1fd0", "\u1fd1", "\u1fd2", "\u1fd3", "\u1fd6", "\u1fd7", "\u1fe0", "\u1fe1", "\u1fe2", "\u1fe3", "\u1fe4", "\u1fe5", "\u1fe6", "\u1fe7", "\u1ff2", "\u1ff3", "\u1ff4", "\u1ff6", "\u1ff7", "\u210a", "\u210e", "\u210f", "\u2113", "\u212f", "\u2134", "\u2139", "\u213c", "\u213d", "\u2146", "\u2147", "\u2148", "\u2149", "\u214e", "\u2184", "\u2c30", "\u2c31", "\u2c32", "\u2c33", "\u2c34", "\u2c35", "\u2c36", "\u2c37", "\u2c38", "\u2c39", "\u2c3a", "\u2c3b", "\u2c3c", "\u2c3d", "\u2c3e", "\u2c3f", "\u2c40", "\u2c41", "\u2c42", "\u2c43", "\u2c44", "\u2c45", "\u2c46", "\u2c47", "\u2c48", "\u2c49", "\u2c4a", "\u2c4b", "\u2c4c", "\u2c4d", "\u2c4e", "\u2c4f", "\u2c50", "\u2c51", "\u2c52", "\u2c53", "\u2c54", "\u2c55", "\u2c56", "\u2c57", "\u2c58", "\u2c59", "\u2c5a", "\u2c5b", "\u2c5c", "\u2c5d", "\u2c5e", "\u2c61", "\u2c65", "\u2c66", "\u2c68", "\u2c6a", "\u2c6c", "\u2c71", "\u2c73", "\u2c74", "\u2c76", "\u2c77", "\u2c78", "\u2c79", "\u2c7a", "\u2c7b", "\u2c81", "\u2c83", "\u2c85", "\u2c87", "\u2c89", "\u2c8b", "\u2c8d", "\u2c8f", "\u2c91", "\u2c93", "\u2c95", "\u2c97", "\u2c99", "\u2c9b", "\u2c9d", "\u2c9f", "\u2ca1", "\u2ca3", "\u2ca5", "\u2ca7", "\u2ca9", "\u2cab", "\u2cad", "\u2caf", "\u2cb1", "\u2cb3", "\u2cb5", "\u2cb7", "\u2cb9", "\u2cbb", "\u2cbd", "\u2cbf", "\u2cc1", "\u2cc3", "\u2cc5", "\u2cc7", "\u2cc9", "\u2ccb", "\u2ccd", "\u2ccf", "\u2cd1", "\u2cd3", "\u2cd5", "\u2cd7", "\u2cd9", "\u2cdb", "\u2cdd", "\u2cdf", "\u2ce1", "\u2ce3", "\u2ce4", "\u2cec", "\u2cee", "\u2cf3", "\u2d00", "\u2d01", "\u2d02", "\u2d03", "\u2d04", "\u2d05", "\u2d06", "\u2d07", "\u2d08", "\u2d09", "\u2d0a", "\u2d0b", "\u2d0c", "\u2d0d", "\u2d0e", "\u2d0f", "\u2d10", "\u2d11", "\u2d12", "\u2d13", "\u2d14", "\u2d15", "\u2d16", "\u2d17", "\u2d18", "\u2d19", "\u2d1a", "\u2d1b", "\u2d1c", "\u2d1d", "\u2d1e", "\u2d1f", "\u2d20", "\u2d21", "\u2d22", "\u2d23", "\u2d24", "\u2d25", "\u2d27", "\u2d2d", "\ua641", "\ua643", "\ua645", "\ua647", "\ua649", "\ua64b", "\ua64d", "\ua64f", "\ua651", "\ua653", "\ua655", "\ua657", "\ua659", "\ua65b", "\ua65d", "\ua65f", "\ua661", "\ua663", "\ua665", "\ua667", "\ua669", "\ua66b", "\ua66d", "\ua681", "\ua683", "\ua685", "\ua687", "\ua689", "\ua68b", "\ua68d", "\ua68f", "\ua691", "\ua693", "\ua695", "\ua697", "\ua699", "\ua69b", "\ua723", "\ua725", "\ua727", "\ua729", "\ua72b", "\ua72d", "\ua72f", "\ua730", "\ua731", "\ua733", "\ua735", "\ua737", "\ua739", "\ua73b", "\ua73d", "\ua73f", "\ua741", "\ua743", "\ua745", "\ua747", "\ua749", "\ua74b", "\ua74d", "\ua74f", "\ua751", "\ua753", "\ua755", "\ua757", "\ua759", "\ua75b", "\ua75d", "\ua75f", "\ua761", "\ua763", "\ua765", "\ua767", "\ua769", "\ua76b", "\ua76d", "\ua76f", "\ua771", "\ua772", "\ua773", "\ua774", "\ua775", "\ua776", "\ua777", "\ua778", "\ua77a", "\ua77c", "\ua77f", "\ua781", "\ua783", "\ua785", "\ua787", "\ua78c", "\ua78e", "\ua791", "\ua793", "\ua794", "\ua795", "\ua797", "\ua799", "\ua79b", "\ua79d", "\ua79f", "\ua7a1", "\ua7a3", "\ua7a5", "\ua7a7", "\ua7a9", "\ua7b5", "\ua7b7", "\ua7fa", "\uab30", "\uab31", "\uab32", "\uab33", "\uab34", "\uab35", "\uab36", "\uab37", "\uab38", "\uab39", "\uab3a", "\uab3b", "\uab3c", "\uab3d", "\uab3e", "\uab3f", "\uab40", "\uab41", "\uab42", "\uab43", "\uab44", "\uab45", "\uab46", "\uab47", "\uab48", "\uab49", "\uab4a", "\uab4b", "\uab4c", "\uab4d", "\uab4e", "\uab4f", "\uab50", "\uab51", "\uab52", "\uab53", "\uab54", "\uab55", "\uab56", "\uab57", "\uab58", "\uab59", "\uab5a", "\uab60", "\uab61", "\uab62", "\uab63", "\uab64", "\uab65", "\uab70", "\uab71", "\uab72", "\uab73", "\uab74", "\uab75", "\uab76", "\uab77", "\uab78", "\uab79", "\uab7a", "\uab7b", "\uab7c", "\uab7d", "\uab7e", "\uab7f", "\uab80", "\uab81", "\uab82", "\uab83", "\uab84", "\uab85", "\uab86", "\uab87", "\uab88", "\uab89", "\uab8a", "\uab8b", "\uab8c", "\uab8d", "\uab8e", "\uab8f", "\uab90", "\uab91", "\uab92", "\uab93", "\uab94", "\uab95", "\uab96", "\uab97", "\uab98", "\uab99", "\uab9a", "\uab9b", "\uab9c", "\uab9d", "\uab9e", "\uab9f", "\uaba0", "\uaba1", "\uaba2", "\uaba3", "\uaba4", "\uaba5", "\uaba6", "\uaba7", "\uaba8", "\uaba9", "\uabaa", "\uabab", "\uabac", "\uabad", "\uabae", "\uabaf", "\uabb0", "\uabb1", "\uabb2", "\uabb3", "\uabb4", "\uabb5", "\uabb6", "\uabb7", "\uabb8", "\uabb9", "\uabba", "\uabbb", "\uabbc", "\uabbd", "\uabbe", "\uabbf", "\ufb00", "\ufb01", "\ufb02", "\ufb03", "\ufb04", "\ufb05", "\ufb06", "\ufb13", "\ufb14", "\ufb15", "\ufb16", "\ufb17", "\uff41", "\uff42", "\uff43", "\uff44", "\uff45", "\uff46", "\uff47", "\uff48", "\uff49", "\uff4a", "\uff4b", "\uff4c", "\uff4d", "\uff4e", "\uff4f", "\uff50", "\uff51", "\uff52", "\uff53", "\uff54", "\uff55", "\uff56", "\uff57", "\uff58", "\uff59", "\uff5a", "\ud801\udc28", "\ud801\udc29", "\ud801\udc2a", "\ud801\udc2b", "\ud801\udc2c", "\ud801\udc2d", "\ud801\udc2e", "\ud801\udc2f", "\ud801\udc30", "\ud801\udc31", "\ud801\udc32", "\ud801\udc33", "\ud801\udc34", "\ud801\udc35", "\ud801\udc36", "\ud801\udc37", "\ud801\udc38", "\ud801\udc39", "\ud801\udc3a", "\ud801\udc3b", "\ud801\udc3c", "\ud801\udc3d", "\ud801\udc3e", "\ud801\udc3f", "\ud801\udc40", "\ud801\udc41", "\ud801\udc42", "\ud801\udc43", "\ud801\udc44", "\ud801\udc45", "\ud801\udc46", "\ud801\udc47", "\ud801\udc48", "\ud801\udc49", "\ud801\udc4a", "\ud801\udc4b", "\ud801\udc4c", "\ud801\udc4d", "\ud801\udc4e", "\ud801\udc4f", "\ud801\udcd8", "\ud801\udcd9", "\ud801\udcda", "\ud801\udcdb", "\ud801\udcdc", "\ud801\udcdd", "\ud801\udcde", "\ud801\udcdf", "\ud801\udce0", "\ud801\udce1", "\ud801\udce2", "\ud801\udce3", "\ud801\udce4", "\ud801\udce5", "\ud801\udce6", "\ud801\udce7", "\ud801\udce8", "\ud801\udce9", "\ud801\udcea", "\ud801\udceb", "\ud801\udcec", "\ud801\udced", "\ud801\udcee", "\ud801\udcef", "\ud801\udcf0", "\ud801\udcf1", "\ud801\udcf2", "\ud801\udcf3", "\ud801\udcf4", "\ud801\udcf5", "\ud801\udcf6", "\ud801\udcf7", "\ud801\udcf8", "\ud801\udcf9", "\ud801\udcfa", "\ud801\udcfb", "\ud803\udcc0", "\ud803\udcc1", "\ud803\udcc2", "\ud803\udcc3", "\ud803\udcc4", "\ud803\udcc5", "\ud803\udcc6", "\ud803\udcc7", "\ud803\udcc8", "\ud803\udcc9", "\ud803\udcca", "\ud803\udccb", "\ud803\udccc", "\ud803\udccd", "\ud803\udcce", "\ud803\udccf", "\ud803\udcd0", "\ud803\udcd1", "\ud803\udcd2", "\ud803\udcd3", "\ud803\udcd4", "\ud803\udcd5", "\ud803\udcd6", "\ud803\udcd7", "\ud803\udcd8", "\ud803\udcd9", "\ud803\udcda", "\ud803\udcdb", "\ud803\udcdc", "\ud803\udcdd", "\ud803\udcde", "\ud803\udcdf", "\ud803\udce0", "\ud803\udce1", "\ud803\udce2", "\ud803\udce3", "\ud803\udce4", "\ud803\udce5", "\ud803\udce6", "\ud803\udce7", "\ud803\udce8", "\ud803\udce9", "\ud803\udcea", "\ud803\udceb", "\ud803\udcec", "\ud803\udced", "\ud803\udcee", "\ud803\udcef", "\ud803\udcf0", "\ud803\udcf1", "\ud803\udcf2", "\ud806\udcc0", "\ud806\udcc1", "\ud806\udcc2", "\ud806\udcc3", "\ud806\udcc4", "\ud806\udcc5", "\ud806\udcc6", "\ud806\udcc7", "\ud806\udcc8", "\ud806\udcc9", "\ud806\udcca", "\ud806\udccb", "\ud806\udccc", "\ud806\udccd", "\ud806\udcce", "\ud806\udccf", "\ud806\udcd0", "\ud806\udcd1", "\ud806\udcd2", "\ud806\udcd3", "\ud806\udcd4", "\ud806\udcd5", "\ud806\udcd6", "\ud806\udcd7", "\ud806\udcd8", "\ud806\udcd9", "\ud806\udcda", "\ud806\udcdb", "\ud806\udcdc", "\ud806\udcdd", "\ud806\udcde", "\ud806\udcdf", "\ud835\udc1a", "\ud835\udc1b", "\ud835\udc1c", "\ud835\udc1d", "\ud835\udc1e", "\ud835\udc1f", "\ud835\udc20", "\ud835\udc21", "\ud835\udc22", "\ud835\udc23", "\ud835\udc24", "\ud835\udc25", "\ud835\udc26", "\ud835\udc27", "\ud835\udc28", "\ud835\udc29", "\ud835\udc2a", "\ud835\udc2b", "\ud835\udc2c", "\ud835\udc2d", "\ud835\udc2e", "\ud835\udc2f", "\ud835\udc30", "\ud835\udc31", "\ud835\udc32", "\ud835\udc33", "\ud835\udc4e", "\ud835\udc4f", "\ud835\udc50", "\ud835\udc51", "\ud835\udc52", "\ud835\udc53", "\ud835\udc54", "\ud835\udc56", "\ud835\udc57", "\ud835\udc58", "\ud835\udc59", "\ud835\udc5a", "\ud835\udc5b", "\ud835\udc5c", "\ud835\udc5d", "\ud835\udc5e", "\ud835\udc5f", "\ud835\udc60", "\ud835\udc61", "\ud835\udc62", "\ud835\udc63", "\ud835\udc64", "\ud835\udc65", "\ud835\udc66", "\ud835\udc67", "\ud835\udc82", "\ud835\udc83", "\ud835\udc84", "\ud835\udc85", "\ud835\udc86", "\ud835\udc87", "\ud835\udc88", "\ud835\udc89", "\ud835\udc8a", "\ud835\udc8b", "\ud835\udc8c", "\ud835\udc8d", "\ud835\udc8e", "\ud835\udc8f", "\ud835\udc90", "\ud835\udc91", "\ud835\udc92", "\ud835\udc93", "\ud835\udc94", "\ud835\udc95", "\ud835\udc96", "\ud835\udc97", "\ud835\udc98", "\ud835\udc99", "\ud835\udc9a", "\ud835\udc9b", "\ud835\udcb6", "\ud835\udcb7", "\ud835\udcb8", "\ud835\udcb9", "\ud835\udcbb", "\ud835\udcbd", "\ud835\udcbe", "\ud835\udcbf", "\ud835\udcc0", "\ud835\udcc1", "\ud835\udcc2", "\ud835\udcc3", "\ud835\udcc5", "\ud835\udcc6", "\ud835\udcc7", "\ud835\udcc8", "\ud835\udcc9", "\ud835\udcca", "\ud835\udccb", "\ud835\udccc", "\ud835\udccd", "\ud835\udcce", "\ud835\udccf", "\ud835\udcea", "\ud835\udceb", "\ud835\udcec", "\ud835\udced", "\ud835\udcee", "\ud835\udcef", "\ud835\udcf0", "\ud835\udcf1", "\ud835\udcf2", "\ud835\udcf3", "\ud835\udcf4", "\ud835\udcf5", "\ud835\udcf6", "\ud835\udcf7", "\ud835\udcf8", "\ud835\udcf9", "\ud835\udcfa", "\ud835\udcfb", "\ud835\udcfc", "\ud835\udcfd", "\ud835\udcfe", "\ud835\udcff", "\ud835\udd00", "\ud835\udd01", "\ud835\udd02", "\ud835\udd03", "\ud835\udd1e", "\ud835\udd1f", "\ud835\udd20", "\ud835\udd21", "\ud835\udd22", "\ud835\udd23", "\ud835\udd24", "\ud835\udd25", "\ud835\udd26", "\ud835\udd27", "\ud835\udd28", "\ud835\udd29", "\ud835\udd2a", "\ud835\udd2b", "\ud835\udd2c", "\ud835\udd2d", "\ud835\udd2e", "\ud835\udd2f", "\ud835\udd30", "\ud835\udd31", "\ud835\udd32", "\ud835\udd33", "\ud835\udd34", "\ud835\udd35", "\ud835\udd36", "\ud835\udd37", "\ud835\udd52", "\ud835\udd53", "\ud835\udd54", "\ud835\udd55", "\ud835\udd56", "\ud835\udd57", "\ud835\udd58", "\ud835\udd59", "\ud835\udd5a", "\ud835\udd5b", "\ud835\udd5c", "\ud835\udd5d", "\ud835\udd5e", "\ud835\udd5f", "\ud835\udd60", "\ud835\udd61", "\ud835\udd62", "\ud835\udd63", "\ud835\udd64", "\ud835\udd65", "\ud835\udd66", "\ud835\udd67", "\ud835\udd68", "\ud835\udd69", "\ud835\udd6a", "\ud835\udd6b", "\ud835\udd86", "\ud835\udd87", "\ud835\udd88", "\ud835\udd89", "\ud835\udd8a", "\ud835\udd8b", "\ud835\udd8c", "\ud835\udd8d", "\ud835\udd8e", "\ud835\udd8f", "\ud835\udd90", "\ud835\udd91", "\ud835\udd92", "\ud835\udd93", "\ud835\udd94", "\ud835\udd95", "\ud835\udd96", "\ud835\udd97", "\ud835\udd98", "\ud835\udd99", "\ud835\udd9a", "\ud835\udd9b", "\ud835\udd9c", "\ud835\udd9d", "\ud835\udd9e", "\ud835\udd9f", "\ud835\uddba", "\ud835\uddbb", "\ud835\uddbc", "\ud835\uddbd", "\ud835\uddbe", "\ud835\uddbf", "\ud835\uddc0", "\ud835\uddc1", "\ud835\uddc2", "\ud835\uddc3", "\ud835\uddc4", "\ud835\uddc5", "\ud835\uddc6", "\ud835\uddc7", "\ud835\uddc8", "\ud835\uddc9", "\ud835\uddca", "\ud835\uddcb", "\ud835\uddcc", "\ud835\uddcd", "\ud835\uddce", "\ud835\uddcf", "\ud835\uddd0", "\ud835\uddd1", "\ud835\uddd2", "\ud835\uddd3", "\ud835\uddee", "\ud835\uddef", "\ud835\uddf0", "\ud835\uddf1", "\ud835\uddf2", "\ud835\uddf3", "\ud835\uddf4", "\ud835\uddf5", "\ud835\uddf6", "\ud835\uddf7", "\ud835\uddf8", "\ud835\uddf9", "\ud835\uddfa", "\ud835\uddfb", "\ud835\uddfc", "\ud835\uddfd", "\ud835\uddfe", "\ud835\uddff", "\ud835\ude00", "\ud835\ude01", "\ud835\ude02", "\ud835\ude03", "\ud835\ude04", "\ud835\ude05", "\ud835\ude06", "\ud835\ude07", "\ud835\ude22", "\ud835\ude23", "\ud835\ude24", "\ud835\ude25", "\ud835\ude26", "\ud835\ude27", "\ud835\ude28", "\ud835\ude29", "\ud835\ude2a", "\ud835\ude2b", "\ud835\ude2c", "\ud835\ude2d", "\ud835\ude2e", "\ud835\ude2f", "\ud835\ude30", "\ud835\ude31", "\ud835\ude32", "\ud835\ude33", "\ud835\ude34", "\ud835\ude35", "\ud835\ude36", "\ud835\ude37", "\ud835\ude38", "\ud835\ude39", "\ud835\ude3a", "\ud835\ude3b", "\ud835\ude56", "\ud835\ude57", "\ud835\ude58", "\ud835\ude59", "\ud835\ude5a", "\ud835\ude5b", "\ud835\ude5c", "\ud835\ude5d", "\ud835\ude5e", "\ud835\ude5f", "\ud835\ude60", "\ud835\ude61", "\ud835\ude62", "\ud835\ude63", "\ud835\ude64", "\ud835\ude65", "\ud835\ude66", "\ud835\ude67", "\ud835\ude68", "\ud835\ude69", "\ud835\ude6a", "\ud835\ude6b", "\ud835\ude6c", "\ud835\ude6d", "\ud835\ude6e", "\ud835\ude6f", "\ud835\ude8a", "\ud835\ude8b", "\ud835\ude8c", "\ud835\ude8d", "\ud835\ude8e", "\ud835\ude8f", "\ud835\ude90", "\ud835\ude91", "\ud835\ude92", "\ud835\ude93", "\ud835\ude94", "\ud835\ude95", "\ud835\ude96", "\ud835\ude97", "\ud835\ude98", "\ud835\ude99", "\ud835\ude9a", "\ud835\ude9b", "\ud835\ude9c", "\ud835\ude9d", "\ud835\ude9e", "\ud835\ude9f", "\ud835\udea0", "\ud835\udea1", "\ud835\udea2", "\ud835\udea3", "\ud835\udea4", "\ud835\udea5", "\ud835\udec2", "\ud835\udec3", "\ud835\udec4", "\ud835\udec5", "\ud835\udec6", "\ud835\udec7", "\ud835\udec8", "\ud835\udec9", "\ud835\udeca", "\ud835\udecb", "\ud835\udecc", "\ud835\udecd", "\ud835\udece", "\ud835\udecf", "\ud835\uded0", "\ud835\uded1", "\ud835\uded2", "\ud835\uded3", "\ud835\uded4", "\ud835\uded5", "\ud835\uded6", "\ud835\uded7", "\ud835\uded8", "\ud835\uded9", "\ud835\udeda", "\ud835\udedc", "\ud835\udedd", "\ud835\udede", "\ud835\udedf", "\ud835\udee0", "\ud835\udee1", "\ud835\udefc", "\ud835\udefd", "\ud835\udefe", "\ud835\udeff", "\ud835\udf00", "\ud835\udf01", "\ud835\udf02", "\ud835\udf03", "\ud835\udf04", "\ud835\udf05", "\ud835\udf06", "\ud835\udf07", "\ud835\udf08", "\ud835\udf09", "\ud835\udf0a", "\ud835\udf0b", "\ud835\udf0c", "\ud835\udf0d", "\ud835\udf0e", "\ud835\udf0f", "\ud835\udf10", "\ud835\udf11", "\ud835\udf12", "\ud835\udf13", "\ud835\udf14", "\ud835\udf16", "\ud835\udf17", "\ud835\udf18", "\ud835\udf19", "\ud835\udf1a", "\ud835\udf1b", "\ud835\udf36", "\ud835\udf37", "\ud835\udf38", "\ud835\udf39", "\ud835\udf3a", "\ud835\udf3b", "\ud835\udf3c", "\ud835\udf3d", "\ud835\udf3e", "\ud835\udf3f", "\ud835\udf40", "\ud835\udf41", "\ud835\udf42", "\ud835\udf43", "\ud835\udf44", "\ud835\udf45", "\ud835\udf46", "\ud835\udf47", "\ud835\udf48", "\ud835\udf49", "\ud835\udf4a", "\ud835\udf4b", "\ud835\udf4c", "\ud835\udf4d", "\ud835\udf4e", "\ud835\udf50", "\ud835\udf51", "\ud835\udf52", "\ud835\udf53", "\ud835\udf54", "\ud835\udf55", "\ud835\udf70", "\ud835\udf71", "\ud835\udf72", "\ud835\udf73", "\ud835\udf74", "\ud835\udf75", "\ud835\udf76", "\ud835\udf77", "\ud835\udf78", "\ud835\udf79", "\ud835\udf7a", "\ud835\udf7b", "\ud835\udf7c", "\ud835\udf7d", "\ud835\udf7e", "\ud835\udf7f", "\ud835\udf80", "\ud835\udf81", "\ud835\udf82", "\ud835\udf83", "\ud835\udf84", "\ud835\udf85", "\ud835\udf86", "\ud835\udf87", "\ud835\udf88", "\ud835\udf8a", "\ud835\udf8b", "\ud835\udf8c", "\ud835\udf8d", "\ud835\udf8e", "\ud835\udf8f", "\ud835\udfaa", "\ud835\udfab", "\ud835\udfac", "\ud835\udfad", "\ud835\udfae", "\ud835\udfaf", "\ud835\udfb0", "\ud835\udfb1", "\ud835\udfb2", "\ud835\udfb3", "\ud835\udfb4", "\ud835\udfb5", "\ud835\udfb6", "\ud835\udfb7", "\ud835\udfb8", "\ud835\udfb9", "\ud835\udfba", "\ud835\udfbb", "\ud835\udfbc", "\ud835\udfbd", "\ud835\udfbe", "\ud835\udfbf", "\ud835\udfc0", "\ud835\udfc1", "\ud835\udfc2", "\ud835\udfc4", "\ud835\udfc5", "\ud835\udfc6", "\ud835\udfc7", "\ud835\udfc8", "\ud835\udfc9", "\ud835\udfcb", "\ud83a\udd22", "\ud83a\udd23", "\ud83a\udd24", "\ud83a\udd25", "\ud83a\udd26", "\ud83a\udd27", "\ud83a\udd28", "\ud83a\udd29", "\ud83a\udd2a", "\ud83a\udd2b", "\ud83a\udd2c", "\ud83a\udd2d", "\ud83a\udd2e", "\ud83a\udd2f", "\ud83a\udd30", "\ud83a\udd31", "\ud83a\udd32", "\ud83a\udd33", "\ud83a\udd34", "\ud83a\udd35", "\ud83a\udd36", "\ud83a\udd37", "\ud83a\udd38", "\ud83a\udd39", "\ud83a\udd3a", "\ud83a\udd3b", "\ud83a\udd3c", "\ud83a\udd3d", "\ud83a\udd3e", "\ud83a\udd3f", "\ud83a\udd40", "\ud83a\udd41", "\ud83a\udd42", "\ud83a\udd43", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "\u00c0", "\u00c1", "\u00c2", "\u00c3", "\u00c4", "\u00c5", "\u00c6", "\u00c7", "\u00c8", "\u00c9", "\u00ca", "\u00cb", "\u00cc", "\u00cd", "\u00ce", "\u00cf", "\u00d0", "\u00d1", "\u00d2", "\u00d3", "\u00d4", "\u00d5", "\u00d6", "\u00d8", "\u00d9", "\u00da", "\u00db", "\u00dc", "\u00dd", "\u00de", "\u0100", "\u0102", "\u0104", "\u0106", "\u0108", "\u010a", "\u010c", "\u010e", "\u0110", "\u0112", "\u0114", "\u0116", "\u0118", "\u011a", "\u011c", "\u011e", "\u0120", "\u0122", "\u0124", "\u0126", "\u0128", "\u012a", "\u012c", "\u012e", "\u0130", "\u0132", "\u0134", "\u0136", "\u0139", "\u013b", "\u013d", "\u013f", "\u0141", "\u0143", "\u0145", "\u0147", "\u014a", "\u014c", "\u014e", "\u0150", "\u0152", "\u0154", "\u0156", "\u0158", "\u015a", "\u015c", "\u015e", "\u0160", "\u0162", "\u0164", "\u0166", "\u0168", "\u016a", "\u016c", "\u016e", "\u0170", "\u0172", "\u0174", "\u0176", "\u0178", "\u0179", "\u017b", "\u017d", "\u0181", "\u0182", "\u0184", "\u0186", "\u0187", "\u0189", "\u018a", "\u018b", "\u018e", "\u018f", "\u0190", "\u0191", "\u0193", "\u0194", "\u0196", "\u0197", "\u0198", "\u019c", "\u019d", "\u019f", "\u01a0", "\u01a2", "\u01a4", "\u01a6", "\u01a7", "\u01a9", "\u01ac", "\u01ae", "\u01af", "\u01b1", "\u01b2", "\u01b3", "\u01b5", "\u01b7", "\u01b8", "\u01bc", "\u01c4", "\u01c7", "\u01ca", "\u01cd", "\u01cf", "\u01d1", "\u01d3", "\u01d5", "\u01d7", "\u01d9", "\u01db", "\u01de", "\u01e0", "\u01e2", "\u01e4", "\u01e6", "\u01e8", "\u01ea", "\u01ec", "\u01ee", "\u01f1", "\u01f4", "\u01f6", "\u01f7", "\u01f8", "\u01fa", "\u01fc", "\u01fe", "\u0200", "\u0202", "\u0204", "\u0206", "\u0208", "\u020a", "\u020c", "\u020e", "\u0210", "\u0212", "\u0214", "\u0216", "\u0218", "\u021a", "\u021c", "\u021e", "\u0220", "\u0222", "\u0224", "\u0226", "\u0228", "\u022a", "\u022c", "\u022e", "\u0230", "\u0232", "\u023a", "\u023b", "\u023d", "\u023e", "\u0241", "\u0243", "\u0244", "\u0245", "\u0246", "\u0248", "\u024a", "\u024c", "\u024e", "\u0370", "\u0372", "\u0376", "\u037f", "\u0386", "\u0388", "\u0389", "\u038a", "\u038c", "\u038e", "\u038f", "\u0391", "\u0392", "\u0393", "\u0394", "\u0395", "\u0396", "\u0397", "\u0398", "\u0399", "\u039a", "\u039b", "\u039c", "\u039d", "\u039e", "\u039f", "\u03a0", "\u03a1", "\u03a3", "\u03a4", "\u03a5", "\u03a6", "\u03a7", "\u03a8", "\u03a9", "\u03aa", "\u03ab", "\u03cf", "\u03d2", "\u03d3", "\u03d4", "\u03d8", "\u03da", "\u03dc", "\u03de", "\u03e0", "\u03e2", "\u03e4", "\u03e6", "\u03e8", "\u03ea", "\u03ec", "\u03ee", "\u03f4", "\u03f7", "\u03f9", "\u03fa", "\u03fd", "\u03fe", "\u03ff", "\u0400", "\u0401", "\u0402", "\u0403", "\u0404", "\u0405", "\u0406", "\u0407", "\u0408", "\u0409", "\u040a", "\u040b", "\u040c", "\u040d", "\u040e", "\u040f", "\u0410", "\u0411", "\u0412", "\u0413", "\u0414", "\u0415", "\u0416", "\u0417", "\u0418", "\u0419", "\u041a", "\u041b", "\u041c", "\u041d", "\u041e", "\u041f", "\u0420", "\u0421", "\u0422", "\u0423", "\u0424", "\u0425", "\u0426", "\u0427", "\u0428", "\u0429", "\u042a", "\u042b", "\u042c", "\u042d", "\u042e", "\u042f", "\u0460", "\u0462", "\u0464", "\u0466", "\u0468", "\u046a", "\u046c", "\u046e", "\u0470", "\u0472", "\u0474", "\u0476", "\u0478", "\u047a", "\u047c", "\u047e", "\u0480", "\u048a", "\u048c", "\u048e", "\u0490", "\u0492", "\u0494", "\u0496", "\u0498", "\u049a", "\u049c", "\u049e", "\u04a0", "\u04a2", "\u04a4", "\u04a6", "\u04a8", "\u04aa", "\u04ac", "\u04ae", "\u04b0", "\u04b2", "\u04b4", "\u04b6", "\u04b8", "\u04ba", "\u04bc", "\u04be", "\u04c0", "\u04c1", "\u04c3", "\u04c5", "\u04c7", "\u04c9", "\u04cb", "\u04cd", "\u04d0", "\u04d2", "\u04d4", "\u04d6", "\u04d8", "\u04da", "\u04dc", "\u04de", "\u04e0", "\u04e2", "\u04e4", "\u04e6", "\u04e8", "\u04ea", "\u04ec", "\u04ee", "\u04f0", "\u04f2", "\u04f4", "\u04f6", "\u04f8", "\u04fa", "\u04fc", "\u04fe", "\u0500", "\u0502", "\u0504", "\u0506", "\u0508", "\u050a", "\u050c", "\u050e", "\u0510", "\u0512", "\u0514", "\u0516", "\u0518", "\u051a", "\u051c", "\u051e", "\u0520", "\u0522", "\u0524", "\u0526", "\u0528", "\u052a", "\u052c", "\u052e", "\u0531", "\u0532", "\u0533", "\u0534", "\u0535", "\u0536", "\u0537", "\u0538", "\u0539", "\u053a", "\u053b", "\u053c", "\u053d", "\u053e", "\u053f", "\u0540", "\u0541", "\u0542", "\u0543", "\u0544", "\u0545", "\u0546", "\u0547", "\u0548", "\u0549", "\u054a", "\u054b", "\u054c", "\u054d", "\u054e", "\u054f", "\u0550", "\u0551", "\u0552", "\u0553", "\u0554", "\u0555", "\u0556", "\u10a0", "\u10a1", "\u10a2", "\u10a3", "\u10a4", "\u10a5", "\u10a6", "\u10a7", "\u10a8", "\u10a9", "\u10aa", "\u10ab", "\u10ac", "\u10ad", "\u10ae", "\u10af", "\u10b0", "\u10b1", "\u10b2", "\u10b3", "\u10b4", "\u10b5", "\u10b6", "\u10b7", "\u10b8", "\u10b9", "\u10ba", "\u10bb", "\u10bc", "\u10bd", "\u10be", "\u10bf", "\u10c0", "\u10c1", "\u10c2", "\u10c3", "\u10c4", "\u10c5", "\u10c7", "\u10cd", "\u13a0", "\u13a1", "\u13a2", "\u13a3", "\u13a4", "\u13a5", "\u13a6", "\u13a7", "\u13a8", "\u13a9", "\u13aa", "\u13ab", "\u13ac", "\u13ad", "\u13ae", "\u13af", "\u13b0", "\u13b1", "\u13b2", "\u13b3", "\u13b4", "\u13b5", "\u13b6", "\u13b7", "\u13b8", "\u13b9", "\u13ba", "\u13bb", "\u13bc", "\u13bd", "\u13be", "\u13bf", "\u13c0", "\u13c1", "\u13c2", "\u13c3", "\u13c4", "\u13c5", "\u13c6", "\u13c7", "\u13c8", "\u13c9", "\u13ca", "\u13cb", "\u13cc", "\u13cd", "\u13ce", "\u13cf", "\u13d0", "\u13d1", "\u13d2", "\u13d3", "\u13d4", "\u13d5", "\u13d6", "\u13d7", "\u13d8", "\u13d9", "\u13da", "\u13db", "\u13dc", "\u13dd", "\u13de", "\u13df", "\u13e0", "\u13e1", "\u13e2", "\u13e3", "\u13e4", "\u13e5", "\u13e6", "\u13e7", "\u13e8", "\u13e9", "\u13ea", "\u13eb", "\u13ec", "\u13ed", "\u13ee", "\u13ef", "\u13f0", "\u13f1", "\u13f2", "\u13f3", "\u13f4", "\u13f5", "\u1e00", "\u1e02", "\u1e04", "\u1e06", "\u1e08", "\u1e0a", "\u1e0c", "\u1e0e", "\u1e10", "\u1e12", "\u1e14", "\u1e16", "\u1e18", "\u1e1a", "\u1e1c", "\u1e1e", "\u1e20", "\u1e22", "\u1e24", "\u1e26", "\u1e28", "\u1e2a", "\u1e2c", "\u1e2e", "\u1e30", "\u1e32", "\u1e34", "\u1e36", "\u1e38", "\u1e3a", "\u1e3c", "\u1e3e", "\u1e40", "\u1e42", "\u1e44", "\u1e46", "\u1e48", "\u1e4a", "\u1e4c", "\u1e4e", "\u1e50", "\u1e52", "\u1e54", "\u1e56", "\u1e58", "\u1e5a", "\u1e5c", "\u1e5e", "\u1e60", "\u1e62", "\u1e64", "\u1e66", "\u1e68", "\u1e6a", "\u1e6c", "\u1e6e", "\u1e70", "\u1e72", "\u1e74", "\u1e76", "\u1e78", "\u1e7a", "\u1e7c", "\u1e7e", "\u1e80", "\u1e82", "\u1e84", "\u1e86", "\u1e88", "\u1e8a", "\u1e8c", "\u1e8e", "\u1e90", "\u1e92", "\u1e94", "\u1e9e", "\u1ea0", "\u1ea2", "\u1ea4", "\u1ea6", "\u1ea8", "\u1eaa", "\u1eac", "\u1eae", "\u1eb0", "\u1eb2", "\u1eb4", "\u1eb6", "\u1eb8", "\u1eba", "\u1ebc", "\u1ebe", "\u1ec0", "\u1ec2", "\u1ec4", "\u1ec6", "\u1ec8", "\u1eca", "\u1ecc", "\u1ece", "\u1ed0", "\u1ed2", "\u1ed4", "\u1ed6", "\u1ed8", "\u1eda", "\u1edc", "\u1ede", "\u1ee0", "\u1ee2", "\u1ee4", "\u1ee6", "\u1ee8", "\u1eea", "\u1eec", "\u1eee", "\u1ef0", "\u1ef2", "\u1ef4", "\u1ef6", "\u1ef8", "\u1efa", "\u1efc", "\u1efe", "\u1f08", "\u1f09", "\u1f0a", "\u1f0b", "\u1f0c", "\u1f0d", "\u1f0e", "\u1f0f", "\u1f18", "\u1f19", "\u1f1a", "\u1f1b", "\u1f1c", "\u1f1d", "\u1f28", "\u1f29", "\u1f2a", "\u1f2b", "\u1f2c", "\u1f2d", "\u1f2e", "\u1f2f", "\u1f38", "\u1f39", "\u1f3a", "\u1f3b", "\u1f3c", "\u1f3d", "\u1f3e", "\u1f3f", "\u1f48", "\u1f49", "\u1f4a", "\u1f4b", "\u1f4c", "\u1f4d", "\u1f59", "\u1f5b", "\u1f5d", "\u1f5f", "\u1f68", "\u1f69", "\u1f6a", "\u1f6b", "\u1f6c", "\u1f6d", "\u1f6e", "\u1f6f", "\u1fb8", "\u1fb9", "\u1fba", "\u1fbb", "\u1fc8", "\u1fc9", "\u1fca", "\u1fcb", "\u1fd8", "\u1fd9", "\u1fda", "\u1fdb", "\u1fe8", "\u1fe9", "\u1fea", "\u1feb", "\u1fec", "\u1ff8", "\u1ff9", "\u1ffa", "\u1ffb", "\u2102", "\u2107", "\u210b", "\u210c", "\u210d", "\u2110", "\u2111", "\u2112", "\u2115", "\u2119", "\u211a", "\u211b", "\u211c", "\u211d", "\u2124", "\u2126", "\u2128", "\u212a", "\u212b", "\u212c", "\u212d", "\u2130", "\u2131", "\u2132", "\u2133", "\u213e", "\u213f", "\u2145", "\u2183", "\u2c00", "\u2c01", "\u2c02", "\u2c03", "\u2c04", "\u2c05", "\u2c06", "\u2c07", "\u2c08", "\u2c09", "\u2c0a", "\u2c0b", "\u2c0c", "\u2c0d", "\u2c0e", "\u2c0f", "\u2c10", "\u2c11", "\u2c12", "\u2c13", "\u2c14", "\u2c15", "\u2c16", "\u2c17", "\u2c18", "\u2c19", "\u2c1a", "\u2c1b", "\u2c1c", "\u2c1d", "\u2c1e", "\u2c1f", "\u2c20", "\u2c21", "\u2c22", "\u2c23", "\u2c24", "\u2c25", "\u2c26", "\u2c27", "\u2c28", "\u2c29", "\u2c2a", "\u2c2b", "\u2c2c", "\u2c2d", "\u2c2e", "\u2c60", "\u2c62", "\u2c63", "\u2c64", "\u2c67", "\u2c69", "\u2c6b", "\u2c6d", "\u2c6e", "\u2c6f", "\u2c70", "\u2c72", "\u2c75", "\u2c7e", "\u2c7f", "\u2c80", "\u2c82", "\u2c84", "\u2c86", "\u2c88", "\u2c8a", "\u2c8c", "\u2c8e", "\u2c90", "\u2c92", "\u2c94", "\u2c96", "\u2c98", "\u2c9a", "\u2c9c", "\u2c9e", "\u2ca0", "\u2ca2", "\u2ca4", "\u2ca6", "\u2ca8", "\u2caa", "\u2cac", "\u2cae", "\u2cb0", "\u2cb2", "\u2cb4", "\u2cb6", "\u2cb8", "\u2cba", "\u2cbc", "\u2cbe", "\u2cc0", "\u2cc2", "\u2cc4", "\u2cc6", "\u2cc8", "\u2cca", "\u2ccc", "\u2cce", "\u2cd0", "\u2cd2", "\u2cd4", "\u2cd6", "\u2cd8", "\u2cda", "\u2cdc", "\u2cde", "\u2ce0", "\u2ce2", "\u2ceb", "\u2ced", "\u2cf2", "\ua640", "\ua642", "\ua644", "\ua646", "\ua648", "\ua64a", "\ua64c", "\ua64e", "\ua650", "\ua652", "\ua654", "\ua656", "\ua658", "\ua65a", "\ua65c", "\ua65e", "\ua660", "\ua662", "\ua664", "\ua666", "\ua668", "\ua66a", "\ua66c", "\ua680", "\ua682", "\ua684", "\ua686", "\ua688", "\ua68a", "\ua68c", "\ua68e", "\ua690", "\ua692", "\ua694", "\ua696", "\ua698", "\ua69a", "\ua722", "\ua724", "\ua726", "\ua728", "\ua72a", "\ua72c", "\ua72e", "\ua732", "\ua734", "\ua736", "\ua738", "\ua73a", "\ua73c", "\ua73e", "\ua740", "\ua742", "\ua744", "\ua746", "\ua748", "\ua74a", "\ua74c", "\ua74e", "\ua750", "\ua752", "\ua754", "\ua756", "\ua758", "\ua75a", "\ua75c", "\ua75e", "\ua760", "\ua762", "\ua764", "\ua766", "\ua768", "\ua76a", "\ua76c", "\ua76e", "\ua779", "\ua77b", "\ua77d", "\ua77e", "\ua780", "\ua782", "\ua784", "\ua786", "\ua78b", "\ua78d", "\ua790", "\ua792", "\ua796", "\ua798", "\ua79a", "\ua79c", "\ua79e", "\ua7a0", "\ua7a2", "\ua7a4", "\ua7a6", "\ua7a8", "\ua7aa", "\ua7ab", "\ua7ac", "\ua7ad", "\ua7ae", "\ua7b0", "\ua7b1", "\ua7b2", "\ua7b3", "\ua7b4", "\ua7b6", "\uff21", "\uff22", "\uff23", "\uff24", "\uff25", "\uff26", "\uff27", "\uff28", "\uff29", "\uff2a", "\uff2b", "\uff2c", "\uff2d", "\uff2e", "\uff2f", "\uff30", "\uff31", "\uff32", "\uff33", "\uff34", "\uff35", "\uff36", "\uff37", "\uff38", "\uff39", "\uff3a", "\ud801\udc00", "\ud801\udc01", "\ud801\udc02", "\ud801\udc03", "\ud801\udc04", "\ud801\udc05", "\ud801\udc06", "\ud801\udc07", "\ud801\udc08", "\ud801\udc09", "\ud801\udc0a", "\ud801\udc0b", "\ud801\udc0c", "\ud801\udc0d", "\ud801\udc0e", "\ud801\udc0f", "\ud801\udc10", "\ud801\udc11", "\ud801\udc12", "\ud801\udc13", "\ud801\udc14", "\ud801\udc15", "\ud801\udc16", "\ud801\udc17", "\ud801\udc18", "\ud801\udc19", "\ud801\udc1a", "\ud801\udc1b", "\ud801\udc1c", "\ud801\udc1d", "\ud801\udc1e", "\ud801\udc1f", "\ud801\udc20", "\ud801\udc21", "\ud801\udc22", "\ud801\udc23", "\ud801\udc24", "\ud801\udc25", "\ud801\udc26", "\ud801\udc27", "\ud801\udcb0", "\ud801\udcb1", "\ud801\udcb2", "\ud801\udcb3", "\ud801\udcb4", "\ud801\udcb5", "\ud801\udcb6", "\ud801\udcb7", "\ud801\udcb8", "\ud801\udcb9", "\ud801\udcba", "\ud801\udcbb", "\ud801\udcbc", "\ud801\udcbd", "\ud801\udcbe", "\ud801\udcbf", "\ud801\udcc0", "\ud801\udcc1", "\ud801\udcc2", "\ud801\udcc3", "\ud801\udcc4", "\ud801\udcc5", "\ud801\udcc6", "\ud801\udcc7", "\ud801\udcc8", "\ud801\udcc9", "\ud801\udcca", "\ud801\udccb", "\ud801\udccc", "\ud801\udccd", "\ud801\udcce", "\ud801\udccf", "\ud801\udcd0", "\ud801\udcd1", "\ud801\udcd2", "\ud801\udcd3", "\ud803\udc80", "\ud803\udc81", "\ud803\udc82", "\ud803\udc83", "\ud803\udc84", "\ud803\udc85", "\ud803\udc86", "\ud803\udc87", "\ud803\udc88", "\ud803\udc89", "\ud803\udc8a", "\ud803\udc8b", "\ud803\udc8c", "\ud803\udc8d", "\ud803\udc8e", "\ud803\udc8f", "\ud803\udc90", "\ud803\udc91", "\ud803\udc92", "\ud803\udc93", "\ud803\udc94", "\ud803\udc95", "\ud803\udc96", "\ud803\udc97", "\ud803\udc98", "\ud803\udc99", "\ud803\udc9a", "\ud803\udc9b", "\ud803\udc9c", "\ud803\udc9d", "\ud803\udc9e", "\ud803\udc9f", "\ud803\udca0", "\ud803\udca1", "\ud803\udca2", "\ud803\udca3", "\ud803\udca4", "\ud803\udca5", "\ud803\udca6", "\ud803\udca7", "\ud803\udca8", "\ud803\udca9", "\ud803\udcaa", "\ud803\udcab", "\ud803\udcac", "\ud803\udcad", "\ud803\udcae", "\ud803\udcaf", "\ud803\udcb0", "\ud803\udcb1", "\ud803\udcb2", "\ud806\udca0", "\ud806\udca1", "\ud806\udca2", "\ud806\udca3", "\ud806\udca4", "\ud806\udca5", "\ud806\udca6", "\ud806\udca7", "\ud806\udca8", "\ud806\udca9", "\ud806\udcaa", "\ud806\udcab", "\ud806\udcac", "\ud806\udcad", "\ud806\udcae", "\ud806\udcaf", "\ud806\udcb0", "\ud806\udcb1", "\ud806\udcb2", "\ud806\udcb3", "\ud806\udcb4", "\ud806\udcb5", "\ud806\udcb6", "\ud806\udcb7", "\ud806\udcb8", "\ud806\udcb9", "\ud806\udcba", "\ud806\udcbb", "\ud806\udcbc", "\ud806\udcbd", "\ud806\udcbe", "\ud806\udcbf", "\ud835\udc00", "\ud835\udc01", "\ud835\udc02", "\ud835\udc03", "\ud835\udc04", "\ud835\udc05", "\ud835\udc06", "\ud835\udc07", "\ud835\udc08", "\ud835\udc09", "\ud835\udc0a", "\ud835\udc0b", "\ud835\udc0c", "\ud835\udc0d", "\ud835\udc0e", "\ud835\udc0f", "\ud835\udc10", "\ud835\udc11", "\ud835\udc12", "\ud835\udc13", "\ud835\udc14", "\ud835\udc15", "\ud835\udc16", "\ud835\udc17", "\ud835\udc18", "\ud835\udc19", "\ud835\udc34", "\ud835\udc35", "\ud835\udc36", "\ud835\udc37", "\ud835\udc38", "\ud835\udc39", "\ud835\udc3a", "\ud835\udc3b", "\ud835\udc3c", "\ud835\udc3d", "\ud835\udc3e", "\ud835\udc3f", "\ud835\udc40", "\ud835\udc41", "\ud835\udc42", "\ud835\udc43", "\ud835\udc44", "\ud835\udc45", "\ud835\udc46", "\ud835\udc47", "\ud835\udc48", "\ud835\udc49", "\ud835\udc4a", "\ud835\udc4b", "\ud835\udc4c", "\ud835\udc4d", "\ud835\udc68", "\ud835\udc69", "\ud835\udc6a", "\ud835\udc6b", "\ud835\udc6c", "\ud835\udc6d", "\ud835\udc6e", "\ud835\udc6f", "\ud835\udc70", "\ud835\udc71", "\ud835\udc72", "\ud835\udc73", "\ud835\udc74", "\ud835\udc75", "\ud835\udc76", "\ud835\udc77", "\ud835\udc78", "\ud835\udc79", "\ud835\udc7a", "\ud835\udc7b", "\ud835\udc7c", "\ud835\udc7d", "\ud835\udc7e", "\ud835\udc7f", "\ud835\udc80", "\ud835\udc81", "\ud835\udc9c", "\ud835\udc9e", "\ud835\udc9f", "\ud835\udca2", "\ud835\udca5", "\ud835\udca6", "\ud835\udca9", "\ud835\udcaa", "\ud835\udcab", "\ud835\udcac", "\ud835\udcae", "\ud835\udcaf", "\ud835\udcb0", "\ud835\udcb1", "\ud835\udcb2", "\ud835\udcb3", "\ud835\udcb4", "\ud835\udcb5", "\ud835\udcd0", "\ud835\udcd1", "\ud835\udcd2", "\ud835\udcd3", "\ud835\udcd4", "\ud835\udcd5", "\ud835\udcd6", "\ud835\udcd7", "\ud835\udcd8", "\ud835\udcd9", "\ud835\udcda", "\ud835\udcdb", "\ud835\udcdc", "\ud835\udcdd", "\ud835\udcde", "\ud835\udcdf", "\ud835\udce0", "\ud835\udce1", "\ud835\udce2", "\ud835\udce3", "\ud835\udce4", "\ud835\udce5", "\ud835\udce6", "\ud835\udce7", "\ud835\udce8", "\ud835\udce9", "\ud835\udd04", "\ud835\udd05", "\ud835\udd07", "\ud835\udd08", "\ud835\udd09", "\ud835\udd0a", "\ud835\udd0d", "\ud835\udd0e", "\ud835\udd0f", "\ud835\udd10", "\ud835\udd11", "\ud835\udd12", "\ud835\udd13", "\ud835\udd14", "\ud835\udd16", "\ud835\udd17", "\ud835\udd18", "\ud835\udd19", "\ud835\udd1a", "\ud835\udd1b", "\ud835\udd1c", "\ud835\udd38", "\ud835\udd39", "\ud835\udd3b", "\ud835\udd3c", "\ud835\udd3d", "\ud835\udd3e", "\ud835\udd40", "\ud835\udd41", "\ud835\udd42", "\ud835\udd43", "\ud835\udd44", "\ud835\udd46", "\ud835\udd4a", "\ud835\udd4b", "\ud835\udd4c", "\ud835\udd4d", "\ud835\udd4e", "\ud835\udd4f", "\ud835\udd50", "\ud835\udd6c", "\ud835\udd6d", "\ud835\udd6e", "\ud835\udd6f", "\ud835\udd70", "\ud835\udd71", "\ud835\udd72", "\ud835\udd73", "\ud835\udd74", "\ud835\udd75", "\ud835\udd76", "\ud835\udd77", "\ud835\udd78", "\ud835\udd79", "\ud835\udd7a", "\ud835\udd7b", "\ud835\udd7c", "\ud835\udd7d", "\ud835\udd7e", "\ud835\udd7f", "\ud835\udd80", "\ud835\udd81", "\ud835\udd82", "\ud835\udd83", "\ud835\udd84", "\ud835\udd85", "\ud835\udda0", "\ud835\udda1", "\ud835\udda2", "\ud835\udda3", "\ud835\udda4", "\ud835\udda5", "\ud835\udda6", "\ud835\udda7", "\ud835\udda8", "\ud835\udda9", "\ud835\uddaa", "\ud835\uddab", "\ud835\uddac", "\ud835\uddad", "\ud835\uddae", "\ud835\uddaf", "\ud835\uddb0", "\ud835\uddb1", "\ud835\uddb2", "\ud835\uddb3", "\ud835\uddb4", "\ud835\uddb5", "\ud835\uddb6", "\ud835\uddb7", "\ud835\uddb8", "\ud835\uddb9", "\ud835\uddd4", "\ud835\uddd5", "\ud835\uddd6", "\ud835\uddd7", "\ud835\uddd8", "\ud835\uddd9", "\ud835\uddda", "\ud835\udddb", "\ud835\udddc", "\ud835\udddd", "\ud835\uddde", "\ud835\udddf", "\ud835\udde0", "\ud835\udde1", "\ud835\udde2", "\ud835\udde3", "\ud835\udde4", "\ud835\udde5", "\ud835\udde6", "\ud835\udde7", "\ud835\udde8", "\ud835\udde9", "\ud835\uddea", "\ud835\uddeb", "\ud835\uddec", "\ud835\udded", "\ud835\ude08", "\ud835\ude09", "\ud835\ude0a", "\ud835\ude0b", "\ud835\ude0c", "\ud835\ude0d", "\ud835\ude0e", "\ud835\ude0f", "\ud835\ude10", "\ud835\ude11", "\ud835\ude12", "\ud835\ude13", "\ud835\ude14", "\ud835\ude15", "\ud835\ude16", "\ud835\ude17", "\ud835\ude18", "\ud835\ude19", "\ud835\ude1a", "\ud835\ude1b", "\ud835\ude1c", "\ud835\ude1d", "\ud835\ude1e", "\ud835\ude1f", "\ud835\ude20", "\ud835\ude21", "\ud835\ude3c", "\ud835\ude3d", "\ud835\ude3e", "\ud835\ude3f", "\ud835\ude40", "\ud835\ude41", "\ud835\ude42", "\ud835\ude43", "\ud835\ude44", "\ud835\ude45", "\ud835\ude46", "\ud835\ude47", "\ud835\ude48", "\ud835\ude49", "\ud835\ude4a", "\ud835\ude4b", "\ud835\ude4c", "\ud835\ude4d", "\ud835\ude4e", "\ud835\ude4f", "\ud835\ude50", "\ud835\ude51", "\ud835\ude52", "\ud835\ude53", "\ud835\ude54", "\ud835\ude55", "\ud835\ude70", "\ud835\ude71", "\ud835\ude72", "\ud835\ude73", "\ud835\ude74", "\ud835\ude75", "\ud835\ude76", "\ud835\ude77", "\ud835\ude78", "\ud835\ude79", "\ud835\ude7a", "\ud835\ude7b", "\ud835\ude7c", "\ud835\ude7d", "\ud835\ude7e", "\ud835\ude7f", "\ud835\ude80", "\ud835\ude81", "\ud835\ude82", "\ud835\ude83", "\ud835\ude84", "\ud835\ude85", "\ud835\ude86", "\ud835\ude87", "\ud835\ude88", "\ud835\ude89", "\ud835\udea8", "\ud835\udea9", "\ud835\udeaa", "\ud835\udeab", "\ud835\udeac", "\ud835\udead", "\ud835\udeae", "\ud835\udeaf", "\ud835\udeb0", "\ud835\udeb1", "\ud835\udeb2", "\ud835\udeb3", "\ud835\udeb4", "\ud835\udeb5", "\ud835\udeb6", "\ud835\udeb7", "\ud835\udeb8", "\ud835\udeb9", "\ud835\udeba", "\ud835\udebb", "\ud835\udebc", "\ud835\udebd", "\ud835\udebe", "\ud835\udebf", "\ud835\udec0", "\ud835\udee2", "\ud835\udee3", "\ud835\udee4", "\ud835\udee5", "\ud835\udee6", "\ud835\udee7", "\ud835\udee8", "\ud835\udee9", "\ud835\udeea", "\ud835\udeeb", "\ud835\udeec", "\ud835\udeed", "\ud835\udeee", "\ud835\udeef", "\ud835\udef0", "\ud835\udef1", "\ud835\udef2", "\ud835\udef3", "\ud835\udef4", "\ud835\udef5", "\ud835\udef6", "\ud835\udef7", "\ud835\udef8", "\ud835\udef9", "\ud835\udefa", "\ud835\udf1c", "\ud835\udf1d", "\ud835\udf1e", "\ud835\udf1f", "\ud835\udf20", "\ud835\udf21", "\ud835\udf22", "\ud835\udf23", "\ud835\udf24", "\ud835\udf25", "\ud835\udf26", "\ud835\udf27", "\ud835\udf28", "\ud835\udf29", "\ud835\udf2a", "\ud835\udf2b", "\ud835\udf2c", "\ud835\udf2d", "\ud835\udf2e", "\ud835\udf2f", "\ud835\udf30", "\ud835\udf31", "\ud835\udf32", "\ud835\udf33", "\ud835\udf34", "\ud835\udf56", "\ud835\udf57", "\ud835\udf58", "\ud835\udf59", "\ud835\udf5a", "\ud835\udf5b", "\ud835\udf5c", "\ud835\udf5d", "\ud835\udf5e", "\ud835\udf5f", "\ud835\udf60", "\ud835\udf61", "\ud835\udf62", "\ud835\udf63", "\ud835\udf64", "\ud835\udf65", "\ud835\udf66", "\ud835\udf67", "\ud835\udf68", "\ud835\udf69", "\ud835\udf6a", "\ud835\udf6b", "\ud835\udf6c", "\ud835\udf6d", "\ud835\udf6e", "\ud835\udf90", "\ud835\udf91", "\ud835\udf92", "\ud835\udf93", "\ud835\udf94", "\ud835\udf95", "\ud835\udf96", "\ud835\udf97", "\ud835\udf98", "\ud835\udf99", "\ud835\udf9a", "\ud835\udf9b", "\ud835\udf9c", "\ud835\udf9d", "\ud835\udf9e", "\ud835\udf9f", "\ud835\udfa0", "\ud835\udfa1", "\ud835\udfa2", "\ud835\udfa3", "\ud835\udfa4", "\ud835\udfa5", "\ud835\udfa6", "\ud835\udfa7", "\ud835\udfa8", "\ud835\udfca", "\ud83a\udd00", "\ud83a\udd01", "\ud83a\udd02", "\ud83a\udd03", "\ud83a\udd04", "\ud83a\udd05", "\ud83a\udd06", "\ud83a\udd07", "\ud83a\udd08", "\ud83a\udd09", "\ud83a\udd0a", "\ud83a\udd0b", "\ud83a\udd0c", "\ud83a\udd0d", "\ud83a\udd0e", "\ud83a\udd0f", "\ud83a\udd10", "\ud83a\udd11", "\ud83a\udd12", "\ud83a\udd13", "\ud83a\udd14", "\ud83a\udd15", "\ud83a\udd16", "\ud83a\udd17", "\ud83a\udd18", "\ud83a\udd19", "\ud83a\udd1a", "\ud83a\udd1b", "\ud83a\udd1c", "\ud83a\udd1d", "\ud83a\udd1e", "\ud83a\udd1f", "\ud83a\udd20", "\ud83a\udd21", "\u0903", "\u093b", "\u093e", "\u093f", "\u0940", "\u0949", "\u094a", "\u094b", "\u094c", "\u094e", "\u094f", "\u0982", "\u0983", "\u09be", "\u09bf", "\u09c0", "\u09c7", "\u09c8", "\u09cb", "\u09cc", "\u09d7", "\u0a03", "\u0a3e", "\u0a3f", "\u0a40", "\u0a83", "\u0abe", "\u0abf", "\u0ac0", "\u0ac9", "\u0acb", "\u0acc", "\u0b02", "\u0b03", "\u0b3e", "\u0b40", "\u0b47", "\u0b48", "\u0b4b", "\u0b4c", "\u0b57", "\u0bbe", "\u0bbf", "\u0bc1", "\u0bc2", "\u0bc6", "\u0bc7", "\u0bc8", "\u0bca", "\u0bcb", "\u0bcc", "\u0bd7", "\u0c01", "\u0c02", "\u0c03", "\u0c41", "\u0c42", "\u0c43", "\u0c44", "\u0c82", "\u0c83", "\u0cbe", "\u0cc0", "\u0cc1", "\u0cc2", "\u0cc3", "\u0cc4", "\u0cc7", "\u0cc8", "\u0cca", "\u0ccb", "\u0cd5", "\u0cd6", "\u0d02", "\u0d03", "\u0d3e", "\u0d3f", "\u0d40", "\u0d46", "\u0d47", "\u0d48", "\u0d4a", "\u0d4b", "\u0d4c", "\u0d57", "\u0d82", "\u0d83", "\u0dcf", "\u0dd0", "\u0dd1", "\u0dd8", "\u0dd9", "\u0dda", "\u0ddb", "\u0ddc", "\u0ddd", "\u0dde", "\u0ddf", "\u0df2", "\u0df3", "\u0f3e", "\u0f3f", "\u0f7f", "\u102b", "\u102c", "\u1031", "\u1038", "\u103b", "\u103c", "\u1056", "\u1057", "\u1062", "\u1063", "\u1064", "\u1067", "\u1068", "\u1069", "\u106a", "\u106b", "\u106c", "\u106d", "\u1083", "\u1084", "\u1087", "\u1088", "\u1089", "\u108a", "\u108b", "\u108c", "\u108f", "\u109a", "\u109b", "\u109c", "\u17b6", "\u17be", "\u17bf", "\u17c0", "\u17c1", "\u17c2", "\u17c3", "\u17c4", "\u17c5", "\u17c7", "\u17c8", "\u1923", "\u1924", "\u1925", "\u1926", "\u1929", "\u192a", "\u192b", "\u1930", "\u1931", "\u1933", "\u1934", "\u1935", "\u1936", "\u1937", "\u1938", "\u1a19", "\u1a1a", "\u1a55", "\u1a57", "\u1a61", "\u1a63", "\u1a64", "\u1a6d", "\u1a6e", "\u1a6f", "\u1a70", "\u1a71", "\u1a72", "\u1b04", "\u1b35", "\u1b3b", "\u1b3d", "\u1b3e", "\u1b3f", "\u1b40", "\u1b41", "\u1b43", "\u1b44", "\u1b82", "\u1ba1", "\u1ba6", "\u1ba7", "\u1baa", "\u1be7", "\u1bea", "\u1beb", "\u1bec", "\u1bee", "\u1bf2", "\u1bf3", "\u1c24", "\u1c25", "\u1c26", "\u1c27", "\u1c28", "\u1c29", "\u1c2a", "\u1c2b", "\u1c34", "\u1c35", "\u1ce1", "\u1cf2", "\u1cf3", "\u302e", "\u302f", "\ua823", "\ua824", "\ua827", "\ua880", "\ua881", "\ua8b4", "\ua8b5", "\ua8b6", "\ua8b7", "\ua8b8", "\ua8b9", "\ua8ba", "\ua8bb", "\ua8bc", "\ua8bd", "\ua8be", "\ua8bf", "\ua8c0", "\ua8c1", "\ua8c2", "\ua8c3", "\ua952", "\ua953", "\ua983", "\ua9b4", "\ua9b5", "\ua9ba", "\ua9bb", "\ua9bd", "\ua9be", "\ua9bf", "\ua9c0", "\uaa2f", "\uaa30", "\uaa33", "\uaa34", "\uaa4d", "\uaa7b", "\uaa7d", "\uaaeb", "\uaaee", "\uaaef", "\uaaf5", "\uabe3", "\uabe4", "\uabe6", "\uabe7", "\uabe9", "\uabea", "\uabec", "\ud804\udc00", "\ud804\udc02", "\ud804\udc82", "\ud804\udcb0", "\ud804\udcb1", "\ud804\udcb2", "\ud804\udcb7", "\ud804\udcb8", "\ud804\udd2c", "\ud804\udd82", "\ud804\uddb3", "\ud804\uddb4", "\ud804\uddb5", "\ud804\uddbf", "\ud804\uddc0", "\ud804\ude2c", "\ud804\ude2d", "\ud804\ude2e", "\ud804\ude32", "\ud804\ude33", "\ud804\ude35", "\ud804\udee0", "\ud804\udee1", "\ud804\udee2", "\ud804\udf02", "\ud804\udf03", "\ud804\udf3e", "\ud804\udf3f", "\ud804\udf41", "\ud804\udf42", "\ud804\udf43", "\ud804\udf44", "\ud804\udf47", "\ud804\udf48", "\ud804\udf4b", "\ud804\udf4c", "\ud804\udf4d", "\ud804\udf57", "\ud804\udf62", "\ud804\udf63", "\ud805\udc35", "\ud805\udc36", "\ud805\udc37", "\ud805\udc40", "\ud805\udc41", "\ud805\udc45", "\ud805\udcb0", "\ud805\udcb1", "\ud805\udcb2", "\ud805\udcb9", "\ud805\udcbb", "\ud805\udcbc", "\ud805\udcbd", "\ud805\udcbe", "\ud805\udcc1", "\ud805\uddaf", "\ud805\uddb0", "\ud805\uddb1", "\ud805\uddb8", "\ud805\uddb9", "\ud805\uddba", "\ud805\uddbb", "\ud805\uddbe", "\ud805\ude30", "\ud805\ude31", "\ud805\ude32", "\ud805\ude3b", "\ud805\ude3c", "\ud805\ude3e", "\ud805\udeac", "\ud805\udeae", "\ud805\udeaf", "\ud805\udeb6", "\ud805\udf20", "\ud805\udf21", "\ud805\udf26", "\ud807\udc2f", "\ud807\udc3e", "\ud807\udca9", "\ud807\udcb1", "\ud807\udcb4", "\ud81b\udf51", "\ud81b\udf52", "\ud81b\udf53", "\ud81b\udf54", "\ud81b\udf55", "\ud81b\udf56", "\ud81b\udf57", "\ud81b\udf58", "\ud81b\udf59", "\ud81b\udf5a", "\ud81b\udf5b", "\ud81b\udf5c", "\ud81b\udf5d", "\ud81b\udf5e", "\ud81b\udf5f", "\ud81b\udf60", "\ud81b\udf61", "\ud81b\udf62", "\ud81b\udf63", "\ud81b\udf64", "\ud81b\udf65", "\ud81b\udf66", "\ud81b\udf67", "\ud81b\udf68", "\ud81b\udf69", "\ud81b\udf6a", "\ud81b\udf6b", "\ud81b\udf6c", "\ud81b\udf6d", "\ud81b\udf6e", "\ud81b\udf6f", "\ud81b\udf70", "\ud81b\udf71", "\ud81b\udf72", "\ud81b\udf73", "\ud81b\udf74", "\ud81b\udf75", "\ud81b\udf76", "\ud81b\udf77", "\ud81b\udf78", "\ud81b\udf79", "\ud81b\udf7a", "\ud81b\udf7b", "\ud81b\udf7c", "\ud81b\udf7d", "\ud81b\udf7e", "\ud834\udd65", "\ud834\udd66", "\ud834\udd6d", "\ud834\udd6e", "\ud834\udd6f", "\ud834\udd70", "\ud834\udd71", "\ud834\udd72", "\u0300", "\u0301", "\u0302", "\u0303", "\u0304", "\u0305", "\u0306", "\u0307", "\u0308", "\u0309", "\u030a", "\u030b", "\u030c", "\u030d", "\u030e", "\u030f", "\u0310", "\u0311", "\u0312", "\u0313", "\u0314", "\u0315", "\u0316", "\u0317", "\u0318", "\u0319", "\u031a", "\u031b", "\u031c", "\u031d", "\u031e", "\u031f", "\u0320", "\u0321", "\u0322", "\u0323", "\u0324", "\u0325", "\u0326", "\u0327", "\u0328", "\u0329", "\u032a", "\u032b", "\u032c", "\u032d", "\u032e", "\u032f", "\u0330", "\u0331", "\u0332", "\u0333", "\u0334", "\u0335", "\u0336", "\u0337", "\u0338", "\u0339", "\u033a", "\u033b", "\u033c", "\u033d", "\u033e", "\u033f", "\u0340", "\u0341", "\u0342", "\u0343", "\u0344", "\u0345", "\u0346", "\u0347", "\u0348", "\u0349", "\u034a", "\u034b", "\u034c", "\u034d", "\u034e", "\u034f", "\u0350", "\u0351", "\u0352", "\u0353", "\u0354", "\u0355", "\u0356", "\u0357", "\u0358", "\u0359", "\u035a", "\u035b", "\u035c", "\u035d", "\u035e", "\u035f", "\u0360", "\u0361", "\u0362", "\u0363", "\u0364", "\u0365", "\u0366", "\u0367", "\u0368", "\u0369", "\u036a", "\u036b", "\u036c", "\u036d", "\u036e", "\u036f", "\u0483", "\u0484", "\u0485", "\u0486", "\u0487", "\u0591", "\u0592", "\u0593", "\u0594", "\u0595", "\u0596", "\u0597", "\u0598", "\u0599", "\u059a", "\u059b", "\u059c", "\u059d", "\u059e", "\u059f", "\u05a0", "\u05a1", "\u05a2", "\u05a3", "\u05a4", "\u05a5", "\u05a6", "\u05a7", "\u05a8", "\u05a9", "\u05aa", "\u05ab", "\u05ac", "\u05ad", "\u05ae", "\u05af", "\u05b0", "\u05b1", "\u05b2", "\u05b3", "\u05b4", "\u05b5", "\u05b6", "\u05b7", "\u05b8", "\u05b9", "\u05ba", "\u05bb", "\u05bc", "\u05bd", "\u05bf", "\u05c1", "\u05c2", "\u05c4", "\u05c5", "\u05c7", "\u0610", "\u0611", "\u0612", "\u0613", "\u0614", "\u0615", "\u0616", "\u0617", "\u0618", "\u0619", "\u061a", "\u064b", "\u064c", "\u064d", "\u064e", "\u064f", "\u0650", "\u0651", "\u0652", "\u0653", "\u0654", "\u0655", "\u0656", "\u0657", "\u0658", "\u0659", "\u065a", "\u065b", "\u065c", "\u065d", "\u065e", "\u065f", "\u0670", "\u06d6", "\u06d7", "\u06d8", "\u06d9", "\u06da", "\u06db", "\u06dc", "\u06df", "\u06e0", "\u06e1", "\u06e2", "\u06e3", "\u06e4", "\u06e7", "\u06e8", "\u06ea", "\u06eb", "\u06ec", "\u06ed", "\u0711", "\u0730", "\u0731", "\u0732", "\u0733", "\u0734", "\u0735", "\u0736", "\u0737", "\u0738", "\u0739", "\u073a", "\u073b", "\u073c", "\u073d", "\u073e", "\u073f", "\u0740", "\u0741", "\u0742", "\u0743", "\u0744", "\u0745", "\u0746", "\u0747", "\u0748", "\u0749", "\u074a", "\u07a6", "\u07a7", "\u07a8", "\u07a9", "\u07aa", "\u07ab", "\u07ac", "\u07ad", "\u07ae", "\u07af", "\u07b0", "\u07eb", "\u07ec", "\u07ed", "\u07ee", "\u07ef", "\u07f0", "\u07f1", "\u07f2", "\u07f3", "\u0816", "\u0817", "\u0818", "\u0819", "\u081b", "\u081c", "\u081d", "\u081e", "\u081f", "\u0820", "\u0821", "\u0822", "\u0823", "\u0825", "\u0826", "\u0827", "\u0829", "\u082a", "\u082b", "\u082c", "\u082d", "\u0859", "\u085a", "\u085b", "\u08d4", "\u08d5", "\u08d6", "\u08d7", "\u08d8", "\u08d9", "\u08da", "\u08db", "\u08dc", "\u08dd", "\u08de", "\u08df", "\u08e0", "\u08e1", "\u08e3", "\u08e4", "\u08e5", "\u08e6", "\u08e7", "\u08e8", "\u08e9", "\u08ea", "\u08eb", "\u08ec", "\u08ed", "\u08ee", "\u08ef", "\u08f0", "\u08f1", "\u08f2", "\u08f3", "\u08f4", "\u08f5", "\u08f6", "\u08f7", "\u08f8", "\u08f9", "\u08fa", "\u08fb", "\u08fc", "\u08fd", "\u08fe", "\u08ff", "\u0900", "\u0901", "\u0902", "\u093a", "\u093c", "\u0941", "\u0942", "\u0943", "\u0944", "\u0945", "\u0946", "\u0947", "\u0948", "\u094d", "\u0951", "\u0952", "\u0953", "\u0954", "\u0955", "\u0956", "\u0957", "\u0962", "\u0963", "\u0981", "\u09bc", "\u09c1", "\u09c2", "\u09c3", "\u09c4", "\u09cd", "\u09e2", "\u09e3", "\u0a01", "\u0a02", "\u0a3c", "\u0a41", "\u0a42", "\u0a47", "\u0a48", "\u0a4b", "\u0a4c", "\u0a4d", "\u0a51", "\u0a70", "\u0a71", "\u0a75", "\u0a81", "\u0a82", "\u0abc", "\u0ac1", "\u0ac2", "\u0ac3", "\u0ac4", "\u0ac5", "\u0ac7", "\u0ac8", "\u0acd", "\u0ae2", "\u0ae3", "\u0b01", "\u0b3c", "\u0b3f", "\u0b41", "\u0b42", "\u0b43", "\u0b44", "\u0b4d", "\u0b56", "\u0b62", "\u0b63", "\u0b82", "\u0bc0", "\u0bcd", "\u0c00", "\u0c3e", "\u0c3f", "\u0c40", "\u0c46", "\u0c47", "\u0c48", "\u0c4a", "\u0c4b", "\u0c4c", "\u0c4d", "\u0c55", "\u0c56", "\u0c62", "\u0c63", "\u0c81", "\u0cbc", "\u0cbf", "\u0cc6", "\u0ccc", "\u0ccd", "\u0ce2", "\u0ce3", "\u0d01", "\u0d41", "\u0d42", "\u0d43", "\u0d44", "\u0d4d", "\u0d62", "\u0d63", "\u0dca", "\u0dd2", "\u0dd3", "\u0dd4", "\u0dd6", "\u0e31", "\u0e34", "\u0e35", "\u0e36", "\u0e37", "\u0e38", "\u0e39", "\u0e3a", "\u0e47", "\u0e48", "\u0e49", "\u0e4a", "\u0e4b", "\u0e4c", "\u0e4d", "\u0e4e", "\u0eb1", "\u0eb4", "\u0eb5", "\u0eb6", "\u0eb7", "\u0eb8", "\u0eb9", "\u0ebb", "\u0ebc", "\u0ec8", "\u0ec9", "\u0eca", "\u0ecb", "\u0ecc", "\u0ecd", "\u0f18", "\u0f19", "\u0f35", "\u0f37", "\u0f39", "\u0f71", "\u0f72", "\u0f73", "\u0f74", "\u0f75", "\u0f76", "\u0f77", "\u0f78", "\u0f79", "\u0f7a", "\u0f7b", "\u0f7c", "\u0f7d", "\u0f7e", "\u0f80", "\u0f81", "\u0f82", "\u0f83", "\u0f84", "\u0f86", "\u0f87", "\u0f8d", "\u0f8e", "\u0f8f", "\u0f90", "\u0f91", "\u0f92", "\u0f93", "\u0f94", "\u0f95", "\u0f96", "\u0f97", "\u0f99", "\u0f9a", "\u0f9b", "\u0f9c", "\u0f9d", "\u0f9e", "\u0f9f", "\u0fa0", "\u0fa1", "\u0fa2", "\u0fa3", "\u0fa4", "\u0fa5", "\u0fa6", "\u0fa7", "\u0fa8", "\u0fa9", "\u0faa", "\u0fab", "\u0fac", "\u0fad", "\u0fae", "\u0faf", "\u0fb0", "\u0fb1", "\u0fb2", "\u0fb3", "\u0fb4", "\u0fb5", "\u0fb6", "\u0fb7", "\u0fb8", "\u0fb9", "\u0fba", "\u0fbb", "\u0fbc", "\u0fc6", "\u102d", "\u102e", "\u102f", "\u1030", "\u1032", "\u1033", "\u1034", "\u1035", "\u1036", "\u1037", "\u1039", "\u103a", "\u103d", "\u103e", "\u1058", "\u1059", "\u105e", "\u105f", "\u1060", "\u1071", "\u1072", "\u1073", "\u1074", "\u1082", "\u1085", "\u1086", "\u108d", "\u109d", "\u135d", "\u135e", "\u135f", "\u1712", "\u1713", "\u1714", "\u1732", "\u1733", "\u1734", "\u1752", "\u1753", "\u1772", "\u1773", "\u17b4", "\u17b5", "\u17b7", "\u17b8", "\u17b9", "\u17ba", "\u17bb", "\u17bc", "\u17bd", "\u17c6", "\u17c9", "\u17ca", "\u17cb", "\u17cc", "\u17cd", "\u17ce", "\u17cf", "\u17d0", "\u17d1", "\u17d2", "\u17d3", "\u17dd", "\u180b", "\u180c", "\u180d", "\u1885", "\u1886", "\u18a9", "\u1920", "\u1921", "\u1922", "\u1927", "\u1928", "\u1932", "\u1939", "\u193a", "\u193b", "\u1a17", "\u1a18", "\u1a1b", "\u1a56", "\u1a58", "\u1a59", "\u1a5a", "\u1a5b", "\u1a5c", "\u1a5d", "\u1a5e", "\u1a60", "\u1a62", "\u1a65", "\u1a66", "\u1a67", "\u1a68", "\u1a69", "\u1a6a", "\u1a6b", "\u1a6c", "\u1a73", "\u1a74", "\u1a75", "\u1a76", "\u1a77", "\u1a78", "\u1a79", "\u1a7a", "\u1a7b", "\u1a7c", "\u1a7f", "\u1ab0", "\u1ab1", "\u1ab2", "\u1ab3", "\u1ab4", "\u1ab5", "\u1ab6", "\u1ab7", "\u1ab8", "\u1ab9", "\u1aba", "\u1abb", "\u1abc", "\u1abd", "\u1b00", "\u1b01", "\u1b02", "\u1b03", "\u1b34", "\u1b36", "\u1b37", "\u1b38", "\u1b39", "\u1b3a", "\u1b3c", "\u1b42", "\u1b6b", "\u1b6c", "\u1b6d", "\u1b6e", "\u1b6f", "\u1b70", "\u1b71", "\u1b72", "\u1b73", "\u1b80", "\u1b81", "\u1ba2", "\u1ba3", "\u1ba4", "\u1ba5", "\u1ba8", "\u1ba9", "\u1bab", "\u1bac", "\u1bad", "\u1be6", "\u1be8", "\u1be9", "\u1bed", "\u1bef", "\u1bf0", "\u1bf1", "\u1c2c", "\u1c2d", "\u1c2e", "\u1c2f", "\u1c30", "\u1c31", "\u1c32", "\u1c33", "\u1c36", "\u1c37", "\u1cd0", "\u1cd1", "\u1cd2", "\u1cd4", "\u1cd5", "\u1cd6", "\u1cd7", "\u1cd8", "\u1cd9", "\u1cda", "\u1cdb", "\u1cdc", "\u1cdd", "\u1cde", "\u1cdf", "\u1ce0", "\u1ce2", "\u1ce3", "\u1ce4", "\u1ce5", "\u1ce6", "\u1ce7", "\u1ce8", "\u1ced", "\u1cf4", "\u1cf8", "\u1cf9", "\u1dc0", "\u1dc1", "\u1dc2", "\u1dc3", "\u1dc4", "\u1dc5", "\u1dc6", "\u1dc7", "\u1dc8", "\u1dc9", "\u1dca", "\u1dcb", "\u1dcc", "\u1dcd", "\u1dce", "\u1dcf", "\u1dd0", "\u1dd1", "\u1dd2", "\u1dd3", "\u1dd4", "\u1dd5", "\u1dd6", "\u1dd7", "\u1dd8", "\u1dd9", "\u1dda", "\u1ddb", "\u1ddc", "\u1ddd", "\u1dde", "\u1ddf", "\u1de0", "\u1de1", "\u1de2", "\u1de3", "\u1de4", "\u1de5", "\u1de6", "\u1de7", "\u1de8", "\u1de9", "\u1dea", "\u1deb", "\u1dec", "\u1ded", "\u1dee", "\u1def", "\u1df0", "\u1df1", "\u1df2", "\u1df3", "\u1df4", "\u1df5", "\u1dfb", "\u1dfc", "\u1dfd", "\u1dfe", "\u1dff", "\u20d0", "\u20d1", "\u20d2", "\u20d3", "\u20d4", "\u20d5", "\u20d6", "\u20d7", "\u20d8", "\u20d9", "\u20da", "\u20db", "\u20dc", "\u20e1", "\u20e5", "\u20e6", "\u20e7", "\u20e8", "\u20e9", "\u20ea", "\u20eb", "\u20ec", "\u20ed", "\u20ee", "\u20ef", "\u20f0", "\u2cef", "\u2cf0", "\u2cf1", "\u2d7f", "\u2de0", "\u2de1", "\u2de2", "\u2de3", "\u2de4", "\u2de5", "\u2de6", "\u2de7", "\u2de8", "\u2de9", "\u2dea", "\u2deb", "\u2dec", "\u2ded", "\u2dee", "\u2def", "\u2df0", "\u2df1", "\u2df2", "\u2df3", "\u2df4", "\u2df5", "\u2df6", "\u2df7", "\u2df8", "\u2df9", "\u2dfa", "\u2dfb", "\u2dfc", "\u2dfd", "\u2dfe", "\u2dff", "\u302a", "\u302b", "\u302c", "\u302d", "\u3099", "\u309a", "\ua66f", "\ua674", "\ua675", "\ua676", "\ua677", "\ua678", "\ua679", "\ua67a", "\ua67b", "\ua67c", "\ua67d", "\ua69e", "\ua69f", "\ua6f0", "\ua6f1", "\ua802", "\ua806", "\ua80b", "\ua825", "\ua826", "\ua8c4", "\ua8c5", "\ua8e0", "\ua8e1", "\ua8e2", "\ua8e3", "\ua8e4", "\ua8e5", "\ua8e6", "\ua8e7", "\ua8e8", "\ua8e9", "\ua8ea", "\ua8eb", "\ua8ec", "\ua8ed", "\ua8ee", "\ua8ef", "\ua8f0", "\ua8f1", "\ua926", "\ua927", "\ua928", "\ua929", "\ua92a", "\ua92b", "\ua92c", "\ua92d", "\ua947", "\ua948", "\ua949", "\ua94a", "\ua94b", "\ua94c", "\ua94d", "\ua94e", "\ua94f", "\ua950", "\ua951", "\ua980", "\ua981", "\ua982", "\ua9b3", "\ua9b6", "\ua9b7", "\ua9b8", "\ua9b9", "\ua9bc", "\ua9e5", "\uaa29", "\uaa2a", "\uaa2b", "\uaa2c", "\uaa2d", "\uaa2e", "\uaa31", "\uaa32", "\uaa35", "\uaa36", "\uaa43", "\uaa4c", "\uaa7c", "\uaab0", "\uaab2", "\uaab3", "\uaab4", "\uaab7", "\uaab8", "\uaabe", "\uaabf", "\uaac1", "\uaaec", "\uaaed", "\uaaf6", "\uabe5", "\uabe8", "\uabed", "\ufb1e", "\ufe00", "\ufe01", "\ufe02", "\ufe03", "\ufe04", "\ufe05", "\ufe06", "\ufe07", "\ufe08", "\ufe09", "\ufe0a", "\ufe0b", "\ufe0c", "\ufe0d", "\ufe0e", "\ufe0f", "\ufe20", "\ufe21", "\ufe22", "\ufe23", "\ufe24", "\ufe25", "\ufe26", "\ufe27", "\ufe28", "\ufe29", "\ufe2a", "\ufe2b", "\ufe2c", "\ufe2d", "\ufe2e", "\ufe2f", "\ud800\uddfd", "\ud800\udee0", "\ud800\udf76", "\ud800\udf77", "\ud800\udf78", "\ud800\udf79", "\ud800\udf7a", "\ud802\ude01", "\ud802\ude02", "\ud802\ude03", "\ud802\ude05", "\ud802\ude06", "\ud802\ude0c", "\ud802\ude0d", "\ud802\ude0e", "\ud802\ude0f", "\ud802\ude38", "\ud802\ude39", "\ud802\ude3a", "\ud802\ude3f", "\ud802\udee5", "\ud802\udee6", "\ud804\udc01", "\ud804\udc38", "\ud804\udc39", "\ud804\udc3a", "\ud804\udc3b", "\ud804\udc3c", "\ud804\udc3d", "\ud804\udc3e", "\ud804\udc3f", "\ud804\udc40", "\ud804\udc41", "\ud804\udc42", "\ud804\udc43", "\ud804\udc44", "\ud804\udc45", "\ud804\udc46", "\ud804\udc7f", "\ud804\udc80", "\ud804\udc81", "\ud804\udcb3", "\ud804\udcb4", "\ud804\udcb5", "\ud804\udcb6", "\ud804\udcb9", "\ud804\udcba", "\ud804\udd00", "\ud804\udd01", "\ud804\udd02", "\ud804\udd27", "\ud804\udd28", "\ud804\udd29", "\ud804\udd2a", "\ud804\udd2b", "\ud804\udd2d", "\ud804\udd2e", "\ud804\udd2f", "\ud804\udd30", "\ud804\udd31", "\ud804\udd32", "\ud804\udd33", "\ud804\udd34", "\ud804\udd73", "\ud804\udd80", "\ud804\udd81", "\ud804\uddb6", "\ud804\uddb7", "\ud804\uddb8", "\ud804\uddb9", "\ud804\uddba", "\ud804\uddbb", "\ud804\uddbc", "\ud804\uddbd", "\ud804\uddbe", "\ud804\uddca", "\ud804\uddcb", "\ud804\uddcc", "\ud804\ude2f", "\ud804\ude30", "\ud804\ude31", "\ud804\ude34", "\ud804\ude36", "\ud804\ude37", "\ud804\ude3e", "\ud804\udedf", "\ud804\udee3", "\ud804\udee4", "\ud804\udee5", "\ud804\udee6", "\ud804\udee7", "\ud804\udee8", "\ud804\udee9", "\ud804\udeea", "\ud804\udf00", "\ud804\udf01", "\ud804\udf3c", "\ud804\udf40", "\ud804\udf66", "\ud804\udf67", "\ud804\udf68", "\ud804\udf69", "\ud804\udf6a", "\ud804\udf6b", "\ud804\udf6c", "\ud804\udf70", "\ud804\udf71", "\ud804\udf72", "\ud804\udf73", "\ud804\udf74", "\ud805\udc38", "\ud805\udc39", "\ud805\udc3a", "\ud805\udc3b", "\ud805\udc3c", "\ud805\udc3d", "\ud805\udc3e", "\ud805\udc3f", "\ud805\udc42", "\ud805\udc43", "\ud805\udc44", "\ud805\udc46", "\ud805\udcb3", "\ud805\udcb4", "\ud805\udcb5", "\ud805\udcb6", "\ud805\udcb7", "\ud805\udcb8", "\ud805\udcba", "\ud805\udcbf", "\ud805\udcc0", "\ud805\udcc2", "\ud805\udcc3", "\ud805\uddb2", "\ud805\uddb3", "\ud805\uddb4", "\ud805\uddb5", "\ud805\uddbc", "\ud805\uddbd", "\ud805\uddbf", "\ud805\uddc0", "\ud805\udddc", "\ud805\udddd", "\ud805\ude33", "\ud805\ude34", "\ud805\ude35", "\ud805\ude36", "\ud805\ude37", "\ud805\ude38", "\ud805\ude39", "\ud805\ude3a", "\ud805\ude3d", "\ud805\ude3f", "\ud805\ude40", "\ud805\udeab", "\ud805\udead", "\ud805\udeb0", "\ud805\udeb1", "\ud805\udeb2", "\ud805\udeb3", "\ud805\udeb4", "\ud805\udeb5", "\ud805\udeb7", "\ud805\udf1d", "\ud805\udf1e", "\ud805\udf1f", "\ud805\udf22", "\ud805\udf23", "\ud805\udf24", "\ud805\udf25", "\ud805\udf27", "\ud805\udf28", "\ud805\udf29", "\ud805\udf2a", "\ud805\udf2b", "\ud807\udc30", "\ud807\udc31", "\ud807\udc32", "\ud807\udc33", "\ud807\udc34", "\ud807\udc35", "\ud807\udc36", "\ud807\udc38", "\ud807\udc39", "\ud807\udc3a", "\ud807\udc3b", "\ud807\udc3c", "\ud807\udc3d", "\ud807\udc3f", "\ud807\udc92", "\ud807\udc93", "\ud807\udc94", "\ud807\udc95", "\ud807\udc96", "\ud807\udc97", "\ud807\udc98", "\ud807\udc99", "\ud807\udc9a", "\ud807\udc9b", "\ud807\udc9c", "\ud807\udc9d", "\ud807\udc9e", "\ud807\udc9f", "\ud807\udca0", "\ud807\udca1", "\ud807\udca2", "\ud807\udca3", "\ud807\udca4", "\ud807\udca5", "\ud807\udca6", "\ud807\udca7", "\ud807\udcaa", "\ud807\udcab", "\ud807\udcac", "\ud807\udcad", "\ud807\udcae", "\ud807\udcaf", "\ud807\udcb0", "\ud807\udcb2", "\ud807\udcb3", "\ud807\udcb5", "\ud807\udcb6", "\ud81a\udef0", "\ud81a\udef1", "\ud81a\udef2", "\ud81a\udef3", "\ud81a\udef4", "\ud81a\udf30", "\ud81a\udf31", "\ud81a\udf32", "\ud81a\udf33", "\ud81a\udf34", "\ud81a\udf35", "\ud81a\udf36", "\ud81b\udf8f", "\ud81b\udf90", "\ud81b\udf91", "\ud81b\udf92", "\ud82f\udc9d", "\ud82f\udc9e", "\ud834\udd67", "\ud834\udd68", "\ud834\udd69", "\ud834\udd7b", "\ud834\udd7c", "\ud834\udd7d", "\ud834\udd7e", "\ud834\udd7f", "\ud834\udd80", "\ud834\udd81", "\ud834\udd82", "\ud834\udd85", "\ud834\udd86", "\ud834\udd87", "\ud834\udd88", "\ud834\udd89", "\ud834\udd8a", "\ud834\udd8b", "\ud834\uddaa", "\ud834\uddab", "\ud834\uddac", "\ud834\uddad", "\ud834\ude42", "\ud834\ude43", "\ud834\ude44", "\ud836\ude00", "\ud836\ude01", "\ud836\ude02", "\ud836\ude03", "\ud836\ude04", "\ud836\ude05", "\ud836\ude06", "\ud836\ude07", "\ud836\ude08", "\ud836\ude09", "\ud836\ude0a", "\ud836\ude0b", "\ud836\ude0c", "\ud836\ude0d", "\ud836\ude0e", "\ud836\ude0f", "\ud836\ude10", "\ud836\ude11", "\ud836\ude12", "\ud836\ude13", "\ud836\ude14", "\ud836\ude15", "\ud836\ude16", "\ud836\ude17", "\ud836\ude18", "\ud836\ude19", "\ud836\ude1a", "\ud836\ude1b", "\ud836\ude1c", "\ud836\ude1d", "\ud836\ude1e", "\ud836\ude1f", "\ud836\ude20", "\ud836\ude21", "\ud836\ude22", "\ud836\ude23", "\ud836\ude24", "\ud836\ude25", "\ud836\ude26", "\ud836\ude27", "\ud836\ude28", "\ud836\ude29", "\ud836\ude2a", "\ud836\ude2b", "\ud836\ude2c", "\ud836\ude2d", "\ud836\ude2e", "\ud836\ude2f", "\ud836\ude30", "\ud836\ude31", "\ud836\ude32", "\ud836\ude33", "\ud836\ude34", "\ud836\ude35", "\ud836\ude36", "\ud836\ude3b", "\ud836\ude3c", "\ud836\ude3d", "\ud836\ude3e", "\ud836\ude3f", "\ud836\ude40", "\ud836\ude41", "\ud836\ude42", "\ud836\ude43", "\ud836\ude44", "\ud836\ude45", "\ud836\ude46", "\ud836\ude47", "\ud836\ude48", "\ud836\ude49", "\ud836\ude4a", "\ud836\ude4b", "\ud836\ude4c", "\ud836\ude4d", "\ud836\ude4e", "\ud836\ude4f", "\ud836\ude50", "\ud836\ude51", "\ud836\ude52", "\ud836\ude53", "\ud836\ude54", "\ud836\ude55", "\ud836\ude56", "\ud836\ude57", "\ud836\ude58", "\ud836\ude59", "\ud836\ude5a", "\ud836\ude5b", "\ud836\ude5c", "\ud836\ude5d", "\ud836\ude5e", "\ud836\ude5f", "\ud836\ude60", "\ud836\ude61", "\ud836\ude62", "\ud836\ude63", "\ud836\ude64", "\ud836\ude65", "\ud836\ude66", "\ud836\ude67", "\ud836\ude68", "\ud836\ude69", "\ud836\ude6a", "\ud836\ude6b", "\ud836\ude6c", "\ud836\ude75", "\ud836\ude84", "\ud836\ude9b", "\ud836\ude9c", "\ud836\ude9d", "\ud836\ude9e", "\ud836\ude9f", "\ud836\udea1", "\ud836\udea2", "\ud836\udea3", "\ud836\udea4", "\ud836\udea5", "\ud836\udea6", "\ud836\udea7", "\ud836\udea8", "\ud836\udea9", "\ud836\udeaa", "\ud836\udeab", "\ud836\udeac", "\ud836\udead", "\ud836\udeae", "\ud836\udeaf", "\ud838\udc00", "\ud838\udc01", "\ud838\udc02", "\ud838\udc03", "\ud838\udc04", "\ud838\udc05", "\ud838\udc06", "\ud838\udc08", "\ud838\udc09", "\ud838\udc0a", "\ud838\udc0b", "\ud838\udc0c", "\ud838\udc0d", "\ud838\udc0e", "\ud838\udc0f", "\ud838\udc10", "\ud838\udc11", "\ud838\udc12", "\ud838\udc13", "\ud838\udc14", "\ud838\udc15", "\ud838\udc16", "\ud838\udc17", "\ud838\udc18", "\ud838\udc1b", "\ud838\udc1c", "\ud838\udc1d", "\ud838\udc1e", "\ud838\udc1f", "\ud838\udc20", "\ud838\udc21", "\ud838\udc23", "\ud838\udc24", "\ud838\udc26", "\ud838\udc27", "\ud838\udc28", "\ud838\udc29", "\ud838\udc2a", "\ud83a\udcd0", "\ud83a\udcd1", "\ud83a\udcd2", "\ud83a\udcd3", "\ud83a\udcd4", "\ud83a\udcd5", "\ud83a\udcd6", "\ud83a\udd44", "\ud83a\udd45", "\ud83a\udd46", "\ud83a\udd47", "\ud83a\udd48", "\ud83a\udd49", "\ud83a\udd4a", "\udb40\udd00", "\udb40\udd01", "\udb40\udd02", "\udb40\udd03", "\udb40\udd04", "\udb40\udd05", "\udb40\udd06", "\udb40\udd07", "\udb40\udd08", "\udb40\udd09", "\udb40\udd0a", "\udb40\udd0b", "\udb40\udd0c", "\udb40\udd0d", "\udb40\udd0e", "\udb40\udd0f", "\udb40\udd10", "\udb40\udd11", "\udb40\udd12", "\udb40\udd13", "\udb40\udd14", "\udb40\udd15", "\udb40\udd16", "\udb40\udd17", "\udb40\udd18", "\udb40\udd19", "\udb40\udd1a", "\udb40\udd1b", "\udb40\udd1c", "\udb40\udd1d", "\udb40\udd1e", "\udb40\udd1f", "\udb40\udd20", "\udb40\udd21", "\udb40\udd22", "\udb40\udd23", "\udb40\udd24", "\udb40\udd25", "\udb40\udd26", "\udb40\udd27", "\udb40\udd28", "\udb40\udd29", "\udb40\udd2a", "\udb40\udd2b", "\udb40\udd2c", "\udb40\udd2d", "\udb40\udd2e", "\udb40\udd2f", "\udb40\udd30", "\udb40\udd31", "\udb40\udd32", "\udb40\udd33", "\udb40\udd34", "\udb40\udd35", "\udb40\udd36", "\udb40\udd37", "\udb40\udd38", "\udb40\udd39", "\udb40\udd3a", "\udb40\udd3b", "\udb40\udd3c", "\udb40\udd3d", "\udb40\udd3e", "\udb40\udd3f", "\udb40\udd40", "\udb40\udd41", "\udb40\udd42", "\udb40\udd43", "\udb40\udd44", "\udb40\udd45", "\udb40\udd46", "\udb40\udd47", "\udb40\udd48", "\udb40\udd49", "\udb40\udd4a", "\udb40\udd4b", "\udb40\udd4c", "\udb40\udd4d", "\udb40\udd4e", "\udb40\udd4f", "\udb40\udd50", "\udb40\udd51", "\udb40\udd52", "\udb40\udd53", "\udb40\udd54", "\udb40\udd55", "\udb40\udd56", "\udb40\udd57", "\udb40\udd58", "\udb40\udd59", "\udb40\udd5a", "\udb40\udd5b", "\udb40\udd5c", "\udb40\udd5d", "\udb40\udd5e", "\udb40\udd5f", "\udb40\udd60", "\udb40\udd61", "\udb40\udd62", "\udb40\udd63", "\udb40\udd64", "\udb40\udd65", "\udb40\udd66", "\udb40\udd67", "\udb40\udd68", "\udb40\udd69", "\udb40\udd6a", "\udb40\udd6b", "\udb40\udd6c", "\udb40\udd6d", "\udb40\udd6e", "\udb40\udd6f", "\udb40\udd70", "\udb40\udd71", "\udb40\udd72", "\udb40\udd73", "\udb40\udd74", "\udb40\udd75", "\udb40\udd76", "\udb40\udd77", "\udb40\udd78", "\udb40\udd79", "\udb40\udd7a", "\udb40\udd7b", "\udb40\udd7c", "\udb40\udd7d", "\udb40\udd7e", "\udb40\udd7f", "\udb40\udd80", "\udb40\udd81", "\udb40\udd82", "\udb40\udd83", "\udb40\udd84", "\udb40\udd85", "\udb40\udd86", "\udb40\udd87", "\udb40\udd88", "\udb40\udd89", "\udb40\udd8a", "\udb40\udd8b", "\udb40\udd8c", "\udb40\udd8d", "\udb40\udd8e", "\udb40\udd8f", "\udb40\udd90", "\udb40\udd91", "\udb40\udd92", "\udb40\udd93", "\udb40\udd94", "\udb40\udd95", "\udb40\udd96", "\udb40\udd97", "\udb40\udd98", "\udb40\udd99", "\udb40\udd9a", "\udb40\udd9b", "\udb40\udd9c", "\udb40\udd9d", "\udb40\udd9e", "\udb40\udd9f", "\udb40\udda0", "\udb40\udda1", "\udb40\udda2", "\udb40\udda3", "\udb40\udda4", "\udb40\udda5", "\udb40\udda6", "\udb40\udda7", "\udb40\udda8", "\udb40\udda9", "\udb40\uddaa", "\udb40\uddab", "\udb40\uddac", "\udb40\uddad", "\udb40\uddae", "\udb40\uddaf", "\udb40\uddb0", "\udb40\uddb1", "\udb40\uddb2", "\udb40\uddb3", "\udb40\uddb4", "\udb40\uddb5", "\udb40\uddb6", "\udb40\uddb7", "\udb40\uddb8", "\udb40\uddb9", "\udb40\uddba", "\udb40\uddbb", "\udb40\uddbc", "\udb40\uddbd", "\udb40\uddbe", "\udb40\uddbf", "\udb40\uddc0", "\udb40\uddc1", "\udb40\uddc2", "\udb40\uddc3", "\udb40\uddc4", "\udb40\uddc5", "\udb40\uddc6", "\udb40\uddc7", "\udb40\uddc8", "\udb40\uddc9", "\udb40\uddca", "\udb40\uddcb", "\udb40\uddcc", "\udb40\uddcd", "\udb40\uddce", "\udb40\uddcf", "\udb40\uddd0", "\udb40\uddd1", "\udb40\uddd2", "\udb40\uddd3", "\udb40\uddd4", "\udb40\uddd5", "\udb40\uddd6", "\udb40\uddd7", "\udb40\uddd8", "\udb40\uddd9", "\udb40\uddda", "\udb40\udddb", "\udb40\udddc", "\udb40\udddd", "\udb40\uddde", "\udb40\udddf", "\udb40\udde0", "\udb40\udde1", "\udb40\udde2", "\udb40\udde3", "\udb40\udde4", "\udb40\udde5", "\udb40\udde6", "\udb40\udde7", "\udb40\udde8", "\udb40\udde9", "\udb40\uddea", "\udb40\uddeb", "\udb40\uddec", "\udb40\udded", "\udb40\uddee", "\udb40\uddef"] \ No newline at end of file diff --git a/build/lib/better_profanity/profanity.py b/build/lib/better_profanity/profanity.py new file mode 100644 index 0000000..feabdc5 --- /dev/null +++ b/build/lib/better_profanity/profanity.py @@ -0,0 +1,179 @@ +from itertools import product +from typing import Set, List +from .utils import (ALLOWED_CHARACTERS, get_complete_path_of_file, + get_next_words, get_start_index_of_next_word, + load_unicode_symbols, any_next_words_form_swear_word) + +## GLOBAL VARIABLES ## +CENSOR_WORDSET = set() +CHARS_MAPPING = { + 'a': ('a', '@', '*', '4'), + 'i': ('i', '*', 'l', '1'), + 'o': ('o', '*', '0', '@'), + 'u': ('u', '*', 'v'), + 'v': ('v', '*', 'u'), + 'l': ('l', '1'), + 'e': ('e', '*', '3'), + 's': ('s', '$'), +} + +# Pre-load the unicode characters +load_unicode_symbols() + +# The max number of additional words forming a swear word. For example: +# - hand job = 1 +# - this is a fish = 3 +MAX_NUMBER_COMBINATIONS = 1 + + +def count_non_allowed_characters(word: str) -> int: + count = 0 + for char in iter(word): + if char not in ALLOWED_CHARACTERS: + count += 1 + return count + +def load_censor_words(custom_words: List = None): + """Generate a set of words that need to be censored.""" + global CENSOR_WORDSET + global MAX_NUMBER_COMBINATIONS + + # Replace the words from `profanity_wordlist.txt` with a custom list + if custom_words: + temp_words = custom_words + else: + temp_words = read_wordlist() + + all_censor_words = set() + for word in temp_words: + # All words in CENSOR_WORDSET must be in lowercase + word = word.lower() + num_of_non_allowed_chars = count_non_allowed_characters(word) + if num_of_non_allowed_chars > MAX_NUMBER_COMBINATIONS: + MAX_NUMBER_COMBINATIONS = num_of_non_allowed_chars + + all_censor_words.update(set(generate_patterns_from_word(word))) + + # The default wordlist takes ~5MB+ of memory + CENSOR_WORDSET = all_censor_words + + +def generate_patterns_from_word(word: str) -> Set[str]: + """Return all patterns can be generated from the word.""" + combos = [ + (char,) if char not in CHARS_MAPPING else CHARS_MAPPING[char] + for char in iter(word) + ] + return (''.join(pattern) for pattern in product(*combos)) + + +def read_wordlist() -> Set[str]: + """Return words from file `profanity_wordlist.txt`.""" + wordlist_filename = 'profanity_wordlist.txt' + wordlist_path = get_complete_path_of_file(wordlist_filename) + try: + with open(wordlist_path, encoding='utf-8') as wordlist_file: + for row in iter(wordlist_file): + row = row.strip() + if row != "": + yield row + except FileNotFoundError: + print('Unable to find profanity_wordlist.txt') + pass + + +def get_replacement_for_swear_word(censor_char: str) -> str: + return censor_char * 4 + + +def contains_profanity(text: str) -> bool: + """Return True if the input text has any swear words.""" + return text != censor(text) + + +def update_next_words_indices( + text: str, words_indices: List[tuple], start_idx: int +) -> List[tuple]: + """Return a list of next words_indices after the input index.""" + if not words_indices: + words_indices = get_next_words(text, start_idx, MAX_NUMBER_COMBINATIONS) + else: + del words_indices[:2] + if words_indices and words_indices[-1][0] != "": + words_indices += get_next_words(text, words_indices[-1][1], 1) + return words_indices + + +def hide_swear_words(text: str, censor_char: str) -> str: + """Replace the swear words with censor characters.""" + censored_text = "" + cur_word = "" + skip_index = -1 + skip_cur_char = False + next_words_indices = [] + start_idx_of_next_word = get_start_index_of_next_word(text, 0) + + # If there are no words in the text, return the raw text without parsing + if start_idx_of_next_word >= len(text) - 1: + return text + + # Left strip the text, to avoid inaccurate parsing + if start_idx_of_next_word > 0: + censored_text = text[:start_idx_of_next_word] + text = text[start_idx_of_next_word:] + + # Splitting each word in the text to compare with censored words + # for index in iter(range(start_idx_of_next_word, len(text))): + # char = text[index] + for index, char in iter(enumerate(text)): + if index < skip_index: + continue + if char in ALLOWED_CHARACTERS: + cur_word += char + continue + + # Skip continuous non-allowed characters + if cur_word.strip() == "": + censored_text += char + cur_word = "" + continue + + # Iterate the next words combined with the current one + # to check if it forms a swear word + next_words_indices = update_next_words_indices(text, next_words_indices, index) + contains_swear_word, end_index = any_next_words_form_swear_word( + cur_word, text, next_words_indices, CENSOR_WORDSET + ) + if contains_swear_word: + cur_word = get_replacement_for_swear_word(censor_char) + skip_index = end_index + char = "" + next_words_indices = [] + + # If the current a swear word + if cur_word.lower() in CENSOR_WORDSET: + cur_word = get_replacement_for_swear_word(censor_char) + + censored_text += cur_word + censored_text += char + cur_word = "" + + # Final check + if cur_word != "" and skip_index < len(text): + if cur_word.lower() in CENSOR_WORDSET: + cur_word = get_replacement_for_swear_word(censor_char) + censored_text += cur_word + return censored_text + + +def censor(text: str, censor_char: str = '*') -> str: + """Replace the swear words in the text with `censor_char`.""" + + if not isinstance(text, str): + text = str(text) + if not isinstance(censor_char, str): + censor_char = str(censor_char) + + if not CENSOR_WORDSET: + load_censor_words() + return hide_swear_words(text, censor_char) diff --git a/build/lib/better_profanity/profanity_wordlist.txt b/build/lib/better_profanity/profanity_wordlist.txt new file mode 100644 index 0000000..9e0ed26 --- /dev/null +++ b/build/lib/better_profanity/profanity_wordlist.txt @@ -0,0 +1,140 @@ +2g1c +2 girls 1 cup +anal +ass +asshole +arsehole +assmunch +auto erotic +autoerotic +ballsack +bastard +bdsm +bestiality +bitch +bich +bimbo +bimbos +blowjob +blow job +blue waffle +boob +boobs +booty call +brown shower +brown showers +boner +bondage +bukake +bukkake +bullshit +bull shit +busty +clitoris +cock +cocks +cow girl +cow girls +cowgirl +cowgirls +crotch +cum +cumming +cuming +cunt +dick +dildo +deepthroat +deep throat +dog style +doggie style +doggiestyle +doggy style +doggystyle +dyke +erotic +erotism +fag +faggot +femdom +fingering +footjob +foot job +fuck +futanari +futanary +gangbang +gang bang +gokkun +golden shower +goldenshower +gay +handjob +hand job +hentai +hooker +horny +incest +jerkoff +jerk off +jizz +kinbaku +lesbian +masturbate +motherfucker +milf +muff +nigga +nigger +nigg +nipple +nipples +nude +nudes +orgy +panty +panties +penis +piss +playboy +porn +porno +pornography +pussy +rape +raping +rapist +s&m +s & m +sadism +scrotum +sex +semen +shemale +she male +shibari +shibary +shit +shota +slut +smegma +spunk +strip club +stripclub +suck +sucks +tit +tits +titties +titty +threesome +three some +throating +twat +vagina +wank +whore +xxx +xx +yaoi +yury \ No newline at end of file diff --git a/build/lib/better_profanity/utils.py b/build/lib/better_profanity/utils.py new file mode 100644 index 0000000..42463b8 --- /dev/null +++ b/build/lib/better_profanity/utils.py @@ -0,0 +1,95 @@ +import json +import os.path +from collections import defaultdict +from string import ascii_letters, digits +from typing import List, Set, Tuple + + +## GLOBAL VARIABLES ## +ALLOWED_CHARACTERS = set(ascii_letters) +ALLOWED_CHARACTERS.update(set(digits)) +ALLOWED_CHARACTERS.update({'@', '$', '*', '\"', '\''}) + + +def get_complete_path_of_file(filename: str) -> str: + """Join the path of the current directory with the input filename.""" + root = os.path.abspath(os.path.dirname(__file__)) + return os.path.join(root, filename) + + +def load_unicode_symbols(unicode_symbols_json: str = "alphabetic_unicode.json"): + """Load the unicode characters from categories Ll, Lu, Mc, Mn into `ALLOWED_CHARACTERS`.""" + # More about Unicode categories can be found at + # https://en.wikipedia.org/wiki/Template:General_Category_(Unicode) + with open(get_complete_path_of_file(unicode_symbols_json), "r") as json_file: + ALLOWED_CHARACTERS.update(json.load(json_file)) + + +def get_start_index_of_next_word(text: str, start_idx: int) -> int: + start_idx_of_next_word = len(text) + for index in iter(range(start_idx, len(text))): + if text[index] not in ALLOWED_CHARACTERS: + continue + start_idx_of_next_word = index + break + + return start_idx_of_next_word + + +def get_next_word_and_end_index(text: str, start_idx: int): + next_word = "" + index = start_idx + for index in iter(range(start_idx, len(text))): + char = text[index] + if char in ALLOWED_CHARACTERS: + next_word += char + continue + break + return next_word, index + + +def any_next_words_form_swear_word( + cur_word: str, text: str, words_indices: List[tuple], censor_words: Set[str] +) -> Tuple[bool, int]: + """Return True, and the end index of the word in the text, if any word formed in words_indices is in `CENSOR_WORDSET`.""" + full_word = cur_word.lower() + full_word_with_separators = cur_word.lower() + + # Check both words in the pairs + for index in iter(range(0, len(words_indices), 2)): + single_word, end_index = words_indices[index] + word_with_separators, _ = words_indices[index + 1] + if single_word == "": + continue + + full_word = "%s%s" % (full_word, single_word.lower()) + full_word_with_separators = "%s%s" % (full_word_with_separators, word_with_separators.lower()) + if full_word in censor_words or full_word_with_separators in censor_words: + return True, end_index + return False, -1 + + +def get_next_words(text: str, start_idx: int, num_of_next_words: int = 1) -> List[Tuple[str, int]]: + """ + Return a list of pairs of next words and next words included with separators, combined with their end indices. + For example: Word `hand_job` has next words pairs: `job`, `_job`. + """ + + # Find the starting index of the next word + start_idx_of_next_word = get_start_index_of_next_word(text, start_idx) + + # Return an empty string if there are no other words + if start_idx_of_next_word >= len(text) - 1: + return [("", start_idx_of_next_word), ("", start_idx_of_next_word)] + + # Combine the words into a list + next_word, end_index = get_next_word_and_end_index(text, start_idx_of_next_word) + + words = [ + (next_word, end_index), + ("%s%s" % (text[start_idx:start_idx_of_next_word], next_word), end_index) + ] + if num_of_next_words > 1: + words.extend(get_next_words(text, end_index, num_of_next_words - 1)) + + return words diff --git a/dist/better_profanity-0.3.1-py3-none-any.whl b/dist/better_profanity-0.3.1-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..990132ba1944f6b490c5fa9033bafd9bfd8668a1 GIT binary patch literal 39944 zcmeFacU%-n+ck_t$&xb&2q+mw36eyzL=hxO&WPlkMhPNWKnaSVNK_DzoEf4BG6E7L zN69dxVPJ-tuhCt1-Q9bi`+48@`;T80SDil9U0pNNUEO`v)dwAQ99(KFEUdFwvmAz6 zp4nfwMSvwy;Ex&jv$FN_vh^@`^KiAZbaC|Z7cw_@1WI#rK{x*%^Wq z1fqp5e$-a)?cp!fTUv}x5K?i~qDMOJ_F+OP~Xe*5=+Wj@GU=wt`Nct}gdpbXBdFzB2`P%vVmHr|-z~?EBi} zB7DE^D|BV8xUT;4TljmaEB8IC&htm(UW~qjpaYAd7og~Lx%85({iClXY~bUJ5omM3 zF=`Dc{Et!NKoNM1LXJSee#fXTpaANYfC8u+0Sch*U<8`g6oT$rTLJ^c2v9VKprLC^ z zi=8wlVy_}+@ji8;sIF40S?6GEK7%$WlU7>5&lpxQ;|MYPkvhzNO>JgB((7Z!jS+|D zhmA2Q4W+)8TiM5gMR)v$9FcuAEX*wAS07zvC2=F!lqwai*=qR+q_C)isR*{`hb(4dzjJ*&LgQ$ZZto)y`;e zd$-|s!XpMgG=-FPhg`Np5kf^ejui8#sBD%^L`)3DIoA0Q+&(!9E16|0lK_@^yMW~- z)u8zHCBNC}2ypwl-z;0dlo`TzmW@K@q&OjwstpNrXj8<Y|FC)1n1XCWva7>+E!^oT!g602BB#Pgu_K{0Q&@LNQ*OsYL96t1JIg zu(P0{hwZ3;Hhx`d4f7XvLO8Z~rtB&G<+eAIZvA#X=*quQC0)6`s*2;t5iC8b z8oxL5$)+!3JAwHb2)(WR7%(o6yDQVH!!?FKFUOW310dF(wrxc@67>duX^ zd+)+MVkhd%U1Im%kvq~Z+lc#_?Ry*UcebKd+^x2A#@tJ`qNdzzw(oVg4{SuWxS=*X z8r=0Z9yhp?ZFW?+@7j1MaVyyDT<2!8@wmplW6$Bny=j+ykGs>3<1TlmUA6v-z~<06>Bu^^t4z0n^7#+An9o@4Jz$2(Ez$PWhLq= z5>cJjYN9bKkO!l^ z{zD4nRD-Kn&0V@5bLd|GsEStz40&CtEZ9lxN;%dxu3*PLU=xnHecp#>3e=-u!>p7w!w$_q}oiGV$$~z zBfu%B-}d&Q0ci=NS%0~>OyJwBj8 zT>(zop184J?Q*q)6gw-1t-Z)0FXGqAm=m*Kls-5xA;z%t?MHVp|T z@MW5?w(z%bwQ#liOR{wRHCem!ZJ7)PSLyNzU~9a@RFzAjA| zsMwWUs*-U6dI7L5&2Yk$0So|oZ`Guz`Voy#QfbeQ?bOnmKT6KGN>KIVg+Gb{!14)5 zj+FewPRaW1a%7rc+8}fGyrq`sSl3j*8XjhbqXE;Q0xLg3fc}Z8YfKqRQpGiq5ro#_U-~A z+x+iL@2Go8Gm$?6+gnGd`iYHD5+-;Glx<3kQ0~XipV{7$fIkXfaucA~WPT@eXS==5 z0cz4OAUWWl!}CqiM_Vya871HioB(VqM&k`!oA9E0%0Aa7C=Y^09)p99Bs}inw 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ê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 DLB(p- literal 0 HcmV?d00001 diff --git a/setup.py b/setup.py index fc5fc39..0a450ba 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ long_description_content_type="text/markdown", url="https://github.com/snguyenthanh/better_profanity", classifiers=[ - "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "License :: OSI Approved :: MIT License", diff --git a/tests.py b/tests.py index 5f7c138..e60434a 100644 --- a/tests.py +++ b/tests.py @@ -21,6 +21,10 @@ def test_leaves_paragraphs_untouched(self): censored_text = profanity.censor(innocent_text) self.assertEqual(innocent_text, censored_text) + def test_empty_string(self): + censored_text = profanity.censor("") + self.assertEqual(censored_text, "") + def test_censorship_1(self): bad_text = "Dude, I hate shit. Fuck bullshit." censored_text = profanity.censor(bad_text) From 45e5edb6b4679e45678b4e0412ab5b586cf3f2de Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 24 Feb 2019 12:38:30 +0800 Subject: [PATCH 10/30] Fix typo on README.md --- README.md | 2 +- better_profanity.egg-info/PKG-INFO | 2 +- dist/better_profanity-0.3.1-py3-none-any.whl | Bin 39944 -> 39946 bytes dist/better_profanity-0.3.1.tar.gz | Bin 23443 -> 23440 bytes 4 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5c7fab9..0a67b22 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,7 @@ $ python tests.py ``` ## Versions -- [v0.3.1](https://github.com/snguyenthanh/better_profanity/releases/tag/0.3.1) - Remove unused dependencies. +- [v0.3.1](https://github.com/snguyenthanh/better_profanity/releases/tag/v0.3.1) - Remove unused dependencies. - [v0.3.0](https://github.com/snguyenthanh/better_profanity/releases/tag/0.3.0) - Add support for Unicode characters (Categories: Ll, Lu, Mc and Mn) [#2](https://github.com/snguyenthanh/better_profanity/pull/2). - [v0.2.0](https://github.com/snguyenthanh/better_profanity/releases/tag/0.2) - Bug fix + faster censoring - [v0.1.0](https://github.com/snguyenthanh/better_profanity/releases/tag/v0.1) - Initial release diff --git a/better_profanity.egg-info/PKG-INFO b/better_profanity.egg-info/PKG-INFO index 4815361..7553640 100644 --- a/better_profanity.egg-info/PKG-INFO +++ b/better_profanity.egg-info/PKG-INFO @@ -140,7 +140,7 @@ Description: # better_profanity ``` ## Versions - - [v0.3.1](https://github.com/snguyenthanh/better_profanity/releases/tag/0.3.1) - Remove unused dependencies. + - [v0.3.1](https://github.com/snguyenthanh/better_profanity/releases/tag/v0.3.1) - Remove unused dependencies. - [v0.3.0](https://github.com/snguyenthanh/better_profanity/releases/tag/0.3.0) - Add support for Unicode characters (Categories: Ll, Lu, Mc and Mn) [#2](https://github.com/snguyenthanh/better_profanity/pull/2). - [v0.2.0](https://github.com/snguyenthanh/better_profanity/releases/tag/0.2) - Bug fix + faster censoring - [v0.1.0](https://github.com/snguyenthanh/better_profanity/releases/tag/v0.1) - Initial release diff --git a/dist/better_profanity-0.3.1-py3-none-any.whl b/dist/better_profanity-0.3.1-py3-none-any.whl index 990132ba1944f6b490c5fa9033bafd9bfd8668a1..5cb5b1fd8e6258b1404c04847639472a6a0b6211 100644 GIT binary patch delta 3092 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ê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 DLB(p- From c881c916676715fb257ad7ccfa1cd6a37fe6c790 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 24 Feb 2019 12:46:50 +0800 Subject: [PATCH 11/30] v0.3.2 - Fix a typo in documentation --- README.md | 3 ++- better_profanity.egg-info/PKG-INFO | 5 +++-- better_profanity/__init__.py | 2 +- build/lib/better_profanity/__init__.py | 2 +- dist/better_profanity-0.3.1.tar.gz | Bin 23440 -> 0 bytes ...> better_profanity-0.3.2-py3-none-any.whl} | Bin 39946 -> 39970 bytes dist/better_profanity-0.3.2.tar.gz | Bin 0 -> 23474 bytes 7 files changed, 7 insertions(+), 5 deletions(-) delete mode 100644 dist/better_profanity-0.3.1.tar.gz rename dist/{better_profanity-0.3.1-py3-none-any.whl => better_profanity-0.3.2-py3-none-any.whl} (88%) create mode 100644 dist/better_profanity-0.3.2.tar.gz diff --git a/README.md b/README.md index 0a67b22..a8ced38 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,8 @@ $ python tests.py ``` ## Versions -- [v0.3.1](https://github.com/snguyenthanh/better_profanity/releases/tag/v0.3.1) - Remove unused dependencies. +- [v0.3.2](https://github.com/snguyenthanh/better_profanity/releases/tag/0.3.2) - Fix a typo in documentation. +- [v0.3.1](https://github.com/snguyenthanh/better_profanity/releases/tag/0.3.1) - Remove unused dependencies. - [v0.3.0](https://github.com/snguyenthanh/better_profanity/releases/tag/0.3.0) - Add support for Unicode characters (Categories: Ll, Lu, Mc and Mn) [#2](https://github.com/snguyenthanh/better_profanity/pull/2). - [v0.2.0](https://github.com/snguyenthanh/better_profanity/releases/tag/0.2) - Bug fix + faster censoring - [v0.1.0](https://github.com/snguyenthanh/better_profanity/releases/tag/v0.1) - Initial release diff --git a/better_profanity.egg-info/PKG-INFO b/better_profanity.egg-info/PKG-INFO index 7553640..d1f219a 100644 --- a/better_profanity.egg-info/PKG-INFO +++ b/better_profanity.egg-info/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: better-profanity -Version: 0.3.1 +Version: 0.3.2 Summary: A Python library to clean swear words (and their leetspeak) in strings Home-page: https://github.com/snguyenthanh/better_profanity Author: Son Nguyen Thanh @@ -140,7 +140,8 @@ Description: # better_profanity ``` ## Versions - - [v0.3.1](https://github.com/snguyenthanh/better_profanity/releases/tag/v0.3.1) - Remove unused dependencies. + - [v0.3.2](https://github.com/snguyenthanh/better_profanity/releases/tag/0.3.2) - Fix a typo in documentation. + - [v0.3.1](https://github.com/snguyenthanh/better_profanity/releases/tag/0.3.1) - Remove unused dependencies. - [v0.3.0](https://github.com/snguyenthanh/better_profanity/releases/tag/0.3.0) - Add support for Unicode characters (Categories: Ll, Lu, Mc and Mn) [#2](https://github.com/snguyenthanh/better_profanity/pull/2). - [v0.2.0](https://github.com/snguyenthanh/better_profanity/releases/tag/0.2) - Bug fix + faster censoring - [v0.1.0](https://github.com/snguyenthanh/better_profanity/releases/tag/v0.1) - Initial release diff --git a/better_profanity/__init__.py b/better_profanity/__init__.py index b4e57f1..56d7564 100644 --- a/better_profanity/__init__.py +++ b/better_profanity/__init__.py @@ -1,2 +1,2 @@ name = 'better_profanity' -__version__ = '0.3.1' +__version__ = '0.3.2' diff --git a/build/lib/better_profanity/__init__.py b/build/lib/better_profanity/__init__.py index b4e57f1..56d7564 100644 --- a/build/lib/better_profanity/__init__.py +++ b/build/lib/better_profanity/__init__.py @@ -1,2 +1,2 @@ name = 'better_profanity' -__version__ = '0.3.1' +__version__ = '0.3.2' diff --git a/dist/better_profanity-0.3.1.tar.gz b/dist/better_profanity-0.3.1.tar.gz deleted file mode 100644 index e8179fe6aa2008d7bf8b719547c47ef9bcff09da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23440 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 JwzB_R{0CznMil@6 delta 3363 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 diff --git a/dist/better_profanity-0.3.2.tar.gz b/dist/better_profanity-0.3.2.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..a5838a7c87f20c858d52f9330fdb2ab4a5ca7811 GIT binary patch literal 23474 zcmcG$2V7H0+cv%yL`6hd1?eavA|)st0?3LWprEL92py5$10jimh%6lhl&S(EA{{}b z1PHx_CPjhJLkW5hHqSW)ucmR= zLZUP!`xgajgdA2vyWo9)r0PTAFT^e{JMB*g^fX0Z#-+!JhL6iR6Fzc6WD$1VLk<<) zEkiC4dcEa4T3@3|W9BpPOVK-*ICNDK^4?FXJCN~&#>d{1^$ih{>6y7@Sp#KXPUt0J zP-`zK&CpE}PtkhoZ06^}TsR?yrrchE3*GFwR|rOqG*)d#UEgYPCRZ6}hLtr>Cq5cu z-qfGXt)^}*7olObD8&1&4FX+Th3*lwEi;Rjq+~@T*du;lq^%9*CnOi9yninVr}cd9 zpirq1U%%Y9ZuOh~{SD4teD|y@I`H#pw{eTHrIr{N_1TOTx7t(uOgkCxLp6>TK=7#5 z@v23)#yHNjwv$V^)%03p5PWJqEioHpPgR(CqyQ*N02CvDjub#KN_f<0-JzE)6m>mc zQZcA_Os`)#qRZW`zVL7Jb#gg_3!uPANs&uyhomn zUUC*Uczo~N7sgp(r{a1Y`+iQC-k+1vtD>u*$94Om*F#P*u2FTNK>o(@cA@(>76`^m zC4;tkt^Mq}y5bTgtN1>;;FA?uAQc7z z^AHT^8@`>uy3I|L9R!6W9m+Rds+;?rgI9v1T<~Fu;3FXq^rNJD=mFFHru#X3jL~?a zat16+g3t8x_{Wj{J3K1GB=c_}HJIPqphCVUN(A2s2MgJ#Yk-N5V3FPLrlCTZBT%dPdd)mV*pQ%~zH*WnvKpB^w*r3QWkcg~k;Z zwbXG_aLt7+sx_tgC2Vvo4t1Q4<;FS}vt_PuW08>RLlnj#Ywy4i;cK|f!iKs-6jQ8o zNzA87Q>@T$pE&YOKRhh?ojmnSAeub2$6rjFemH7;Jg%iOyAspoI>8psCUWo@(1{rqo1j0%Nxoc$39fQB z?F4VW4E_YdtxmIq*v0W0Jf4ly$v&E=`7qWr`_bUp74c^`jySa}ho?>03S1t9U}vK0 z88BY|tFwyxk&JiCDx{O}+E&$bC7UB!H%U!)`u`))u+gV(Xjtff*w8T6H{Z~>tAC8~ zwa3U4G_+$t1PzUtFalXtzl)%uqz_!zIIEAvYw+r);hVVheeg}E^v~m)*z^Z+O-J>g z;F_59WpGWu#9%hTRx#)euvtvZ2G}r0Z3BEKW{n8G8IwsQYs82U$!amf1hUdz%jhgN z#Jmt`Y6=MJo*Dj+&<#}Wnn6+@sZQ7cC%On6GXW8z|F3KjBP_HU&kwpXExAZxP2{%Ra3tZC@V>JWY`My2eAP~KqW{Y zM}~hQ1fIfM#0CwNF0oPrpi3;!h|wWtX{>1zUux)U6Js=rZV*E?*lv_lG@SLz@fw|a z!zW%WgoI}#FSv(WCM}qS^CvE7g-;|bNQY;{F9?Lw z;{E`qVKviLIn*!q;gpIoc@H#WHQ!_%lkkXnH2=o?<>cF?1CQ^t?5g&AYRj&=?-;oC@yYr}nY;|@<}Xn(sT{T+3T}OURRHF7u{W%h z_b72nnK?$aW@fT4ERz?wjeBve{Uf;X^{i>o^ldNKs%j21T*9?bRp8X^*KC;z_ZE=K z$4_tL5)Rm=?9*3}ddr0i-=%B2bSb^=&>s_3rn2X8T-R}|c#zSju;aVkyvFF}0%PQIzB#>YHNS@XNN#$K`I_O3 zkG|HlpNc-w7c9biZg~1gSkp)H=d+#?mhLxle!VKQCko$9lLg_iEErRCLcG~UNP z%>MHeor+`cH)m!nmoZR&3Xl|lC5&j*5(jnlcVVprWt+KMGzeNNDQc=?r|4kC{yoVr z&Y5;Q?VtZiD|G%SRCajC7-S2LO5#p+CVm+)&#G#fKh({d!h?109e$NFYQfU+^s{^4 z@SCFRpolsSsKmA@@#6x^zoZ`%?8l7zbAtUj<02M*PPz+>RGy~6qFX*N>aR4P!%HxT z4iIgvd2VajaTzf8BH61;SMmQRIjW_)-~y9Hap{%6{nP)o@AZ0`%v|_zfg|7(qpn*v5PbjpU^p{I$DMTJbw=i$B%N&Tyz5^rPR2Q~EGcF< zwx4@lb=CXv{QhpH6c!oI2XC(rmOo43xHE9>p{d5Lh<8pWWhLJH4@KY5I&LU|M>DAx zsMRFy8KWrY5MJF~a#?p@p4PNmc~eS~@c_HCsm-{@3J}8e(A45qe!dwtYv)TjB86nM z_7$YdS8jZ|mzR+^8EF_l9F6G7=YO5$>sQ|6#_#IzV^wE7>h`a?F;@KDX%|_^m}|~! zKvLhTatxICsK-_S`kEmXEifcCC5H5TS9&W}5;HN7IHIMtq=U`U@)aiaReoltEZAFw zLr?o>x%)C8c^44Avcvr`O51lBH5arjYO6VP7q$3y-sP7(=l}9$@H{j>Kd(I{B^%+( zO6ntgK15jvu?T?@TM8Rgx9x|m1WMjG7stHo>w_y#Yia4PK09v_0VS>$Hmhz2XsN9& zV0N?Q-7IX_M!@owa|s)~_v1*IjV@Wh9Jwks;|aq*dy#Y0DK|&`VcdR$utA~g*-vDK4-ujd{F9k9GX8KK5F9+V1V^=^8UweS z59qb*Fi{|gtKF@ld@H`CZ zb&7fir_~_91pg1@UoQIN>VG49mqrZ3#TEgH#AKaqUE!Uac$$GWJTDt@uOD>j|6UH; z$+_5C%4)Fs#}{BiR2H%qvlp5Bt;4b-=ed~Zuz_3ln2rffxnb=f;#aC3gMbeI`Gr^# zA$_3VX$HjPH;7X^IfL$^8zwr?B!kSHLC3#Sux^9dkqI4~VQ?jTP2yxhw#xlr1Uofq z!CeC`w9V6pen!*W(A|+UKtv6?3vGP6@fX$EsW3nkOu93{#mHt2=%X}4TxBrgG<8oB z*;ejDhtu?k{_pjdiW0V$vuS~O2+O6Sz!@7_~CU*cB z(vYeFXW!8#iu7gfl8`}+;D+K2=y8M0kn&dbCI8rMzHAtC9-2Y_J_mTG9hqM2PR{kS zw2OE}ouFv;%&aEN|DOJs$bdguu@&7Gwrn+EjDb!~G<#82)AB{U(toDLM`$`YqupWr zslIL2b`DI~QY(D;&b04yFyDWsE@ezD^{3O?w&=PtJ%;_7a`b=?EhyG;N;8 zt7r%5v!fYw_8wel&agwhBmFrhYVagmFk`_GB!@5vXxcxXoPHjbYsr|E3A;=r9IgF__)qi= zV_vNc_`DRvZSA>ifl0AB()TFxox{yaG5iN(b`@E zZM&^S#vI}jV7;QsZ5JfdMt%^B?>nIZKe3}uWbPB#CFz5XA{(RHP(OprJu13C$KZUlgR4{&|1;+X}gNlcHZxt&@h;D8p*xVbl#Wo<_!Z;%AIz zT8mm^*ji%XsxWEXf;Aax>Gx9U7bRaB92^J&=3t|-eQOU_Otqk8C{UMb+ z%qYAfJ%*fhvRaoZ9^!fdWTpBZx}bj`A^matK)sXWuKaCR2Kw*HGwF};-KG4qKFt3AMaRpVOLF7P3k`vI+t$BWX$F2H0qoV|`^Q%mJi>SmuZT&tBmA3^GIE>pf0#foMMX%ZjOIfnFD+)G)j35R=+Ip>B7jW(j4 zrq;@F{-)$u*GgrvU(9e094LFd0SDR+e}hBK4zJ1~rx$@L0drRqE~T~FD26tBk$#_R zI^C%#mNal*-0{7!ozW%dC}@A0LRQkd-bTgvR?WW`00Ms?Rtz^{Yg+g)@)M#cfZz`(f7fz=dj?x61EBS zevm=^FXof?kJjkylMZ?e7iFB@810-!xiWEa0mC#C6YS2drv z0_pN+e54yNiwCvPBK-$toJu~+$j`(vbhff0#?_G1%Tucx*Ncl!_AB$SlbK~ zsM36xpVntvL40ht;Q^a69MeWsh?Is3QZeJ&WodywIliv$6) zI2gxj?-Mu=VOT*^Y9hIq9d&=YNT=prxcepi4Pn^3CdzYi8pGi=4brL62?vPc1%+W* zO=i!@&ln!FX*QjjtJ-M5B3>Ay`aIH9rw4j5z*$Uj^Y!db!R%OAytjbP#G+1%s<3V5 zF4t7IFDYYkaAQ>C;z~@T+`giDiAyLkGES%EhOqln;jEn9S}nKsT6?wI8kg>FwRK4o zuhnbqX7=q>Y{F~FyT!#9xXIhcf}n@$R1bhM9~YtOhaDUBbfX3ZKi=4vcWo)|oWXA? ziVCb$_#v9w0ea?RI;x)DS`}x~sQTiUTVc@RcPYmylXqqLNDUtzTAh^f_;7{U{$)sM zm%7RPD{=4JNs1RcyAtgA+pp-ANB&wi8+-J8%)N8>Z{QjTz%!RkH6xQwX3hBsbS|nU z4M4?nDaLx6C26a+isP+bpJV>t7MNi(>|>xF3tl}jb7=ydXU4B+Rz~(onqR|J-+=Ef_0mMI310~gpa?67n|I)F& zq0H`^$@-Ewmrs(pqOM_@Fugl|(V^X`%&y2}eNddsN$$_u7hXVr4XQiBfj;Q(J`D_J zU~q|i!$7JP=2n{IRZ^-jsyn{Vq1~d)F3)7WPy8h)i5H+Nmn`fqraP8w9QW)*_@~%s zhr-Xq;;2X9nYi^M=nUM#5w#55#F5Nf6)%JVUGe)I+BZt=d`;FX#b25xrHSj35`|G6 z@ptUo(@X8NP1du-U*1ki%N2jkMo-2C9swoeJdPlea1KWVl5pln;E7iFTWNFg3Jsyu zOS%^lg&o`D3+&qOmfBr3@k$Uk(M}3FuX`b0SfnkU)voIN z3=Uvqj*;^lRtlRrdfWuzZSjhB?OdgHEGFwu#JPS;8WYqtj1?AWjSshNZz!=t8?U>G zbIEfUW{Tr$y*7&gsf_`k}*t08j%*Rc!zqHb>e&d@TgSB#J&k#LZU(Q5t zXtpl?iHTafT)LHSIVyUfl}{`t+PjS}KQj8hX6vOa+r&Q4`A@dBeV*HY;#{BS@v517 zo~>Ws*z)y_@?E>!Rj$IYk3*^Bn^vDgUsi;@tjP6)ym0!%mg;g>Axm`(m0_3wA}}Tc z(}%qIjbVhyz#r8z1jmjXA3Dek3Jwu%Xo#D%((=HaQ#f;y*ZlBtj)Tnf$01ww4FQu@ z${sjgg)_%_#||AgJiwg!IHbM4!FqDUVr@igZA5)-M0suGbf)mCp=Zp|?k{@MZO*HA zykodsWQ_*R`#i2APb(Lao_>AsF67WGE0{;7a$(n%^*2W!6Y=NtH&60+N8jH($=@A) z*NHzTVIm`$-hCt6ofUVx;fCqf_!`h4bWo5fn_slyRirBTn%Ky1VhX>By-*h;U(Mg$ z+MGOGV0EZq@<74fmL|jd#wOQu^mxzF`R=22-A9wUkGgfgA9ZTDG$JjgAT9RdiWvE_ znDJ$?Pg2gz--<%64L4wyCkejQVw1DJ9_x({&5-tHNGmg>nHkd140*>4dDDzQbk2Zv z4-^G#%jzTM7l(m6DY>Bkq4@bXfw4l_$U<4*o~`7(tsN{+P9Yf}t4-yV{cHJXiKuu58U)S(l%U_pR*T%$J$-ZHU%DL)PMCpAYVV5?E$@ z)f6nl2TVE6e~THF8-f{VAS_O9`QU<-z^W_^!-^Fb)z|H(9Q}*jozM{i!ZpFPNF0@G zYD>D2;P?cCI*iIZnW6Sa!B;V0m?rpm6pojBN>QfK`vDWfcVLJ!YjFSf0^ilioX|KZw;`4*52_qpr;SQMgF1yGNb{`LbJqe1~YtAwUcX1@c$wu zV(#yGhY;)c(>V&_~?uPh@lo0+ZDd00w|1LvZGYrmBuLdUHL&Tse&Tv-Gb5!srp29?lu+8_aMB zrBALMgnw^{A^QI*A=dlb>*g#;5}T#&RmsrKTCCZsJ=zOENrH@4j&=TPo24;~X1Z!|k;F#n{!Z0-%kLWC9%8TvYrASM;0>U>fQ+_|{Z+PG-zej5 z!#@`MbUm2B79_EiM?_T;^(m>!_QtB=K#Qu6;^|w$x&!SM4)B0hCC{s3W7# zj3TbhQq(6-{#Z-!)xfEJi=oWfjEpM7eF6B9V-%6Vg_ZVs`cq7cp`5c!8P0peE`}3k z#_^LAbBZg0%q?7}8J>G*+DYcUKDhI|rDk~YooV~$U@qZZ+IMN~-DDj3jIgR^c+8z? z+fx}a7l8tIz#Uml$cuO>9co0VC167s574QJU|)OFRDBUIxl7S+n({B=FX`0$&R+D! z2r{>o)X^!reO~o?xc;5#yU)p|g#UMRYW!Ja^~q3wp`&Y>r}#fy1RCF&Hu+92zk}tW z1vhxIVZ!{H;bC{Ct)GL>goX*}1VyR`s4^j%S})=+GcxP~Squu~y#rQwgY(dsntJwK z`J>SG-i`s{CuO(#UZM8CG}EFr#TC;i%^&IrJc`H4@A#EujhvRfAX z?DhJ>aM>9BqpvE@Fhjoq@RuWvao!&d)R=06TN0Wur-*9?YV4T|#^{1}kluf$NAwNZ zGd`RLnrMSd<8gMvFG06z*Y%ECGDc_G{>MQ5MEk*_HRIag>I9r$u6lqR69VTaGPR=D z_$~cOrScJXfA)#h^EKm6w=Hi>sV9(SZ&10?N;wHJK_Q^CJc<_X|_b}V5ll)hOfR8l6zeF+aS$jv^FWlcN{QFzg zkGt5~LAA(>62L1Gy!$1B_WfZLiWzCM61(O;ibj{_BkYafEc&M3JU}mGzecY$l=L;e z>1zay?Be~A#>LZDPt~8?O~ry%>h-~onD&vPcemN+f&!3R>u+c3`23fxbQT!)?qjs+ zvQAyt{WK4Er88PeF9el8@hdOA&ni>`_bYu^^BcPB{9%D-Xq`2m{B*+@sq?{_b*Omz zVO#1IyPI5IEXPcop9aZZdz*4rt2;K`p-oxZE{)5p^O%YK(~a{;o%n`au9}zD{1f%3 z9=?c>?|S378*IbaSdtk&FXG5G|B$t;{HeF+F_GyrJ&l*UCb;HTj!FAHtx`gYr(45% zq@*2RBuJ+P+A6HI6r1%jte)n=s?5o-Tl;1XRG(s1W@lJ;v}@KA{A0e3x4}ZewH

g7;BUpVA&#PFyN8Fcqyp;) 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 Date: Sun, 24 Feb 2019 13:37:36 +0800 Subject: [PATCH 12/30] Remove build executables --- better_profanity.egg-info/PKG-INFO | 168 ---------------- better_profanity.egg-info/SOURCES.txt | 12 -- .../dependency_links.txt | 1 - better_profanity.egg-info/top_level.txt | 1 - build/lib/better_profanity/__init__.py | 2 - .../better_profanity/alphabetic_unicode.json | 1 - build/lib/better_profanity/profanity.py | 179 ------------------ .../better_profanity/profanity_wordlist.txt | 140 -------------- build/lib/better_profanity/utils.py | 95 ---------- dist/better_profanity-0.3.2-py3-none-any.whl | Bin 39970 -> 0 bytes dist/better_profanity-0.3.2.tar.gz | Bin 23474 -> 0 bytes 11 files changed, 599 deletions(-) delete mode 100644 better_profanity.egg-info/PKG-INFO delete mode 100644 better_profanity.egg-info/SOURCES.txt delete mode 100644 better_profanity.egg-info/dependency_links.txt delete mode 100644 better_profanity.egg-info/top_level.txt delete mode 100644 build/lib/better_profanity/__init__.py delete mode 100644 build/lib/better_profanity/alphabetic_unicode.json delete mode 100644 build/lib/better_profanity/profanity.py delete mode 100644 build/lib/better_profanity/profanity_wordlist.txt delete mode 100644 build/lib/better_profanity/utils.py delete mode 100644 dist/better_profanity-0.3.2-py3-none-any.whl delete mode 100644 dist/better_profanity-0.3.2.tar.gz diff --git a/better_profanity.egg-info/PKG-INFO b/better_profanity.egg-info/PKG-INFO deleted file mode 100644 index d1f219a..0000000 --- a/better_profanity.egg-info/PKG-INFO +++ /dev/null @@ -1,168 +0,0 @@ -Metadata-Version: 2.1 -Name: better-profanity -Version: 0.3.2 -Summary: A Python library to clean swear words (and their leetspeak) in strings -Home-page: https://github.com/snguyenthanh/better_profanity -Author: Son Nguyen Thanh -Author-email: thanhson16198@gmail.com -License: UNKNOWN -Description: # better_profanity - *A Python library to clean swear words (and their leetspeak) in strings* - - [![release](https://img.shields.io/badge/dynamic/json.svg?label=release&url=https%3A%2F%2Fpypi.org%2Fpypi%2Fbetter-profanity%2Fjson&query=%24.info.version&colorB=blue)](https://github.com/snguyenthanh/better_profanity/releases/latest) - [![Build Status](https://travis-ci.com/snguyenthanh/better_profanity.svg?branch=master)](https://travis-ci.com/snguyenthanh/better_profanity) - ![python](https://img.shields.io/badge/python-3.5%2B-blue.svg) - [![license](https://img.shields.io/github/license/mashape/apistatus.svg?style=popout)](https://github.com/snguyenthanh/better_profanity/blob/master/LICENSE) - - - Inspired from package [profanity](https://github.com/ben174/profanity) of [Ben Friedland](https://github.com/ben174), this library is much faster than the original one, by using string comparison instead of regex. - - It supports [modified spellings](https://en.wikipedia.org/wiki/Leet) (such as `p0rn`, `h4ndjob` and `handj0b`). - - ## Requirements - To make use of Python static typing, this package only works with `Python 3.5+`. - - ## Installation - ``` - $ pip install better_profanity - ``` - - ## Unicode characters - - Only Unicode characters from categories `Ll`, `Lu`, `Mc` and `Mn` are added. More on Unicode categories can be found [here][unicode category link]. - - [unicode category link]: https://en.wikipedia.org/wiki/Template:General_Category_(Unicode) - - However, this library has not supported all languages yet, such as *Chinese*. - - ## Usage - By default, on the first `.censor()` call, function `.load_censor_words()` generates all possible [leetspeak](https://en.wikipedia.org/wiki/Leet) words, from [profanity_wordlist.txt](./better_profanity/profanity_wordlist.txt), to be used to compare against the input texts. The full mapping of the library can be found in [profanity.py](./better_profanity/profanity.py#L9-L18). - - For example, the word `handjob` would be loaded into: - ``` - 'h@ndjob', 'handj0b', 'handj@b', 'h*ndj*b', 'h*ndjob', 'h@ndj0b', 'h@ndj*b', 'h4ndj*b', - 'h@ndj@b', 'handjob', 'h4ndj0b', 'h4ndjob', 'h4ndj@b', 'h*ndj0b', 'handj*b', 'h*ndj@b' - ``` - - This set of words will be stored in memory (~5MB+). - - ### 1. Censor swear words from a text - By default, `profanity` replaces each swear words with 4 asterisks `****`. - - ``` - from better_profanity import profanity - - if __name__ == "__main__": - text = "You p1ec3 of sHit." - - censored_text = profanity.censor(text) - print(censored_text) - # You **** of ****. - ``` - - ### 2. Censor doesn't care about word dividers - The function `.censor()` also hide words separated not just by an empty space ` ` but also other dividers, such as `_`, `,` and `.`. Except for `@, $, *, ", '`. - - ``` - from better_profanity import profanity - - if __name__ == "__main__": - text = "...sh1t...hello_cat_fuck,,,,123" - - censored_text = profanity.censor(text) - print(censored_text) - # "...****...hello_cat_****,,,,123" - ``` - - ### 3. Censor swear words with custom character - 4 instances of the character in second parameter in `.censor()` will be used to replace the swear words. - - ``` - from better_profanity import profanity - - if __name__ == "__main__": - text = "You p1ec3 of sHit." - - censored_text = profanity.censor(text, '-') - print(censored_text) - # You ---- of ----. - ``` - - ### 4. Check if the string contains any swear words - Function `.contains_profanity()` return `True` if any words in the given string has a word existing in the wordlist. - - ``` - from better_profanity import profanity - - if __name__ == "__main__": - dirty_text = "That l3sbi4n did a very good H4ndjob." - - profanity.contains_profanity(dirty_text) - # True - ``` - - ### 5. Censor swear words with a custom wordlist - Function `.load_censor_words()` takes a `List` of strings as censored words. - The provided list will replace the default wordlist. - - ``` - from better_profanity import profanity - - if __name__ == "__main__": - custom_badwords = ['happy', 'jolly', 'merry'] - profanity.load_censor_words(custom_badwords) - - print(profanity.contains_profanity("Fuck you!")) - # Fuck you - - print(profanity.contains_profanity("Have a merry day! :)")) - # Have a **** day! :) - ``` - - ### 6. Censor Unicode characters - No extra steps needed! - - ``` - from better_profanity import profanity - - if __name__ == "__main__": - bad_text = "Эффекти́вного противоя́дия от я́да фу́гу не существу́ет до сих пор" - profanity.load_censor_words(["противоя́дия"]) - - censored_text = profanity.censor(text) - print(censored_text) - # Эффекти́вного **** от я́да фу́гу не существу́ет до сих пор - ``` - - ## Testing - ``` - $ python tests.py - ``` - - ## Versions - - [v0.3.2](https://github.com/snguyenthanh/better_profanity/releases/tag/0.3.2) - Fix a typo in documentation. - - [v0.3.1](https://github.com/snguyenthanh/better_profanity/releases/tag/0.3.1) - Remove unused dependencies. - - [v0.3.0](https://github.com/snguyenthanh/better_profanity/releases/tag/0.3.0) - Add support for Unicode characters (Categories: Ll, Lu, Mc and Mn) [#2](https://github.com/snguyenthanh/better_profanity/pull/2). - - [v0.2.0](https://github.com/snguyenthanh/better_profanity/releases/tag/0.2) - Bug fix + faster censoring - - [v0.1.0](https://github.com/snguyenthanh/better_profanity/releases/tag/v0.1) - Initial release - - ## Contributing - Please read [CONTRIBUTING.md](./CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to us. - - ## License - This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details - - ## Special thanks to - - [Andrew Grinevich](https://github.com/Derfirm) - Add support for Unicode characters. - - ## Acknowledgments - - [Ben Friedland](https://github.com/ben174) - For the inspiring package [profanity](https://github.com/ben174/profanity). - -Platform: UNKNOWN -Classifier: Programming Language :: Python :: 3.5 -Classifier: Programming Language :: Python :: 3.6 -Classifier: Programming Language :: Python :: 3.7 -Classifier: License :: OSI Approved :: MIT License -Classifier: Operating System :: OS Independent -Requires-Python: >3.6 -Description-Content-Type: text/markdown diff --git a/better_profanity.egg-info/SOURCES.txt b/better_profanity.egg-info/SOURCES.txt deleted file mode 100644 index f2df9bd..0000000 --- a/better_profanity.egg-info/SOURCES.txt +++ /dev/null @@ -1,12 +0,0 @@ -MANIFEST.in -README.md -setup.py -better_profanity/__init__.py -better_profanity/alphabetic_unicode.json -better_profanity/profanity.py -better_profanity/profanity_wordlist.txt -better_profanity/utils.py -better_profanity.egg-info/PKG-INFO -better_profanity.egg-info/SOURCES.txt -better_profanity.egg-info/dependency_links.txt -better_profanity.egg-info/top_level.txt \ No newline at end of file diff --git a/better_profanity.egg-info/dependency_links.txt b/better_profanity.egg-info/dependency_links.txt deleted file mode 100644 index 8b13789..0000000 --- a/better_profanity.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/better_profanity.egg-info/top_level.txt b/better_profanity.egg-info/top_level.txt deleted file mode 100644 index 2a46797..0000000 --- a/better_profanity.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -better_profanity diff --git a/build/lib/better_profanity/__init__.py b/build/lib/better_profanity/__init__.py deleted file mode 100644 index 56d7564..0000000 --- a/build/lib/better_profanity/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -name = 'better_profanity' -__version__ = '0.3.2' diff --git a/build/lib/better_profanity/alphabetic_unicode.json b/build/lib/better_profanity/alphabetic_unicode.json deleted file mode 100644 index 8e75875..0000000 --- a/build/lib/better_profanity/alphabetic_unicode.json +++ /dev/null @@ -1 +0,0 @@ -["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "\u00b5", "\u00df", "\u00e0", "\u00e1", "\u00e2", "\u00e3", "\u00e4", "\u00e5", "\u00e6", "\u00e7", "\u00e8", "\u00e9", "\u00ea", "\u00eb", "\u00ec", "\u00ed", "\u00ee", "\u00ef", "\u00f0", "\u00f1", "\u00f2", "\u00f3", "\u00f4", "\u00f5", "\u00f6", "\u00f8", "\u00f9", "\u00fa", "\u00fb", "\u00fc", "\u00fd", "\u00fe", "\u00ff", "\u0101", "\u0103", "\u0105", "\u0107", "\u0109", "\u010b", "\u010d", "\u010f", "\u0111", "\u0113", "\u0115", "\u0117", "\u0119", "\u011b", "\u011d", "\u011f", "\u0121", "\u0123", "\u0125", "\u0127", "\u0129", "\u012b", "\u012d", "\u012f", "\u0131", "\u0133", "\u0135", "\u0137", "\u0138", "\u013a", "\u013c", "\u013e", "\u0140", "\u0142", "\u0144", "\u0146", "\u0148", "\u0149", "\u014b", "\u014d", "\u014f", "\u0151", "\u0153", "\u0155", "\u0157", "\u0159", "\u015b", "\u015d", "\u015f", "\u0161", "\u0163", "\u0165", "\u0167", "\u0169", "\u016b", "\u016d", "\u016f", "\u0171", "\u0173", "\u0175", "\u0177", "\u017a", "\u017c", "\u017e", "\u017f", "\u0180", "\u0183", "\u0185", "\u0188", "\u018c", "\u018d", "\u0192", "\u0195", "\u0199", "\u019a", "\u019b", "\u019e", "\u01a1", "\u01a3", "\u01a5", "\u01a8", "\u01aa", "\u01ab", "\u01ad", "\u01b0", "\u01b4", "\u01b6", "\u01b9", "\u01ba", "\u01bd", "\u01be", "\u01bf", "\u01c6", "\u01c9", "\u01cc", "\u01ce", "\u01d0", "\u01d2", "\u01d4", "\u01d6", "\u01d8", "\u01da", "\u01dc", "\u01dd", "\u01df", "\u01e1", "\u01e3", "\u01e5", "\u01e7", "\u01e9", "\u01eb", "\u01ed", "\u01ef", "\u01f0", "\u01f3", "\u01f5", "\u01f9", "\u01fb", "\u01fd", "\u01ff", "\u0201", "\u0203", "\u0205", "\u0207", "\u0209", "\u020b", "\u020d", "\u020f", "\u0211", "\u0213", "\u0215", "\u0217", "\u0219", "\u021b", "\u021d", "\u021f", "\u0221", "\u0223", "\u0225", "\u0227", "\u0229", "\u022b", "\u022d", "\u022f", "\u0231", "\u0233", "\u0234", "\u0235", "\u0236", "\u0237", "\u0238", "\u0239", "\u023c", "\u023f", "\u0240", "\u0242", "\u0247", "\u0249", "\u024b", "\u024d", "\u024f", "\u0250", "\u0251", "\u0252", "\u0253", "\u0254", "\u0255", "\u0256", "\u0257", "\u0258", "\u0259", "\u025a", "\u025b", "\u025c", "\u025d", "\u025e", "\u025f", "\u0260", "\u0261", "\u0262", "\u0263", "\u0264", "\u0265", "\u0266", "\u0267", "\u0268", "\u0269", "\u026a", "\u026b", "\u026c", "\u026d", "\u026e", "\u026f", "\u0270", "\u0271", "\u0272", "\u0273", "\u0274", "\u0275", "\u0276", "\u0277", "\u0278", "\u0279", "\u027a", "\u027b", "\u027c", "\u027d", "\u027e", "\u027f", "\u0280", "\u0281", "\u0282", "\u0283", "\u0284", "\u0285", "\u0286", "\u0287", "\u0288", "\u0289", "\u028a", "\u028b", "\u028c", "\u028d", "\u028e", "\u028f", "\u0290", "\u0291", "\u0292", "\u0293", "\u0295", "\u0296", "\u0297", "\u0298", "\u0299", "\u029a", "\u029b", "\u029c", "\u029d", "\u029e", "\u029f", "\u02a0", "\u02a1", "\u02a2", "\u02a3", "\u02a4", "\u02a5", "\u02a6", "\u02a7", "\u02a8", "\u02a9", "\u02aa", "\u02ab", "\u02ac", "\u02ad", "\u02ae", "\u02af", "\u0371", "\u0373", "\u0377", "\u037b", "\u037c", "\u037d", "\u0390", "\u03ac", "\u03ad", "\u03ae", "\u03af", "\u03b0", "\u03b1", "\u03b2", "\u03b3", "\u03b4", "\u03b5", "\u03b6", "\u03b7", "\u03b8", "\u03b9", "\u03ba", "\u03bb", "\u03bc", "\u03bd", "\u03be", "\u03bf", "\u03c0", "\u03c1", "\u03c2", "\u03c3", "\u03c4", "\u03c5", "\u03c6", "\u03c7", "\u03c8", "\u03c9", "\u03ca", "\u03cb", "\u03cc", "\u03cd", "\u03ce", "\u03d0", "\u03d1", "\u03d5", "\u03d6", "\u03d7", "\u03d9", "\u03db", "\u03dd", "\u03df", "\u03e1", "\u03e3", "\u03e5", "\u03e7", "\u03e9", "\u03eb", "\u03ed", "\u03ef", "\u03f0", "\u03f1", "\u03f2", "\u03f3", "\u03f5", "\u03f8", "\u03fb", "\u03fc", "\u0430", "\u0431", "\u0432", "\u0433", "\u0434", "\u0435", "\u0436", "\u0437", "\u0438", "\u0439", "\u043a", "\u043b", "\u043c", "\u043d", "\u043e", "\u043f", "\u0440", "\u0441", "\u0442", "\u0443", "\u0444", "\u0445", "\u0446", "\u0447", "\u0448", "\u0449", "\u044a", "\u044b", "\u044c", "\u044d", "\u044e", "\u044f", "\u0450", "\u0451", "\u0452", "\u0453", "\u0454", "\u0455", "\u0456", "\u0457", "\u0458", "\u0459", "\u045a", "\u045b", "\u045c", "\u045d", "\u045e", "\u045f", "\u0461", "\u0463", "\u0465", "\u0467", "\u0469", "\u046b", "\u046d", "\u046f", "\u0471", "\u0473", "\u0475", "\u0477", "\u0479", "\u047b", "\u047d", "\u047f", "\u0481", "\u048b", "\u048d", "\u048f", "\u0491", "\u0493", "\u0495", "\u0497", "\u0499", "\u049b", "\u049d", "\u049f", "\u04a1", "\u04a3", "\u04a5", "\u04a7", "\u04a9", "\u04ab", "\u04ad", "\u04af", "\u04b1", "\u04b3", "\u04b5", "\u04b7", "\u04b9", "\u04bb", "\u04bd", "\u04bf", "\u04c2", "\u04c4", "\u04c6", "\u04c8", "\u04ca", "\u04cc", "\u04ce", "\u04cf", "\u04d1", "\u04d3", "\u04d5", "\u04d7", "\u04d9", "\u04db", "\u04dd", "\u04df", "\u04e1", "\u04e3", "\u04e5", "\u04e7", "\u04e9", "\u04eb", "\u04ed", "\u04ef", "\u04f1", "\u04f3", "\u04f5", "\u04f7", "\u04f9", "\u04fb", "\u04fd", "\u04ff", "\u0501", "\u0503", "\u0505", "\u0507", "\u0509", "\u050b", "\u050d", "\u050f", "\u0511", "\u0513", "\u0515", "\u0517", "\u0519", "\u051b", "\u051d", "\u051f", "\u0521", "\u0523", "\u0525", "\u0527", "\u0529", "\u052b", "\u052d", "\u052f", "\u0561", "\u0562", "\u0563", "\u0564", "\u0565", "\u0566", "\u0567", "\u0568", "\u0569", "\u056a", "\u056b", "\u056c", "\u056d", "\u056e", "\u056f", "\u0570", "\u0571", "\u0572", "\u0573", "\u0574", "\u0575", "\u0576", "\u0577", "\u0578", "\u0579", "\u057a", "\u057b", "\u057c", "\u057d", "\u057e", "\u057f", "\u0580", "\u0581", "\u0582", "\u0583", "\u0584", "\u0585", "\u0586", "\u0587", "\u13f8", "\u13f9", "\u13fa", "\u13fb", "\u13fc", "\u13fd", "\u1c80", "\u1c81", "\u1c82", "\u1c83", "\u1c84", "\u1c85", "\u1c86", "\u1c87", "\u1c88", "\u1d00", "\u1d01", "\u1d02", "\u1d03", "\u1d04", "\u1d05", "\u1d06", "\u1d07", "\u1d08", "\u1d09", "\u1d0a", "\u1d0b", "\u1d0c", "\u1d0d", "\u1d0e", "\u1d0f", "\u1d10", "\u1d11", "\u1d12", "\u1d13", "\u1d14", "\u1d15", "\u1d16", "\u1d17", "\u1d18", "\u1d19", "\u1d1a", "\u1d1b", "\u1d1c", "\u1d1d", "\u1d1e", "\u1d1f", "\u1d20", "\u1d21", "\u1d22", "\u1d23", "\u1d24", "\u1d25", "\u1d26", "\u1d27", "\u1d28", "\u1d29", "\u1d2a", "\u1d2b", "\u1d6b", "\u1d6c", "\u1d6d", "\u1d6e", "\u1d6f", "\u1d70", "\u1d71", "\u1d72", "\u1d73", "\u1d74", "\u1d75", "\u1d76", "\u1d77", "\u1d79", "\u1d7a", "\u1d7b", "\u1d7c", "\u1d7d", "\u1d7e", "\u1d7f", "\u1d80", "\u1d81", "\u1d82", "\u1d83", "\u1d84", "\u1d85", "\u1d86", "\u1d87", "\u1d88", "\u1d89", "\u1d8a", "\u1d8b", "\u1d8c", "\u1d8d", "\u1d8e", "\u1d8f", "\u1d90", "\u1d91", "\u1d92", "\u1d93", "\u1d94", "\u1d95", "\u1d96", "\u1d97", "\u1d98", "\u1d99", "\u1d9a", "\u1e01", "\u1e03", "\u1e05", "\u1e07", "\u1e09", "\u1e0b", "\u1e0d", "\u1e0f", "\u1e11", "\u1e13", "\u1e15", "\u1e17", "\u1e19", "\u1e1b", "\u1e1d", "\u1e1f", "\u1e21", "\u1e23", "\u1e25", "\u1e27", "\u1e29", "\u1e2b", "\u1e2d", "\u1e2f", "\u1e31", "\u1e33", "\u1e35", "\u1e37", "\u1e39", "\u1e3b", "\u1e3d", "\u1e3f", "\u1e41", "\u1e43", "\u1e45", "\u1e47", "\u1e49", "\u1e4b", "\u1e4d", "\u1e4f", "\u1e51", "\u1e53", "\u1e55", "\u1e57", "\u1e59", "\u1e5b", "\u1e5d", "\u1e5f", "\u1e61", "\u1e63", "\u1e65", "\u1e67", "\u1e69", "\u1e6b", "\u1e6d", "\u1e6f", "\u1e71", "\u1e73", "\u1e75", "\u1e77", "\u1e79", "\u1e7b", "\u1e7d", "\u1e7f", "\u1e81", "\u1e83", "\u1e85", "\u1e87", "\u1e89", "\u1e8b", "\u1e8d", "\u1e8f", "\u1e91", "\u1e93", "\u1e95", "\u1e96", "\u1e97", "\u1e98", "\u1e99", "\u1e9a", "\u1e9b", "\u1e9c", "\u1e9d", "\u1e9f", "\u1ea1", "\u1ea3", "\u1ea5", "\u1ea7", "\u1ea9", "\u1eab", "\u1ead", "\u1eaf", "\u1eb1", "\u1eb3", "\u1eb5", "\u1eb7", "\u1eb9", "\u1ebb", "\u1ebd", "\u1ebf", "\u1ec1", "\u1ec3", "\u1ec5", "\u1ec7", "\u1ec9", "\u1ecb", "\u1ecd", "\u1ecf", "\u1ed1", "\u1ed3", "\u1ed5", "\u1ed7", "\u1ed9", "\u1edb", "\u1edd", "\u1edf", "\u1ee1", "\u1ee3", "\u1ee5", "\u1ee7", "\u1ee9", "\u1eeb", "\u1eed", "\u1eef", "\u1ef1", "\u1ef3", "\u1ef5", "\u1ef7", "\u1ef9", "\u1efb", "\u1efd", "\u1eff", "\u1f00", "\u1f01", "\u1f02", "\u1f03", "\u1f04", "\u1f05", "\u1f06", "\u1f07", "\u1f10", "\u1f11", "\u1f12", "\u1f13", "\u1f14", "\u1f15", "\u1f20", "\u1f21", "\u1f22", "\u1f23", "\u1f24", "\u1f25", "\u1f26", "\u1f27", "\u1f30", "\u1f31", "\u1f32", "\u1f33", "\u1f34", "\u1f35", "\u1f36", "\u1f37", "\u1f40", "\u1f41", "\u1f42", "\u1f43", "\u1f44", "\u1f45", "\u1f50", "\u1f51", "\u1f52", "\u1f53", "\u1f54", "\u1f55", "\u1f56", "\u1f57", "\u1f60", "\u1f61", "\u1f62", "\u1f63", "\u1f64", "\u1f65", "\u1f66", "\u1f67", "\u1f70", "\u1f71", "\u1f72", "\u1f73", "\u1f74", "\u1f75", "\u1f76", "\u1f77", "\u1f78", "\u1f79", "\u1f7a", "\u1f7b", "\u1f7c", "\u1f7d", "\u1f80", "\u1f81", "\u1f82", "\u1f83", "\u1f84", "\u1f85", "\u1f86", "\u1f87", "\u1f90", "\u1f91", "\u1f92", "\u1f93", "\u1f94", "\u1f95", "\u1f96", "\u1f97", "\u1fa0", "\u1fa1", "\u1fa2", "\u1fa3", "\u1fa4", "\u1fa5", "\u1fa6", "\u1fa7", "\u1fb0", "\u1fb1", "\u1fb2", "\u1fb3", "\u1fb4", "\u1fb6", "\u1fb7", "\u1fbe", "\u1fc2", "\u1fc3", "\u1fc4", "\u1fc6", "\u1fc7", "\u1fd0", "\u1fd1", "\u1fd2", "\u1fd3", "\u1fd6", "\u1fd7", "\u1fe0", "\u1fe1", "\u1fe2", "\u1fe3", "\u1fe4", "\u1fe5", "\u1fe6", "\u1fe7", "\u1ff2", "\u1ff3", "\u1ff4", "\u1ff6", "\u1ff7", "\u210a", "\u210e", "\u210f", "\u2113", "\u212f", "\u2134", "\u2139", "\u213c", "\u213d", "\u2146", "\u2147", "\u2148", "\u2149", "\u214e", "\u2184", "\u2c30", "\u2c31", "\u2c32", "\u2c33", "\u2c34", "\u2c35", "\u2c36", "\u2c37", "\u2c38", "\u2c39", "\u2c3a", "\u2c3b", "\u2c3c", "\u2c3d", "\u2c3e", "\u2c3f", "\u2c40", "\u2c41", "\u2c42", "\u2c43", "\u2c44", "\u2c45", "\u2c46", "\u2c47", "\u2c48", "\u2c49", "\u2c4a", "\u2c4b", "\u2c4c", "\u2c4d", "\u2c4e", "\u2c4f", "\u2c50", "\u2c51", "\u2c52", "\u2c53", "\u2c54", "\u2c55", "\u2c56", "\u2c57", "\u2c58", "\u2c59", "\u2c5a", "\u2c5b", "\u2c5c", "\u2c5d", "\u2c5e", "\u2c61", "\u2c65", "\u2c66", "\u2c68", "\u2c6a", "\u2c6c", "\u2c71", "\u2c73", "\u2c74", "\u2c76", "\u2c77", "\u2c78", "\u2c79", "\u2c7a", "\u2c7b", "\u2c81", "\u2c83", "\u2c85", "\u2c87", "\u2c89", "\u2c8b", "\u2c8d", "\u2c8f", "\u2c91", "\u2c93", "\u2c95", "\u2c97", "\u2c99", "\u2c9b", "\u2c9d", "\u2c9f", "\u2ca1", "\u2ca3", "\u2ca5", "\u2ca7", "\u2ca9", "\u2cab", "\u2cad", "\u2caf", "\u2cb1", "\u2cb3", "\u2cb5", "\u2cb7", "\u2cb9", "\u2cbb", "\u2cbd", "\u2cbf", "\u2cc1", "\u2cc3", "\u2cc5", "\u2cc7", "\u2cc9", "\u2ccb", "\u2ccd", "\u2ccf", "\u2cd1", "\u2cd3", "\u2cd5", "\u2cd7", "\u2cd9", "\u2cdb", "\u2cdd", "\u2cdf", "\u2ce1", "\u2ce3", "\u2ce4", "\u2cec", "\u2cee", "\u2cf3", "\u2d00", "\u2d01", "\u2d02", "\u2d03", "\u2d04", "\u2d05", "\u2d06", "\u2d07", "\u2d08", "\u2d09", "\u2d0a", "\u2d0b", "\u2d0c", "\u2d0d", "\u2d0e", "\u2d0f", "\u2d10", "\u2d11", "\u2d12", "\u2d13", "\u2d14", "\u2d15", "\u2d16", "\u2d17", "\u2d18", "\u2d19", "\u2d1a", "\u2d1b", "\u2d1c", "\u2d1d", "\u2d1e", "\u2d1f", "\u2d20", "\u2d21", "\u2d22", "\u2d23", "\u2d24", "\u2d25", "\u2d27", "\u2d2d", "\ua641", "\ua643", "\ua645", "\ua647", "\ua649", "\ua64b", "\ua64d", "\ua64f", "\ua651", "\ua653", "\ua655", "\ua657", "\ua659", "\ua65b", "\ua65d", "\ua65f", "\ua661", "\ua663", "\ua665", "\ua667", "\ua669", "\ua66b", "\ua66d", "\ua681", "\ua683", "\ua685", "\ua687", "\ua689", "\ua68b", "\ua68d", "\ua68f", "\ua691", "\ua693", "\ua695", "\ua697", "\ua699", "\ua69b", "\ua723", "\ua725", "\ua727", "\ua729", "\ua72b", "\ua72d", "\ua72f", "\ua730", "\ua731", "\ua733", "\ua735", "\ua737", "\ua739", "\ua73b", "\ua73d", "\ua73f", "\ua741", "\ua743", "\ua745", "\ua747", "\ua749", "\ua74b", "\ua74d", "\ua74f", "\ua751", "\ua753", "\ua755", "\ua757", "\ua759", "\ua75b", "\ua75d", "\ua75f", "\ua761", "\ua763", "\ua765", "\ua767", "\ua769", "\ua76b", "\ua76d", "\ua76f", "\ua771", "\ua772", "\ua773", "\ua774", "\ua775", "\ua776", "\ua777", "\ua778", "\ua77a", "\ua77c", "\ua77f", "\ua781", "\ua783", "\ua785", "\ua787", "\ua78c", "\ua78e", "\ua791", "\ua793", "\ua794", "\ua795", "\ua797", "\ua799", "\ua79b", "\ua79d", "\ua79f", "\ua7a1", "\ua7a3", "\ua7a5", "\ua7a7", "\ua7a9", "\ua7b5", "\ua7b7", "\ua7fa", "\uab30", "\uab31", "\uab32", "\uab33", "\uab34", "\uab35", "\uab36", "\uab37", "\uab38", "\uab39", "\uab3a", "\uab3b", "\uab3c", "\uab3d", "\uab3e", "\uab3f", "\uab40", "\uab41", "\uab42", "\uab43", "\uab44", "\uab45", "\uab46", "\uab47", "\uab48", "\uab49", "\uab4a", "\uab4b", "\uab4c", "\uab4d", "\uab4e", "\uab4f", "\uab50", "\uab51", "\uab52", "\uab53", "\uab54", "\uab55", "\uab56", "\uab57", "\uab58", "\uab59", "\uab5a", "\uab60", "\uab61", "\uab62", "\uab63", "\uab64", "\uab65", "\uab70", "\uab71", "\uab72", "\uab73", "\uab74", "\uab75", "\uab76", "\uab77", "\uab78", "\uab79", "\uab7a", "\uab7b", "\uab7c", "\uab7d", "\uab7e", "\uab7f", "\uab80", "\uab81", "\uab82", "\uab83", "\uab84", "\uab85", "\uab86", "\uab87", "\uab88", "\uab89", "\uab8a", "\uab8b", "\uab8c", "\uab8d", "\uab8e", "\uab8f", "\uab90", "\uab91", "\uab92", "\uab93", "\uab94", "\uab95", "\uab96", "\uab97", "\uab98", "\uab99", "\uab9a", "\uab9b", "\uab9c", "\uab9d", "\uab9e", "\uab9f", "\uaba0", "\uaba1", "\uaba2", "\uaba3", "\uaba4", "\uaba5", "\uaba6", "\uaba7", "\uaba8", "\uaba9", "\uabaa", "\uabab", "\uabac", "\uabad", "\uabae", "\uabaf", "\uabb0", "\uabb1", "\uabb2", "\uabb3", "\uabb4", "\uabb5", "\uabb6", "\uabb7", "\uabb8", "\uabb9", "\uabba", "\uabbb", "\uabbc", "\uabbd", "\uabbe", "\uabbf", "\ufb00", "\ufb01", "\ufb02", "\ufb03", "\ufb04", "\ufb05", "\ufb06", "\ufb13", "\ufb14", "\ufb15", "\ufb16", "\ufb17", "\uff41", "\uff42", "\uff43", "\uff44", "\uff45", "\uff46", "\uff47", "\uff48", "\uff49", "\uff4a", "\uff4b", "\uff4c", "\uff4d", "\uff4e", "\uff4f", "\uff50", "\uff51", "\uff52", "\uff53", "\uff54", "\uff55", "\uff56", "\uff57", "\uff58", "\uff59", "\uff5a", "\ud801\udc28", "\ud801\udc29", "\ud801\udc2a", "\ud801\udc2b", "\ud801\udc2c", "\ud801\udc2d", "\ud801\udc2e", "\ud801\udc2f", "\ud801\udc30", "\ud801\udc31", "\ud801\udc32", "\ud801\udc33", "\ud801\udc34", "\ud801\udc35", "\ud801\udc36", "\ud801\udc37", "\ud801\udc38", "\ud801\udc39", "\ud801\udc3a", "\ud801\udc3b", "\ud801\udc3c", "\ud801\udc3d", "\ud801\udc3e", "\ud801\udc3f", "\ud801\udc40", "\ud801\udc41", "\ud801\udc42", "\ud801\udc43", "\ud801\udc44", "\ud801\udc45", "\ud801\udc46", "\ud801\udc47", "\ud801\udc48", "\ud801\udc49", "\ud801\udc4a", "\ud801\udc4b", "\ud801\udc4c", "\ud801\udc4d", "\ud801\udc4e", "\ud801\udc4f", "\ud801\udcd8", "\ud801\udcd9", "\ud801\udcda", "\ud801\udcdb", "\ud801\udcdc", "\ud801\udcdd", "\ud801\udcde", "\ud801\udcdf", "\ud801\udce0", "\ud801\udce1", "\ud801\udce2", "\ud801\udce3", "\ud801\udce4", "\ud801\udce5", "\ud801\udce6", "\ud801\udce7", "\ud801\udce8", "\ud801\udce9", "\ud801\udcea", "\ud801\udceb", "\ud801\udcec", "\ud801\udced", "\ud801\udcee", "\ud801\udcef", "\ud801\udcf0", "\ud801\udcf1", "\ud801\udcf2", "\ud801\udcf3", "\ud801\udcf4", "\ud801\udcf5", "\ud801\udcf6", "\ud801\udcf7", "\ud801\udcf8", "\ud801\udcf9", "\ud801\udcfa", "\ud801\udcfb", "\ud803\udcc0", "\ud803\udcc1", "\ud803\udcc2", "\ud803\udcc3", "\ud803\udcc4", "\ud803\udcc5", "\ud803\udcc6", "\ud803\udcc7", "\ud803\udcc8", "\ud803\udcc9", "\ud803\udcca", "\ud803\udccb", "\ud803\udccc", "\ud803\udccd", "\ud803\udcce", "\ud803\udccf", "\ud803\udcd0", "\ud803\udcd1", "\ud803\udcd2", "\ud803\udcd3", "\ud803\udcd4", "\ud803\udcd5", "\ud803\udcd6", "\ud803\udcd7", "\ud803\udcd8", "\ud803\udcd9", "\ud803\udcda", "\ud803\udcdb", "\ud803\udcdc", "\ud803\udcdd", "\ud803\udcde", "\ud803\udcdf", "\ud803\udce0", "\ud803\udce1", "\ud803\udce2", "\ud803\udce3", "\ud803\udce4", "\ud803\udce5", "\ud803\udce6", "\ud803\udce7", "\ud803\udce8", "\ud803\udce9", "\ud803\udcea", "\ud803\udceb", "\ud803\udcec", "\ud803\udced", "\ud803\udcee", "\ud803\udcef", "\ud803\udcf0", "\ud803\udcf1", "\ud803\udcf2", "\ud806\udcc0", "\ud806\udcc1", "\ud806\udcc2", "\ud806\udcc3", "\ud806\udcc4", "\ud806\udcc5", "\ud806\udcc6", "\ud806\udcc7", "\ud806\udcc8", "\ud806\udcc9", "\ud806\udcca", "\ud806\udccb", "\ud806\udccc", "\ud806\udccd", "\ud806\udcce", "\ud806\udccf", "\ud806\udcd0", "\ud806\udcd1", "\ud806\udcd2", "\ud806\udcd3", "\ud806\udcd4", "\ud806\udcd5", "\ud806\udcd6", "\ud806\udcd7", "\ud806\udcd8", "\ud806\udcd9", "\ud806\udcda", "\ud806\udcdb", "\ud806\udcdc", "\ud806\udcdd", "\ud806\udcde", "\ud806\udcdf", "\ud835\udc1a", "\ud835\udc1b", "\ud835\udc1c", "\ud835\udc1d", "\ud835\udc1e", "\ud835\udc1f", "\ud835\udc20", "\ud835\udc21", "\ud835\udc22", "\ud835\udc23", "\ud835\udc24", "\ud835\udc25", "\ud835\udc26", "\ud835\udc27", "\ud835\udc28", "\ud835\udc29", "\ud835\udc2a", "\ud835\udc2b", "\ud835\udc2c", "\ud835\udc2d", "\ud835\udc2e", "\ud835\udc2f", "\ud835\udc30", "\ud835\udc31", "\ud835\udc32", "\ud835\udc33", "\ud835\udc4e", "\ud835\udc4f", "\ud835\udc50", "\ud835\udc51", "\ud835\udc52", "\ud835\udc53", "\ud835\udc54", "\ud835\udc56", "\ud835\udc57", "\ud835\udc58", "\ud835\udc59", "\ud835\udc5a", "\ud835\udc5b", "\ud835\udc5c", "\ud835\udc5d", "\ud835\udc5e", "\ud835\udc5f", "\ud835\udc60", "\ud835\udc61", "\ud835\udc62", "\ud835\udc63", "\ud835\udc64", "\ud835\udc65", "\ud835\udc66", "\ud835\udc67", "\ud835\udc82", "\ud835\udc83", "\ud835\udc84", "\ud835\udc85", "\ud835\udc86", "\ud835\udc87", "\ud835\udc88", "\ud835\udc89", "\ud835\udc8a", "\ud835\udc8b", "\ud835\udc8c", "\ud835\udc8d", "\ud835\udc8e", "\ud835\udc8f", "\ud835\udc90", "\ud835\udc91", "\ud835\udc92", "\ud835\udc93", "\ud835\udc94", "\ud835\udc95", "\ud835\udc96", "\ud835\udc97", "\ud835\udc98", "\ud835\udc99", "\ud835\udc9a", "\ud835\udc9b", "\ud835\udcb6", "\ud835\udcb7", "\ud835\udcb8", "\ud835\udcb9", "\ud835\udcbb", "\ud835\udcbd", "\ud835\udcbe", "\ud835\udcbf", "\ud835\udcc0", "\ud835\udcc1", "\ud835\udcc2", "\ud835\udcc3", "\ud835\udcc5", "\ud835\udcc6", "\ud835\udcc7", "\ud835\udcc8", "\ud835\udcc9", "\ud835\udcca", "\ud835\udccb", "\ud835\udccc", "\ud835\udccd", "\ud835\udcce", "\ud835\udccf", "\ud835\udcea", "\ud835\udceb", "\ud835\udcec", "\ud835\udced", "\ud835\udcee", "\ud835\udcef", "\ud835\udcf0", "\ud835\udcf1", "\ud835\udcf2", "\ud835\udcf3", "\ud835\udcf4", "\ud835\udcf5", "\ud835\udcf6", "\ud835\udcf7", "\ud835\udcf8", "\ud835\udcf9", "\ud835\udcfa", "\ud835\udcfb", "\ud835\udcfc", "\ud835\udcfd", "\ud835\udcfe", "\ud835\udcff", "\ud835\udd00", "\ud835\udd01", "\ud835\udd02", "\ud835\udd03", "\ud835\udd1e", "\ud835\udd1f", "\ud835\udd20", "\ud835\udd21", "\ud835\udd22", "\ud835\udd23", "\ud835\udd24", "\ud835\udd25", "\ud835\udd26", "\ud835\udd27", "\ud835\udd28", "\ud835\udd29", "\ud835\udd2a", "\ud835\udd2b", "\ud835\udd2c", "\ud835\udd2d", "\ud835\udd2e", "\ud835\udd2f", "\ud835\udd30", "\ud835\udd31", "\ud835\udd32", "\ud835\udd33", "\ud835\udd34", "\ud835\udd35", "\ud835\udd36", "\ud835\udd37", "\ud835\udd52", "\ud835\udd53", "\ud835\udd54", "\ud835\udd55", "\ud835\udd56", "\ud835\udd57", "\ud835\udd58", "\ud835\udd59", "\ud835\udd5a", "\ud835\udd5b", "\ud835\udd5c", "\ud835\udd5d", "\ud835\udd5e", "\ud835\udd5f", "\ud835\udd60", "\ud835\udd61", "\ud835\udd62", "\ud835\udd63", "\ud835\udd64", "\ud835\udd65", "\ud835\udd66", "\ud835\udd67", "\ud835\udd68", "\ud835\udd69", "\ud835\udd6a", "\ud835\udd6b", "\ud835\udd86", "\ud835\udd87", "\ud835\udd88", "\ud835\udd89", "\ud835\udd8a", "\ud835\udd8b", "\ud835\udd8c", "\ud835\udd8d", "\ud835\udd8e", "\ud835\udd8f", "\ud835\udd90", "\ud835\udd91", "\ud835\udd92", "\ud835\udd93", "\ud835\udd94", "\ud835\udd95", "\ud835\udd96", "\ud835\udd97", "\ud835\udd98", "\ud835\udd99", "\ud835\udd9a", "\ud835\udd9b", "\ud835\udd9c", "\ud835\udd9d", "\ud835\udd9e", "\ud835\udd9f", "\ud835\uddba", "\ud835\uddbb", "\ud835\uddbc", "\ud835\uddbd", "\ud835\uddbe", "\ud835\uddbf", "\ud835\uddc0", "\ud835\uddc1", "\ud835\uddc2", "\ud835\uddc3", "\ud835\uddc4", "\ud835\uddc5", "\ud835\uddc6", "\ud835\uddc7", "\ud835\uddc8", "\ud835\uddc9", "\ud835\uddca", "\ud835\uddcb", "\ud835\uddcc", "\ud835\uddcd", "\ud835\uddce", "\ud835\uddcf", "\ud835\uddd0", "\ud835\uddd1", "\ud835\uddd2", "\ud835\uddd3", "\ud835\uddee", "\ud835\uddef", "\ud835\uddf0", "\ud835\uddf1", "\ud835\uddf2", "\ud835\uddf3", "\ud835\uddf4", "\ud835\uddf5", "\ud835\uddf6", "\ud835\uddf7", "\ud835\uddf8", "\ud835\uddf9", "\ud835\uddfa", "\ud835\uddfb", "\ud835\uddfc", "\ud835\uddfd", "\ud835\uddfe", "\ud835\uddff", "\ud835\ude00", "\ud835\ude01", "\ud835\ude02", "\ud835\ude03", "\ud835\ude04", "\ud835\ude05", "\ud835\ude06", "\ud835\ude07", "\ud835\ude22", "\ud835\ude23", "\ud835\ude24", "\ud835\ude25", "\ud835\ude26", "\ud835\ude27", "\ud835\ude28", "\ud835\ude29", "\ud835\ude2a", "\ud835\ude2b", "\ud835\ude2c", "\ud835\ude2d", "\ud835\ude2e", "\ud835\ude2f", "\ud835\ude30", "\ud835\ude31", "\ud835\ude32", "\ud835\ude33", "\ud835\ude34", "\ud835\ude35", "\ud835\ude36", "\ud835\ude37", "\ud835\ude38", "\ud835\ude39", "\ud835\ude3a", "\ud835\ude3b", "\ud835\ude56", "\ud835\ude57", "\ud835\ude58", "\ud835\ude59", "\ud835\ude5a", "\ud835\ude5b", "\ud835\ude5c", "\ud835\ude5d", "\ud835\ude5e", "\ud835\ude5f", "\ud835\ude60", "\ud835\ude61", "\ud835\ude62", "\ud835\ude63", "\ud835\ude64", "\ud835\ude65", "\ud835\ude66", "\ud835\ude67", "\ud835\ude68", "\ud835\ude69", "\ud835\ude6a", "\ud835\ude6b", "\ud835\ude6c", "\ud835\ude6d", "\ud835\ude6e", "\ud835\ude6f", "\ud835\ude8a", "\ud835\ude8b", "\ud835\ude8c", "\ud835\ude8d", "\ud835\ude8e", "\ud835\ude8f", "\ud835\ude90", "\ud835\ude91", "\ud835\ude92", "\ud835\ude93", "\ud835\ude94", "\ud835\ude95", "\ud835\ude96", "\ud835\ude97", "\ud835\ude98", "\ud835\ude99", "\ud835\ude9a", "\ud835\ude9b", "\ud835\ude9c", "\ud835\ude9d", "\ud835\ude9e", "\ud835\ude9f", "\ud835\udea0", "\ud835\udea1", "\ud835\udea2", "\ud835\udea3", "\ud835\udea4", "\ud835\udea5", "\ud835\udec2", "\ud835\udec3", "\ud835\udec4", "\ud835\udec5", "\ud835\udec6", "\ud835\udec7", "\ud835\udec8", "\ud835\udec9", "\ud835\udeca", "\ud835\udecb", "\ud835\udecc", "\ud835\udecd", "\ud835\udece", "\ud835\udecf", "\ud835\uded0", "\ud835\uded1", "\ud835\uded2", "\ud835\uded3", "\ud835\uded4", "\ud835\uded5", "\ud835\uded6", "\ud835\uded7", "\ud835\uded8", "\ud835\uded9", "\ud835\udeda", "\ud835\udedc", "\ud835\udedd", "\ud835\udede", "\ud835\udedf", "\ud835\udee0", "\ud835\udee1", "\ud835\udefc", "\ud835\udefd", "\ud835\udefe", "\ud835\udeff", "\ud835\udf00", "\ud835\udf01", "\ud835\udf02", "\ud835\udf03", "\ud835\udf04", "\ud835\udf05", "\ud835\udf06", "\ud835\udf07", "\ud835\udf08", "\ud835\udf09", "\ud835\udf0a", "\ud835\udf0b", "\ud835\udf0c", "\ud835\udf0d", "\ud835\udf0e", "\ud835\udf0f", "\ud835\udf10", "\ud835\udf11", "\ud835\udf12", "\ud835\udf13", "\ud835\udf14", "\ud835\udf16", "\ud835\udf17", "\ud835\udf18", "\ud835\udf19", "\ud835\udf1a", "\ud835\udf1b", "\ud835\udf36", "\ud835\udf37", "\ud835\udf38", "\ud835\udf39", "\ud835\udf3a", "\ud835\udf3b", "\ud835\udf3c", "\ud835\udf3d", "\ud835\udf3e", "\ud835\udf3f", "\ud835\udf40", "\ud835\udf41", "\ud835\udf42", "\ud835\udf43", "\ud835\udf44", "\ud835\udf45", "\ud835\udf46", "\ud835\udf47", "\ud835\udf48", "\ud835\udf49", "\ud835\udf4a", "\ud835\udf4b", "\ud835\udf4c", "\ud835\udf4d", "\ud835\udf4e", "\ud835\udf50", "\ud835\udf51", "\ud835\udf52", "\ud835\udf53", "\ud835\udf54", "\ud835\udf55", "\ud835\udf70", "\ud835\udf71", "\ud835\udf72", "\ud835\udf73", "\ud835\udf74", "\ud835\udf75", "\ud835\udf76", "\ud835\udf77", "\ud835\udf78", "\ud835\udf79", "\ud835\udf7a", "\ud835\udf7b", "\ud835\udf7c", "\ud835\udf7d", "\ud835\udf7e", "\ud835\udf7f", "\ud835\udf80", "\ud835\udf81", "\ud835\udf82", "\ud835\udf83", "\ud835\udf84", "\ud835\udf85", "\ud835\udf86", "\ud835\udf87", "\ud835\udf88", "\ud835\udf8a", "\ud835\udf8b", "\ud835\udf8c", "\ud835\udf8d", "\ud835\udf8e", "\ud835\udf8f", "\ud835\udfaa", "\ud835\udfab", "\ud835\udfac", "\ud835\udfad", "\ud835\udfae", "\ud835\udfaf", "\ud835\udfb0", "\ud835\udfb1", "\ud835\udfb2", "\ud835\udfb3", "\ud835\udfb4", "\ud835\udfb5", "\ud835\udfb6", "\ud835\udfb7", "\ud835\udfb8", "\ud835\udfb9", "\ud835\udfba", "\ud835\udfbb", "\ud835\udfbc", "\ud835\udfbd", "\ud835\udfbe", "\ud835\udfbf", "\ud835\udfc0", "\ud835\udfc1", "\ud835\udfc2", "\ud835\udfc4", "\ud835\udfc5", "\ud835\udfc6", "\ud835\udfc7", "\ud835\udfc8", "\ud835\udfc9", "\ud835\udfcb", "\ud83a\udd22", "\ud83a\udd23", "\ud83a\udd24", "\ud83a\udd25", "\ud83a\udd26", "\ud83a\udd27", "\ud83a\udd28", "\ud83a\udd29", "\ud83a\udd2a", "\ud83a\udd2b", "\ud83a\udd2c", "\ud83a\udd2d", "\ud83a\udd2e", "\ud83a\udd2f", "\ud83a\udd30", "\ud83a\udd31", "\ud83a\udd32", "\ud83a\udd33", "\ud83a\udd34", "\ud83a\udd35", "\ud83a\udd36", "\ud83a\udd37", "\ud83a\udd38", "\ud83a\udd39", "\ud83a\udd3a", "\ud83a\udd3b", "\ud83a\udd3c", "\ud83a\udd3d", "\ud83a\udd3e", "\ud83a\udd3f", "\ud83a\udd40", "\ud83a\udd41", "\ud83a\udd42", "\ud83a\udd43", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "\u00c0", "\u00c1", "\u00c2", "\u00c3", "\u00c4", "\u00c5", "\u00c6", "\u00c7", "\u00c8", "\u00c9", "\u00ca", "\u00cb", "\u00cc", "\u00cd", "\u00ce", "\u00cf", "\u00d0", "\u00d1", "\u00d2", "\u00d3", "\u00d4", "\u00d5", "\u00d6", "\u00d8", "\u00d9", "\u00da", "\u00db", "\u00dc", "\u00dd", "\u00de", "\u0100", "\u0102", "\u0104", "\u0106", "\u0108", "\u010a", "\u010c", "\u010e", "\u0110", "\u0112", "\u0114", "\u0116", "\u0118", "\u011a", "\u011c", "\u011e", "\u0120", "\u0122", "\u0124", "\u0126", "\u0128", "\u012a", "\u012c", "\u012e", "\u0130", "\u0132", "\u0134", "\u0136", "\u0139", "\u013b", "\u013d", "\u013f", "\u0141", "\u0143", "\u0145", "\u0147", "\u014a", "\u014c", "\u014e", "\u0150", "\u0152", "\u0154", "\u0156", "\u0158", "\u015a", "\u015c", "\u015e", "\u0160", "\u0162", "\u0164", "\u0166", "\u0168", "\u016a", "\u016c", "\u016e", "\u0170", "\u0172", "\u0174", "\u0176", "\u0178", "\u0179", "\u017b", "\u017d", "\u0181", "\u0182", "\u0184", "\u0186", "\u0187", "\u0189", "\u018a", "\u018b", "\u018e", "\u018f", "\u0190", "\u0191", "\u0193", "\u0194", "\u0196", "\u0197", "\u0198", "\u019c", "\u019d", "\u019f", "\u01a0", "\u01a2", "\u01a4", "\u01a6", "\u01a7", "\u01a9", "\u01ac", "\u01ae", "\u01af", "\u01b1", "\u01b2", "\u01b3", "\u01b5", "\u01b7", "\u01b8", "\u01bc", "\u01c4", "\u01c7", "\u01ca", "\u01cd", "\u01cf", "\u01d1", "\u01d3", "\u01d5", "\u01d7", "\u01d9", "\u01db", "\u01de", "\u01e0", "\u01e2", "\u01e4", "\u01e6", "\u01e8", "\u01ea", "\u01ec", "\u01ee", "\u01f1", "\u01f4", "\u01f6", "\u01f7", "\u01f8", "\u01fa", "\u01fc", "\u01fe", "\u0200", "\u0202", "\u0204", "\u0206", "\u0208", "\u020a", "\u020c", "\u020e", "\u0210", "\u0212", "\u0214", "\u0216", "\u0218", "\u021a", "\u021c", "\u021e", "\u0220", "\u0222", "\u0224", "\u0226", "\u0228", "\u022a", "\u022c", "\u022e", "\u0230", "\u0232", "\u023a", "\u023b", "\u023d", "\u023e", "\u0241", "\u0243", "\u0244", "\u0245", "\u0246", "\u0248", "\u024a", "\u024c", "\u024e", "\u0370", "\u0372", "\u0376", "\u037f", "\u0386", "\u0388", "\u0389", "\u038a", "\u038c", "\u038e", "\u038f", "\u0391", "\u0392", "\u0393", "\u0394", "\u0395", "\u0396", "\u0397", "\u0398", "\u0399", "\u039a", "\u039b", "\u039c", "\u039d", "\u039e", "\u039f", "\u03a0", "\u03a1", "\u03a3", "\u03a4", "\u03a5", "\u03a6", "\u03a7", "\u03a8", "\u03a9", "\u03aa", "\u03ab", "\u03cf", "\u03d2", "\u03d3", "\u03d4", "\u03d8", "\u03da", "\u03dc", "\u03de", "\u03e0", "\u03e2", "\u03e4", "\u03e6", "\u03e8", "\u03ea", "\u03ec", "\u03ee", "\u03f4", "\u03f7", "\u03f9", "\u03fa", "\u03fd", "\u03fe", "\u03ff", "\u0400", "\u0401", "\u0402", "\u0403", "\u0404", "\u0405", "\u0406", "\u0407", "\u0408", "\u0409", "\u040a", "\u040b", "\u040c", "\u040d", "\u040e", "\u040f", "\u0410", "\u0411", "\u0412", "\u0413", "\u0414", "\u0415", "\u0416", "\u0417", "\u0418", "\u0419", "\u041a", "\u041b", "\u041c", "\u041d", "\u041e", "\u041f", "\u0420", "\u0421", "\u0422", "\u0423", "\u0424", "\u0425", "\u0426", "\u0427", "\u0428", "\u0429", "\u042a", "\u042b", "\u042c", "\u042d", "\u042e", "\u042f", "\u0460", "\u0462", "\u0464", "\u0466", "\u0468", "\u046a", "\u046c", "\u046e", "\u0470", "\u0472", "\u0474", "\u0476", "\u0478", "\u047a", "\u047c", "\u047e", "\u0480", "\u048a", "\u048c", "\u048e", "\u0490", "\u0492", "\u0494", "\u0496", "\u0498", "\u049a", "\u049c", "\u049e", "\u04a0", "\u04a2", "\u04a4", "\u04a6", "\u04a8", "\u04aa", "\u04ac", "\u04ae", "\u04b0", "\u04b2", "\u04b4", "\u04b6", "\u04b8", "\u04ba", "\u04bc", "\u04be", "\u04c0", "\u04c1", "\u04c3", "\u04c5", "\u04c7", "\u04c9", "\u04cb", "\u04cd", "\u04d0", "\u04d2", "\u04d4", "\u04d6", "\u04d8", "\u04da", "\u04dc", "\u04de", "\u04e0", "\u04e2", "\u04e4", "\u04e6", "\u04e8", "\u04ea", "\u04ec", "\u04ee", "\u04f0", "\u04f2", "\u04f4", "\u04f6", "\u04f8", "\u04fa", "\u04fc", "\u04fe", "\u0500", "\u0502", "\u0504", "\u0506", "\u0508", "\u050a", "\u050c", "\u050e", "\u0510", "\u0512", "\u0514", "\u0516", "\u0518", "\u051a", "\u051c", "\u051e", "\u0520", "\u0522", "\u0524", "\u0526", "\u0528", "\u052a", "\u052c", "\u052e", "\u0531", "\u0532", "\u0533", "\u0534", "\u0535", "\u0536", "\u0537", "\u0538", "\u0539", "\u053a", "\u053b", "\u053c", "\u053d", "\u053e", "\u053f", "\u0540", "\u0541", "\u0542", "\u0543", "\u0544", "\u0545", "\u0546", "\u0547", "\u0548", "\u0549", "\u054a", "\u054b", "\u054c", "\u054d", "\u054e", "\u054f", "\u0550", "\u0551", "\u0552", "\u0553", "\u0554", "\u0555", "\u0556", "\u10a0", "\u10a1", "\u10a2", "\u10a3", "\u10a4", "\u10a5", "\u10a6", "\u10a7", "\u10a8", "\u10a9", "\u10aa", "\u10ab", "\u10ac", "\u10ad", "\u10ae", "\u10af", "\u10b0", "\u10b1", "\u10b2", "\u10b3", "\u10b4", "\u10b5", "\u10b6", "\u10b7", "\u10b8", "\u10b9", "\u10ba", "\u10bb", "\u10bc", "\u10bd", "\u10be", "\u10bf", "\u10c0", "\u10c1", "\u10c2", "\u10c3", "\u10c4", "\u10c5", "\u10c7", "\u10cd", "\u13a0", "\u13a1", "\u13a2", "\u13a3", "\u13a4", "\u13a5", "\u13a6", "\u13a7", "\u13a8", "\u13a9", "\u13aa", "\u13ab", "\u13ac", "\u13ad", "\u13ae", "\u13af", "\u13b0", "\u13b1", "\u13b2", "\u13b3", "\u13b4", "\u13b5", "\u13b6", "\u13b7", "\u13b8", "\u13b9", "\u13ba", "\u13bb", "\u13bc", "\u13bd", "\u13be", "\u13bf", "\u13c0", "\u13c1", "\u13c2", "\u13c3", "\u13c4", "\u13c5", "\u13c6", "\u13c7", "\u13c8", "\u13c9", "\u13ca", "\u13cb", "\u13cc", "\u13cd", "\u13ce", "\u13cf", "\u13d0", "\u13d1", "\u13d2", "\u13d3", "\u13d4", "\u13d5", "\u13d6", "\u13d7", "\u13d8", "\u13d9", "\u13da", "\u13db", "\u13dc", "\u13dd", "\u13de", "\u13df", "\u13e0", "\u13e1", "\u13e2", "\u13e3", "\u13e4", "\u13e5", "\u13e6", "\u13e7", "\u13e8", "\u13e9", "\u13ea", "\u13eb", "\u13ec", "\u13ed", "\u13ee", "\u13ef", "\u13f0", "\u13f1", "\u13f2", "\u13f3", "\u13f4", "\u13f5", "\u1e00", "\u1e02", "\u1e04", "\u1e06", "\u1e08", "\u1e0a", "\u1e0c", "\u1e0e", "\u1e10", "\u1e12", "\u1e14", "\u1e16", "\u1e18", "\u1e1a", "\u1e1c", "\u1e1e", "\u1e20", "\u1e22", "\u1e24", "\u1e26", "\u1e28", "\u1e2a", "\u1e2c", "\u1e2e", "\u1e30", "\u1e32", "\u1e34", "\u1e36", "\u1e38", "\u1e3a", "\u1e3c", "\u1e3e", "\u1e40", "\u1e42", "\u1e44", "\u1e46", "\u1e48", "\u1e4a", "\u1e4c", "\u1e4e", "\u1e50", "\u1e52", "\u1e54", "\u1e56", "\u1e58", "\u1e5a", "\u1e5c", "\u1e5e", "\u1e60", "\u1e62", "\u1e64", "\u1e66", "\u1e68", "\u1e6a", "\u1e6c", "\u1e6e", "\u1e70", "\u1e72", "\u1e74", "\u1e76", "\u1e78", "\u1e7a", "\u1e7c", "\u1e7e", "\u1e80", "\u1e82", "\u1e84", "\u1e86", "\u1e88", "\u1e8a", "\u1e8c", "\u1e8e", "\u1e90", "\u1e92", "\u1e94", "\u1e9e", "\u1ea0", "\u1ea2", "\u1ea4", "\u1ea6", "\u1ea8", "\u1eaa", "\u1eac", "\u1eae", "\u1eb0", "\u1eb2", "\u1eb4", "\u1eb6", "\u1eb8", "\u1eba", "\u1ebc", "\u1ebe", "\u1ec0", "\u1ec2", "\u1ec4", "\u1ec6", "\u1ec8", "\u1eca", "\u1ecc", "\u1ece", "\u1ed0", "\u1ed2", "\u1ed4", "\u1ed6", "\u1ed8", "\u1eda", "\u1edc", "\u1ede", "\u1ee0", "\u1ee2", "\u1ee4", "\u1ee6", "\u1ee8", "\u1eea", "\u1eec", "\u1eee", "\u1ef0", "\u1ef2", "\u1ef4", "\u1ef6", "\u1ef8", "\u1efa", "\u1efc", "\u1efe", "\u1f08", "\u1f09", "\u1f0a", "\u1f0b", "\u1f0c", "\u1f0d", "\u1f0e", "\u1f0f", "\u1f18", "\u1f19", "\u1f1a", "\u1f1b", "\u1f1c", "\u1f1d", "\u1f28", "\u1f29", "\u1f2a", "\u1f2b", "\u1f2c", "\u1f2d", "\u1f2e", "\u1f2f", "\u1f38", "\u1f39", "\u1f3a", "\u1f3b", "\u1f3c", "\u1f3d", "\u1f3e", "\u1f3f", "\u1f48", "\u1f49", "\u1f4a", "\u1f4b", "\u1f4c", "\u1f4d", "\u1f59", "\u1f5b", "\u1f5d", "\u1f5f", "\u1f68", "\u1f69", "\u1f6a", "\u1f6b", "\u1f6c", "\u1f6d", "\u1f6e", "\u1f6f", "\u1fb8", "\u1fb9", "\u1fba", "\u1fbb", "\u1fc8", "\u1fc9", "\u1fca", "\u1fcb", "\u1fd8", "\u1fd9", "\u1fda", "\u1fdb", "\u1fe8", "\u1fe9", "\u1fea", "\u1feb", "\u1fec", "\u1ff8", "\u1ff9", "\u1ffa", "\u1ffb", "\u2102", "\u2107", "\u210b", "\u210c", "\u210d", "\u2110", "\u2111", "\u2112", "\u2115", "\u2119", "\u211a", "\u211b", "\u211c", "\u211d", "\u2124", "\u2126", "\u2128", "\u212a", "\u212b", "\u212c", "\u212d", "\u2130", "\u2131", "\u2132", "\u2133", "\u213e", "\u213f", "\u2145", "\u2183", "\u2c00", "\u2c01", "\u2c02", "\u2c03", "\u2c04", "\u2c05", "\u2c06", "\u2c07", "\u2c08", "\u2c09", "\u2c0a", "\u2c0b", "\u2c0c", "\u2c0d", "\u2c0e", "\u2c0f", "\u2c10", "\u2c11", "\u2c12", "\u2c13", "\u2c14", "\u2c15", "\u2c16", "\u2c17", "\u2c18", "\u2c19", "\u2c1a", "\u2c1b", "\u2c1c", "\u2c1d", "\u2c1e", "\u2c1f", "\u2c20", "\u2c21", "\u2c22", "\u2c23", "\u2c24", "\u2c25", "\u2c26", "\u2c27", "\u2c28", "\u2c29", "\u2c2a", "\u2c2b", "\u2c2c", "\u2c2d", "\u2c2e", "\u2c60", "\u2c62", "\u2c63", "\u2c64", "\u2c67", "\u2c69", "\u2c6b", "\u2c6d", "\u2c6e", "\u2c6f", "\u2c70", "\u2c72", "\u2c75", "\u2c7e", "\u2c7f", "\u2c80", "\u2c82", "\u2c84", "\u2c86", "\u2c88", "\u2c8a", "\u2c8c", "\u2c8e", "\u2c90", "\u2c92", "\u2c94", "\u2c96", "\u2c98", "\u2c9a", "\u2c9c", "\u2c9e", "\u2ca0", "\u2ca2", "\u2ca4", "\u2ca6", "\u2ca8", "\u2caa", "\u2cac", "\u2cae", "\u2cb0", "\u2cb2", "\u2cb4", "\u2cb6", "\u2cb8", "\u2cba", "\u2cbc", "\u2cbe", "\u2cc0", "\u2cc2", "\u2cc4", "\u2cc6", "\u2cc8", "\u2cca", "\u2ccc", "\u2cce", "\u2cd0", "\u2cd2", "\u2cd4", "\u2cd6", "\u2cd8", "\u2cda", "\u2cdc", "\u2cde", "\u2ce0", "\u2ce2", "\u2ceb", "\u2ced", "\u2cf2", "\ua640", "\ua642", "\ua644", "\ua646", "\ua648", "\ua64a", "\ua64c", "\ua64e", "\ua650", "\ua652", "\ua654", "\ua656", "\ua658", "\ua65a", "\ua65c", "\ua65e", "\ua660", "\ua662", "\ua664", "\ua666", "\ua668", "\ua66a", "\ua66c", "\ua680", "\ua682", "\ua684", "\ua686", "\ua688", "\ua68a", "\ua68c", "\ua68e", "\ua690", "\ua692", "\ua694", "\ua696", "\ua698", "\ua69a", "\ua722", "\ua724", "\ua726", "\ua728", "\ua72a", "\ua72c", "\ua72e", "\ua732", "\ua734", "\ua736", "\ua738", "\ua73a", "\ua73c", "\ua73e", "\ua740", "\ua742", "\ua744", "\ua746", "\ua748", "\ua74a", "\ua74c", "\ua74e", "\ua750", "\ua752", "\ua754", "\ua756", "\ua758", "\ua75a", "\ua75c", "\ua75e", "\ua760", "\ua762", "\ua764", "\ua766", "\ua768", "\ua76a", "\ua76c", "\ua76e", "\ua779", "\ua77b", "\ua77d", "\ua77e", "\ua780", "\ua782", "\ua784", "\ua786", "\ua78b", "\ua78d", "\ua790", "\ua792", "\ua796", "\ua798", "\ua79a", "\ua79c", "\ua79e", "\ua7a0", "\ua7a2", "\ua7a4", "\ua7a6", "\ua7a8", "\ua7aa", "\ua7ab", "\ua7ac", "\ua7ad", "\ua7ae", "\ua7b0", "\ua7b1", "\ua7b2", "\ua7b3", "\ua7b4", "\ua7b6", "\uff21", "\uff22", "\uff23", "\uff24", "\uff25", "\uff26", "\uff27", "\uff28", "\uff29", "\uff2a", "\uff2b", "\uff2c", "\uff2d", "\uff2e", "\uff2f", "\uff30", "\uff31", "\uff32", "\uff33", "\uff34", "\uff35", "\uff36", "\uff37", "\uff38", "\uff39", "\uff3a", "\ud801\udc00", "\ud801\udc01", "\ud801\udc02", "\ud801\udc03", "\ud801\udc04", "\ud801\udc05", "\ud801\udc06", "\ud801\udc07", "\ud801\udc08", "\ud801\udc09", "\ud801\udc0a", "\ud801\udc0b", "\ud801\udc0c", "\ud801\udc0d", "\ud801\udc0e", "\ud801\udc0f", "\ud801\udc10", "\ud801\udc11", "\ud801\udc12", "\ud801\udc13", "\ud801\udc14", "\ud801\udc15", "\ud801\udc16", "\ud801\udc17", "\ud801\udc18", "\ud801\udc19", "\ud801\udc1a", "\ud801\udc1b", "\ud801\udc1c", "\ud801\udc1d", "\ud801\udc1e", "\ud801\udc1f", "\ud801\udc20", "\ud801\udc21", "\ud801\udc22", "\ud801\udc23", "\ud801\udc24", "\ud801\udc25", "\ud801\udc26", "\ud801\udc27", "\ud801\udcb0", "\ud801\udcb1", "\ud801\udcb2", "\ud801\udcb3", "\ud801\udcb4", "\ud801\udcb5", "\ud801\udcb6", "\ud801\udcb7", "\ud801\udcb8", "\ud801\udcb9", "\ud801\udcba", "\ud801\udcbb", "\ud801\udcbc", "\ud801\udcbd", "\ud801\udcbe", "\ud801\udcbf", "\ud801\udcc0", "\ud801\udcc1", "\ud801\udcc2", "\ud801\udcc3", "\ud801\udcc4", "\ud801\udcc5", "\ud801\udcc6", "\ud801\udcc7", "\ud801\udcc8", "\ud801\udcc9", "\ud801\udcca", "\ud801\udccb", "\ud801\udccc", "\ud801\udccd", "\ud801\udcce", "\ud801\udccf", "\ud801\udcd0", "\ud801\udcd1", "\ud801\udcd2", "\ud801\udcd3", "\ud803\udc80", "\ud803\udc81", "\ud803\udc82", "\ud803\udc83", "\ud803\udc84", "\ud803\udc85", "\ud803\udc86", "\ud803\udc87", "\ud803\udc88", "\ud803\udc89", "\ud803\udc8a", "\ud803\udc8b", "\ud803\udc8c", "\ud803\udc8d", "\ud803\udc8e", "\ud803\udc8f", "\ud803\udc90", "\ud803\udc91", "\ud803\udc92", "\ud803\udc93", "\ud803\udc94", "\ud803\udc95", "\ud803\udc96", "\ud803\udc97", "\ud803\udc98", "\ud803\udc99", "\ud803\udc9a", "\ud803\udc9b", "\ud803\udc9c", "\ud803\udc9d", "\ud803\udc9e", "\ud803\udc9f", "\ud803\udca0", "\ud803\udca1", "\ud803\udca2", "\ud803\udca3", "\ud803\udca4", "\ud803\udca5", "\ud803\udca6", "\ud803\udca7", "\ud803\udca8", "\ud803\udca9", "\ud803\udcaa", "\ud803\udcab", "\ud803\udcac", "\ud803\udcad", "\ud803\udcae", "\ud803\udcaf", "\ud803\udcb0", "\ud803\udcb1", "\ud803\udcb2", "\ud806\udca0", "\ud806\udca1", "\ud806\udca2", "\ud806\udca3", "\ud806\udca4", "\ud806\udca5", "\ud806\udca6", "\ud806\udca7", "\ud806\udca8", "\ud806\udca9", "\ud806\udcaa", "\ud806\udcab", "\ud806\udcac", "\ud806\udcad", "\ud806\udcae", "\ud806\udcaf", "\ud806\udcb0", "\ud806\udcb1", "\ud806\udcb2", "\ud806\udcb3", "\ud806\udcb4", "\ud806\udcb5", "\ud806\udcb6", "\ud806\udcb7", "\ud806\udcb8", "\ud806\udcb9", "\ud806\udcba", "\ud806\udcbb", "\ud806\udcbc", "\ud806\udcbd", "\ud806\udcbe", "\ud806\udcbf", "\ud835\udc00", "\ud835\udc01", "\ud835\udc02", "\ud835\udc03", "\ud835\udc04", "\ud835\udc05", "\ud835\udc06", "\ud835\udc07", "\ud835\udc08", "\ud835\udc09", "\ud835\udc0a", "\ud835\udc0b", "\ud835\udc0c", "\ud835\udc0d", "\ud835\udc0e", "\ud835\udc0f", "\ud835\udc10", "\ud835\udc11", "\ud835\udc12", "\ud835\udc13", "\ud835\udc14", "\ud835\udc15", "\ud835\udc16", "\ud835\udc17", "\ud835\udc18", "\ud835\udc19", "\ud835\udc34", "\ud835\udc35", "\ud835\udc36", "\ud835\udc37", "\ud835\udc38", "\ud835\udc39", "\ud835\udc3a", "\ud835\udc3b", "\ud835\udc3c", "\ud835\udc3d", "\ud835\udc3e", "\ud835\udc3f", "\ud835\udc40", "\ud835\udc41", "\ud835\udc42", "\ud835\udc43", "\ud835\udc44", "\ud835\udc45", "\ud835\udc46", "\ud835\udc47", "\ud835\udc48", "\ud835\udc49", "\ud835\udc4a", "\ud835\udc4b", "\ud835\udc4c", "\ud835\udc4d", "\ud835\udc68", "\ud835\udc69", "\ud835\udc6a", "\ud835\udc6b", "\ud835\udc6c", "\ud835\udc6d", "\ud835\udc6e", "\ud835\udc6f", "\ud835\udc70", "\ud835\udc71", "\ud835\udc72", "\ud835\udc73", "\ud835\udc74", "\ud835\udc75", "\ud835\udc76", "\ud835\udc77", "\ud835\udc78", "\ud835\udc79", "\ud835\udc7a", "\ud835\udc7b", "\ud835\udc7c", "\ud835\udc7d", "\ud835\udc7e", "\ud835\udc7f", "\ud835\udc80", "\ud835\udc81", "\ud835\udc9c", "\ud835\udc9e", "\ud835\udc9f", "\ud835\udca2", "\ud835\udca5", "\ud835\udca6", "\ud835\udca9", "\ud835\udcaa", "\ud835\udcab", "\ud835\udcac", "\ud835\udcae", "\ud835\udcaf", "\ud835\udcb0", "\ud835\udcb1", "\ud835\udcb2", "\ud835\udcb3", "\ud835\udcb4", "\ud835\udcb5", "\ud835\udcd0", "\ud835\udcd1", "\ud835\udcd2", "\ud835\udcd3", "\ud835\udcd4", "\ud835\udcd5", "\ud835\udcd6", "\ud835\udcd7", "\ud835\udcd8", "\ud835\udcd9", "\ud835\udcda", "\ud835\udcdb", "\ud835\udcdc", "\ud835\udcdd", "\ud835\udcde", "\ud835\udcdf", "\ud835\udce0", "\ud835\udce1", "\ud835\udce2", "\ud835\udce3", "\ud835\udce4", "\ud835\udce5", "\ud835\udce6", "\ud835\udce7", "\ud835\udce8", "\ud835\udce9", "\ud835\udd04", "\ud835\udd05", "\ud835\udd07", "\ud835\udd08", "\ud835\udd09", "\ud835\udd0a", "\ud835\udd0d", "\ud835\udd0e", "\ud835\udd0f", "\ud835\udd10", "\ud835\udd11", "\ud835\udd12", "\ud835\udd13", "\ud835\udd14", "\ud835\udd16", "\ud835\udd17", "\ud835\udd18", "\ud835\udd19", "\ud835\udd1a", "\ud835\udd1b", "\ud835\udd1c", "\ud835\udd38", "\ud835\udd39", "\ud835\udd3b", "\ud835\udd3c", "\ud835\udd3d", "\ud835\udd3e", "\ud835\udd40", "\ud835\udd41", "\ud835\udd42", "\ud835\udd43", "\ud835\udd44", "\ud835\udd46", "\ud835\udd4a", "\ud835\udd4b", "\ud835\udd4c", "\ud835\udd4d", "\ud835\udd4e", "\ud835\udd4f", "\ud835\udd50", "\ud835\udd6c", "\ud835\udd6d", "\ud835\udd6e", "\ud835\udd6f", "\ud835\udd70", "\ud835\udd71", "\ud835\udd72", "\ud835\udd73", "\ud835\udd74", "\ud835\udd75", "\ud835\udd76", "\ud835\udd77", "\ud835\udd78", "\ud835\udd79", "\ud835\udd7a", "\ud835\udd7b", "\ud835\udd7c", "\ud835\udd7d", "\ud835\udd7e", "\ud835\udd7f", "\ud835\udd80", "\ud835\udd81", "\ud835\udd82", "\ud835\udd83", "\ud835\udd84", "\ud835\udd85", "\ud835\udda0", "\ud835\udda1", "\ud835\udda2", "\ud835\udda3", "\ud835\udda4", "\ud835\udda5", "\ud835\udda6", "\ud835\udda7", "\ud835\udda8", "\ud835\udda9", "\ud835\uddaa", "\ud835\uddab", "\ud835\uddac", "\ud835\uddad", "\ud835\uddae", "\ud835\uddaf", "\ud835\uddb0", "\ud835\uddb1", "\ud835\uddb2", "\ud835\uddb3", "\ud835\uddb4", "\ud835\uddb5", "\ud835\uddb6", "\ud835\uddb7", "\ud835\uddb8", "\ud835\uddb9", "\ud835\uddd4", "\ud835\uddd5", "\ud835\uddd6", "\ud835\uddd7", "\ud835\uddd8", "\ud835\uddd9", "\ud835\uddda", "\ud835\udddb", "\ud835\udddc", "\ud835\udddd", "\ud835\uddde", "\ud835\udddf", "\ud835\udde0", "\ud835\udde1", "\ud835\udde2", "\ud835\udde3", "\ud835\udde4", "\ud835\udde5", "\ud835\udde6", "\ud835\udde7", "\ud835\udde8", "\ud835\udde9", "\ud835\uddea", "\ud835\uddeb", "\ud835\uddec", "\ud835\udded", "\ud835\ude08", "\ud835\ude09", "\ud835\ude0a", "\ud835\ude0b", "\ud835\ude0c", "\ud835\ude0d", "\ud835\ude0e", "\ud835\ude0f", "\ud835\ude10", "\ud835\ude11", "\ud835\ude12", "\ud835\ude13", "\ud835\ude14", "\ud835\ude15", "\ud835\ude16", "\ud835\ude17", "\ud835\ude18", "\ud835\ude19", "\ud835\ude1a", "\ud835\ude1b", "\ud835\ude1c", "\ud835\ude1d", "\ud835\ude1e", "\ud835\ude1f", "\ud835\ude20", "\ud835\ude21", "\ud835\ude3c", "\ud835\ude3d", "\ud835\ude3e", "\ud835\ude3f", "\ud835\ude40", "\ud835\ude41", "\ud835\ude42", "\ud835\ude43", "\ud835\ude44", "\ud835\ude45", "\ud835\ude46", "\ud835\ude47", "\ud835\ude48", "\ud835\ude49", "\ud835\ude4a", "\ud835\ude4b", "\ud835\ude4c", "\ud835\ude4d", "\ud835\ude4e", "\ud835\ude4f", "\ud835\ude50", "\ud835\ude51", "\ud835\ude52", "\ud835\ude53", "\ud835\ude54", "\ud835\ude55", "\ud835\ude70", "\ud835\ude71", "\ud835\ude72", "\ud835\ude73", "\ud835\ude74", "\ud835\ude75", "\ud835\ude76", "\ud835\ude77", "\ud835\ude78", "\ud835\ude79", "\ud835\ude7a", "\ud835\ude7b", "\ud835\ude7c", "\ud835\ude7d", "\ud835\ude7e", "\ud835\ude7f", "\ud835\ude80", "\ud835\ude81", "\ud835\ude82", "\ud835\ude83", "\ud835\ude84", "\ud835\ude85", "\ud835\ude86", "\ud835\ude87", "\ud835\ude88", "\ud835\ude89", "\ud835\udea8", "\ud835\udea9", "\ud835\udeaa", "\ud835\udeab", "\ud835\udeac", "\ud835\udead", "\ud835\udeae", "\ud835\udeaf", "\ud835\udeb0", "\ud835\udeb1", "\ud835\udeb2", "\ud835\udeb3", "\ud835\udeb4", "\ud835\udeb5", "\ud835\udeb6", "\ud835\udeb7", "\ud835\udeb8", "\ud835\udeb9", "\ud835\udeba", "\ud835\udebb", "\ud835\udebc", "\ud835\udebd", "\ud835\udebe", "\ud835\udebf", "\ud835\udec0", "\ud835\udee2", "\ud835\udee3", "\ud835\udee4", "\ud835\udee5", "\ud835\udee6", "\ud835\udee7", "\ud835\udee8", "\ud835\udee9", "\ud835\udeea", "\ud835\udeeb", "\ud835\udeec", "\ud835\udeed", "\ud835\udeee", "\ud835\udeef", "\ud835\udef0", "\ud835\udef1", "\ud835\udef2", "\ud835\udef3", "\ud835\udef4", "\ud835\udef5", "\ud835\udef6", "\ud835\udef7", "\ud835\udef8", "\ud835\udef9", "\ud835\udefa", "\ud835\udf1c", "\ud835\udf1d", "\ud835\udf1e", "\ud835\udf1f", "\ud835\udf20", "\ud835\udf21", "\ud835\udf22", "\ud835\udf23", "\ud835\udf24", "\ud835\udf25", "\ud835\udf26", "\ud835\udf27", "\ud835\udf28", "\ud835\udf29", "\ud835\udf2a", "\ud835\udf2b", "\ud835\udf2c", "\ud835\udf2d", "\ud835\udf2e", "\ud835\udf2f", "\ud835\udf30", "\ud835\udf31", "\ud835\udf32", "\ud835\udf33", "\ud835\udf34", "\ud835\udf56", "\ud835\udf57", "\ud835\udf58", "\ud835\udf59", "\ud835\udf5a", "\ud835\udf5b", "\ud835\udf5c", "\ud835\udf5d", "\ud835\udf5e", "\ud835\udf5f", "\ud835\udf60", "\ud835\udf61", "\ud835\udf62", "\ud835\udf63", "\ud835\udf64", "\ud835\udf65", "\ud835\udf66", "\ud835\udf67", "\ud835\udf68", "\ud835\udf69", "\ud835\udf6a", "\ud835\udf6b", "\ud835\udf6c", "\ud835\udf6d", "\ud835\udf6e", "\ud835\udf90", "\ud835\udf91", "\ud835\udf92", "\ud835\udf93", "\ud835\udf94", "\ud835\udf95", "\ud835\udf96", "\ud835\udf97", "\ud835\udf98", "\ud835\udf99", "\ud835\udf9a", "\ud835\udf9b", "\ud835\udf9c", "\ud835\udf9d", "\ud835\udf9e", "\ud835\udf9f", "\ud835\udfa0", "\ud835\udfa1", "\ud835\udfa2", "\ud835\udfa3", "\ud835\udfa4", "\ud835\udfa5", "\ud835\udfa6", "\ud835\udfa7", "\ud835\udfa8", "\ud835\udfca", "\ud83a\udd00", "\ud83a\udd01", "\ud83a\udd02", "\ud83a\udd03", "\ud83a\udd04", "\ud83a\udd05", "\ud83a\udd06", "\ud83a\udd07", "\ud83a\udd08", "\ud83a\udd09", "\ud83a\udd0a", "\ud83a\udd0b", "\ud83a\udd0c", "\ud83a\udd0d", "\ud83a\udd0e", "\ud83a\udd0f", "\ud83a\udd10", "\ud83a\udd11", "\ud83a\udd12", "\ud83a\udd13", "\ud83a\udd14", "\ud83a\udd15", "\ud83a\udd16", "\ud83a\udd17", "\ud83a\udd18", "\ud83a\udd19", "\ud83a\udd1a", "\ud83a\udd1b", "\ud83a\udd1c", "\ud83a\udd1d", "\ud83a\udd1e", "\ud83a\udd1f", "\ud83a\udd20", "\ud83a\udd21", "\u0903", "\u093b", "\u093e", "\u093f", "\u0940", "\u0949", "\u094a", "\u094b", "\u094c", "\u094e", "\u094f", "\u0982", "\u0983", "\u09be", "\u09bf", "\u09c0", "\u09c7", "\u09c8", "\u09cb", "\u09cc", "\u09d7", "\u0a03", "\u0a3e", "\u0a3f", "\u0a40", "\u0a83", "\u0abe", "\u0abf", "\u0ac0", "\u0ac9", "\u0acb", "\u0acc", "\u0b02", "\u0b03", "\u0b3e", "\u0b40", "\u0b47", "\u0b48", "\u0b4b", "\u0b4c", "\u0b57", "\u0bbe", "\u0bbf", "\u0bc1", "\u0bc2", "\u0bc6", "\u0bc7", "\u0bc8", "\u0bca", "\u0bcb", "\u0bcc", "\u0bd7", "\u0c01", "\u0c02", "\u0c03", "\u0c41", "\u0c42", "\u0c43", "\u0c44", "\u0c82", "\u0c83", "\u0cbe", "\u0cc0", "\u0cc1", "\u0cc2", "\u0cc3", "\u0cc4", "\u0cc7", "\u0cc8", "\u0cca", "\u0ccb", "\u0cd5", "\u0cd6", "\u0d02", "\u0d03", "\u0d3e", "\u0d3f", "\u0d40", "\u0d46", "\u0d47", "\u0d48", "\u0d4a", "\u0d4b", "\u0d4c", "\u0d57", "\u0d82", "\u0d83", "\u0dcf", "\u0dd0", "\u0dd1", "\u0dd8", "\u0dd9", "\u0dda", "\u0ddb", "\u0ddc", "\u0ddd", "\u0dde", "\u0ddf", "\u0df2", "\u0df3", "\u0f3e", "\u0f3f", "\u0f7f", "\u102b", "\u102c", "\u1031", "\u1038", "\u103b", "\u103c", "\u1056", "\u1057", "\u1062", "\u1063", "\u1064", "\u1067", "\u1068", "\u1069", "\u106a", "\u106b", "\u106c", "\u106d", "\u1083", "\u1084", "\u1087", "\u1088", "\u1089", "\u108a", "\u108b", "\u108c", "\u108f", "\u109a", "\u109b", "\u109c", "\u17b6", "\u17be", "\u17bf", "\u17c0", "\u17c1", "\u17c2", "\u17c3", "\u17c4", "\u17c5", "\u17c7", "\u17c8", "\u1923", "\u1924", "\u1925", "\u1926", "\u1929", "\u192a", "\u192b", "\u1930", "\u1931", "\u1933", "\u1934", "\u1935", "\u1936", "\u1937", "\u1938", "\u1a19", "\u1a1a", "\u1a55", "\u1a57", "\u1a61", "\u1a63", "\u1a64", "\u1a6d", "\u1a6e", "\u1a6f", "\u1a70", "\u1a71", "\u1a72", "\u1b04", "\u1b35", "\u1b3b", "\u1b3d", "\u1b3e", "\u1b3f", "\u1b40", "\u1b41", "\u1b43", "\u1b44", "\u1b82", "\u1ba1", "\u1ba6", "\u1ba7", "\u1baa", "\u1be7", "\u1bea", "\u1beb", "\u1bec", "\u1bee", "\u1bf2", "\u1bf3", "\u1c24", "\u1c25", "\u1c26", "\u1c27", "\u1c28", "\u1c29", "\u1c2a", "\u1c2b", "\u1c34", "\u1c35", "\u1ce1", "\u1cf2", "\u1cf3", "\u302e", "\u302f", "\ua823", "\ua824", "\ua827", "\ua880", "\ua881", "\ua8b4", "\ua8b5", "\ua8b6", "\ua8b7", "\ua8b8", "\ua8b9", "\ua8ba", "\ua8bb", "\ua8bc", "\ua8bd", "\ua8be", "\ua8bf", "\ua8c0", "\ua8c1", "\ua8c2", "\ua8c3", "\ua952", "\ua953", "\ua983", "\ua9b4", "\ua9b5", "\ua9ba", "\ua9bb", "\ua9bd", "\ua9be", "\ua9bf", "\ua9c0", "\uaa2f", "\uaa30", "\uaa33", "\uaa34", "\uaa4d", "\uaa7b", "\uaa7d", "\uaaeb", "\uaaee", "\uaaef", "\uaaf5", "\uabe3", "\uabe4", "\uabe6", "\uabe7", "\uabe9", "\uabea", "\uabec", "\ud804\udc00", "\ud804\udc02", "\ud804\udc82", "\ud804\udcb0", "\ud804\udcb1", "\ud804\udcb2", "\ud804\udcb7", "\ud804\udcb8", "\ud804\udd2c", "\ud804\udd82", "\ud804\uddb3", "\ud804\uddb4", "\ud804\uddb5", "\ud804\uddbf", "\ud804\uddc0", "\ud804\ude2c", "\ud804\ude2d", "\ud804\ude2e", "\ud804\ude32", "\ud804\ude33", "\ud804\ude35", "\ud804\udee0", "\ud804\udee1", "\ud804\udee2", "\ud804\udf02", "\ud804\udf03", "\ud804\udf3e", "\ud804\udf3f", "\ud804\udf41", "\ud804\udf42", "\ud804\udf43", "\ud804\udf44", "\ud804\udf47", "\ud804\udf48", "\ud804\udf4b", "\ud804\udf4c", "\ud804\udf4d", "\ud804\udf57", "\ud804\udf62", "\ud804\udf63", "\ud805\udc35", "\ud805\udc36", "\ud805\udc37", "\ud805\udc40", "\ud805\udc41", "\ud805\udc45", "\ud805\udcb0", "\ud805\udcb1", "\ud805\udcb2", "\ud805\udcb9", "\ud805\udcbb", "\ud805\udcbc", "\ud805\udcbd", "\ud805\udcbe", "\ud805\udcc1", "\ud805\uddaf", "\ud805\uddb0", "\ud805\uddb1", "\ud805\uddb8", "\ud805\uddb9", "\ud805\uddba", "\ud805\uddbb", "\ud805\uddbe", "\ud805\ude30", "\ud805\ude31", "\ud805\ude32", "\ud805\ude3b", "\ud805\ude3c", "\ud805\ude3e", "\ud805\udeac", "\ud805\udeae", "\ud805\udeaf", "\ud805\udeb6", "\ud805\udf20", "\ud805\udf21", "\ud805\udf26", "\ud807\udc2f", "\ud807\udc3e", "\ud807\udca9", "\ud807\udcb1", "\ud807\udcb4", "\ud81b\udf51", "\ud81b\udf52", "\ud81b\udf53", "\ud81b\udf54", "\ud81b\udf55", "\ud81b\udf56", "\ud81b\udf57", "\ud81b\udf58", "\ud81b\udf59", "\ud81b\udf5a", "\ud81b\udf5b", "\ud81b\udf5c", "\ud81b\udf5d", "\ud81b\udf5e", "\ud81b\udf5f", "\ud81b\udf60", "\ud81b\udf61", "\ud81b\udf62", "\ud81b\udf63", "\ud81b\udf64", "\ud81b\udf65", "\ud81b\udf66", "\ud81b\udf67", "\ud81b\udf68", "\ud81b\udf69", "\ud81b\udf6a", "\ud81b\udf6b", "\ud81b\udf6c", "\ud81b\udf6d", "\ud81b\udf6e", "\ud81b\udf6f", "\ud81b\udf70", "\ud81b\udf71", "\ud81b\udf72", "\ud81b\udf73", "\ud81b\udf74", "\ud81b\udf75", "\ud81b\udf76", "\ud81b\udf77", "\ud81b\udf78", "\ud81b\udf79", "\ud81b\udf7a", "\ud81b\udf7b", "\ud81b\udf7c", "\ud81b\udf7d", "\ud81b\udf7e", "\ud834\udd65", "\ud834\udd66", "\ud834\udd6d", "\ud834\udd6e", "\ud834\udd6f", "\ud834\udd70", "\ud834\udd71", "\ud834\udd72", "\u0300", "\u0301", "\u0302", "\u0303", "\u0304", "\u0305", "\u0306", "\u0307", "\u0308", "\u0309", "\u030a", "\u030b", "\u030c", "\u030d", "\u030e", "\u030f", "\u0310", "\u0311", "\u0312", "\u0313", "\u0314", "\u0315", "\u0316", "\u0317", "\u0318", "\u0319", "\u031a", "\u031b", "\u031c", "\u031d", "\u031e", "\u031f", "\u0320", "\u0321", "\u0322", "\u0323", "\u0324", "\u0325", "\u0326", "\u0327", "\u0328", "\u0329", "\u032a", "\u032b", "\u032c", "\u032d", "\u032e", "\u032f", "\u0330", "\u0331", "\u0332", "\u0333", "\u0334", "\u0335", "\u0336", "\u0337", "\u0338", "\u0339", "\u033a", "\u033b", "\u033c", "\u033d", "\u033e", "\u033f", "\u0340", "\u0341", "\u0342", "\u0343", "\u0344", "\u0345", "\u0346", "\u0347", "\u0348", "\u0349", "\u034a", "\u034b", "\u034c", "\u034d", "\u034e", "\u034f", "\u0350", "\u0351", "\u0352", "\u0353", "\u0354", "\u0355", "\u0356", "\u0357", "\u0358", "\u0359", "\u035a", "\u035b", "\u035c", "\u035d", "\u035e", "\u035f", "\u0360", "\u0361", "\u0362", "\u0363", "\u0364", "\u0365", "\u0366", "\u0367", "\u0368", "\u0369", "\u036a", "\u036b", "\u036c", "\u036d", "\u036e", "\u036f", "\u0483", "\u0484", "\u0485", "\u0486", "\u0487", "\u0591", "\u0592", "\u0593", "\u0594", "\u0595", "\u0596", "\u0597", "\u0598", "\u0599", "\u059a", "\u059b", "\u059c", "\u059d", "\u059e", "\u059f", "\u05a0", "\u05a1", "\u05a2", "\u05a3", "\u05a4", "\u05a5", "\u05a6", "\u05a7", "\u05a8", "\u05a9", "\u05aa", "\u05ab", "\u05ac", "\u05ad", "\u05ae", "\u05af", "\u05b0", "\u05b1", "\u05b2", "\u05b3", "\u05b4", "\u05b5", "\u05b6", "\u05b7", "\u05b8", "\u05b9", "\u05ba", "\u05bb", "\u05bc", "\u05bd", "\u05bf", "\u05c1", "\u05c2", "\u05c4", "\u05c5", "\u05c7", "\u0610", "\u0611", "\u0612", "\u0613", "\u0614", "\u0615", "\u0616", "\u0617", "\u0618", "\u0619", "\u061a", "\u064b", "\u064c", "\u064d", "\u064e", "\u064f", "\u0650", "\u0651", "\u0652", "\u0653", "\u0654", "\u0655", "\u0656", "\u0657", "\u0658", "\u0659", "\u065a", "\u065b", "\u065c", "\u065d", "\u065e", "\u065f", "\u0670", "\u06d6", "\u06d7", "\u06d8", "\u06d9", "\u06da", "\u06db", "\u06dc", "\u06df", "\u06e0", "\u06e1", "\u06e2", "\u06e3", "\u06e4", "\u06e7", "\u06e8", "\u06ea", "\u06eb", "\u06ec", "\u06ed", "\u0711", "\u0730", "\u0731", "\u0732", "\u0733", "\u0734", "\u0735", "\u0736", "\u0737", "\u0738", "\u0739", "\u073a", "\u073b", "\u073c", "\u073d", "\u073e", "\u073f", "\u0740", "\u0741", "\u0742", "\u0743", "\u0744", "\u0745", "\u0746", "\u0747", "\u0748", "\u0749", "\u074a", "\u07a6", "\u07a7", "\u07a8", "\u07a9", "\u07aa", "\u07ab", "\u07ac", "\u07ad", "\u07ae", "\u07af", "\u07b0", "\u07eb", "\u07ec", "\u07ed", "\u07ee", "\u07ef", "\u07f0", "\u07f1", "\u07f2", "\u07f3", "\u0816", "\u0817", "\u0818", "\u0819", "\u081b", "\u081c", "\u081d", "\u081e", "\u081f", "\u0820", "\u0821", "\u0822", "\u0823", "\u0825", "\u0826", "\u0827", "\u0829", "\u082a", "\u082b", "\u082c", "\u082d", "\u0859", "\u085a", "\u085b", "\u08d4", "\u08d5", "\u08d6", "\u08d7", "\u08d8", "\u08d9", "\u08da", "\u08db", "\u08dc", "\u08dd", "\u08de", "\u08df", "\u08e0", "\u08e1", "\u08e3", "\u08e4", "\u08e5", "\u08e6", "\u08e7", "\u08e8", "\u08e9", "\u08ea", "\u08eb", "\u08ec", "\u08ed", "\u08ee", "\u08ef", "\u08f0", "\u08f1", "\u08f2", "\u08f3", "\u08f4", "\u08f5", "\u08f6", "\u08f7", "\u08f8", "\u08f9", "\u08fa", "\u08fb", "\u08fc", "\u08fd", "\u08fe", "\u08ff", "\u0900", "\u0901", "\u0902", "\u093a", "\u093c", "\u0941", "\u0942", "\u0943", "\u0944", "\u0945", "\u0946", "\u0947", "\u0948", "\u094d", "\u0951", "\u0952", "\u0953", "\u0954", "\u0955", "\u0956", "\u0957", "\u0962", "\u0963", "\u0981", "\u09bc", "\u09c1", "\u09c2", "\u09c3", "\u09c4", "\u09cd", "\u09e2", "\u09e3", "\u0a01", "\u0a02", "\u0a3c", "\u0a41", "\u0a42", "\u0a47", "\u0a48", "\u0a4b", "\u0a4c", "\u0a4d", "\u0a51", "\u0a70", "\u0a71", "\u0a75", "\u0a81", "\u0a82", "\u0abc", "\u0ac1", "\u0ac2", "\u0ac3", "\u0ac4", "\u0ac5", "\u0ac7", "\u0ac8", "\u0acd", "\u0ae2", "\u0ae3", "\u0b01", "\u0b3c", "\u0b3f", "\u0b41", "\u0b42", "\u0b43", "\u0b44", "\u0b4d", "\u0b56", "\u0b62", "\u0b63", "\u0b82", "\u0bc0", "\u0bcd", "\u0c00", "\u0c3e", "\u0c3f", "\u0c40", "\u0c46", "\u0c47", "\u0c48", "\u0c4a", "\u0c4b", "\u0c4c", "\u0c4d", "\u0c55", "\u0c56", "\u0c62", "\u0c63", "\u0c81", "\u0cbc", "\u0cbf", "\u0cc6", "\u0ccc", "\u0ccd", "\u0ce2", "\u0ce3", "\u0d01", "\u0d41", "\u0d42", "\u0d43", "\u0d44", "\u0d4d", "\u0d62", "\u0d63", "\u0dca", "\u0dd2", "\u0dd3", "\u0dd4", "\u0dd6", "\u0e31", "\u0e34", "\u0e35", "\u0e36", "\u0e37", "\u0e38", "\u0e39", "\u0e3a", "\u0e47", "\u0e48", "\u0e49", "\u0e4a", "\u0e4b", "\u0e4c", "\u0e4d", "\u0e4e", "\u0eb1", "\u0eb4", "\u0eb5", "\u0eb6", "\u0eb7", "\u0eb8", "\u0eb9", "\u0ebb", "\u0ebc", "\u0ec8", "\u0ec9", "\u0eca", "\u0ecb", "\u0ecc", "\u0ecd", "\u0f18", "\u0f19", "\u0f35", "\u0f37", "\u0f39", "\u0f71", "\u0f72", "\u0f73", "\u0f74", "\u0f75", "\u0f76", "\u0f77", "\u0f78", "\u0f79", "\u0f7a", "\u0f7b", "\u0f7c", "\u0f7d", "\u0f7e", "\u0f80", "\u0f81", "\u0f82", "\u0f83", "\u0f84", "\u0f86", "\u0f87", "\u0f8d", "\u0f8e", "\u0f8f", "\u0f90", "\u0f91", "\u0f92", "\u0f93", "\u0f94", "\u0f95", "\u0f96", "\u0f97", "\u0f99", "\u0f9a", "\u0f9b", "\u0f9c", "\u0f9d", "\u0f9e", "\u0f9f", "\u0fa0", "\u0fa1", "\u0fa2", "\u0fa3", "\u0fa4", "\u0fa5", "\u0fa6", "\u0fa7", "\u0fa8", "\u0fa9", "\u0faa", "\u0fab", "\u0fac", "\u0fad", "\u0fae", "\u0faf", "\u0fb0", "\u0fb1", "\u0fb2", "\u0fb3", "\u0fb4", "\u0fb5", "\u0fb6", "\u0fb7", "\u0fb8", "\u0fb9", "\u0fba", "\u0fbb", "\u0fbc", "\u0fc6", "\u102d", "\u102e", "\u102f", "\u1030", "\u1032", "\u1033", "\u1034", "\u1035", "\u1036", "\u1037", "\u1039", "\u103a", "\u103d", "\u103e", "\u1058", "\u1059", "\u105e", "\u105f", "\u1060", "\u1071", "\u1072", "\u1073", "\u1074", "\u1082", "\u1085", "\u1086", "\u108d", "\u109d", "\u135d", "\u135e", "\u135f", "\u1712", "\u1713", "\u1714", "\u1732", "\u1733", "\u1734", "\u1752", "\u1753", "\u1772", "\u1773", "\u17b4", "\u17b5", "\u17b7", "\u17b8", "\u17b9", "\u17ba", "\u17bb", "\u17bc", "\u17bd", "\u17c6", "\u17c9", "\u17ca", "\u17cb", "\u17cc", "\u17cd", "\u17ce", "\u17cf", "\u17d0", "\u17d1", "\u17d2", "\u17d3", "\u17dd", "\u180b", "\u180c", "\u180d", "\u1885", "\u1886", "\u18a9", "\u1920", "\u1921", "\u1922", "\u1927", "\u1928", "\u1932", "\u1939", "\u193a", "\u193b", "\u1a17", "\u1a18", "\u1a1b", "\u1a56", "\u1a58", "\u1a59", "\u1a5a", "\u1a5b", "\u1a5c", "\u1a5d", "\u1a5e", "\u1a60", "\u1a62", "\u1a65", "\u1a66", "\u1a67", "\u1a68", "\u1a69", "\u1a6a", "\u1a6b", "\u1a6c", "\u1a73", "\u1a74", "\u1a75", "\u1a76", "\u1a77", "\u1a78", "\u1a79", "\u1a7a", "\u1a7b", "\u1a7c", "\u1a7f", "\u1ab0", "\u1ab1", "\u1ab2", "\u1ab3", "\u1ab4", "\u1ab5", "\u1ab6", "\u1ab7", "\u1ab8", "\u1ab9", "\u1aba", "\u1abb", "\u1abc", "\u1abd", "\u1b00", "\u1b01", "\u1b02", "\u1b03", "\u1b34", "\u1b36", "\u1b37", "\u1b38", "\u1b39", "\u1b3a", "\u1b3c", "\u1b42", "\u1b6b", "\u1b6c", "\u1b6d", "\u1b6e", "\u1b6f", "\u1b70", "\u1b71", "\u1b72", "\u1b73", "\u1b80", "\u1b81", "\u1ba2", "\u1ba3", "\u1ba4", "\u1ba5", "\u1ba8", "\u1ba9", "\u1bab", "\u1bac", "\u1bad", "\u1be6", "\u1be8", "\u1be9", "\u1bed", "\u1bef", "\u1bf0", "\u1bf1", "\u1c2c", "\u1c2d", "\u1c2e", "\u1c2f", "\u1c30", "\u1c31", "\u1c32", "\u1c33", "\u1c36", "\u1c37", "\u1cd0", "\u1cd1", "\u1cd2", "\u1cd4", "\u1cd5", "\u1cd6", "\u1cd7", "\u1cd8", "\u1cd9", "\u1cda", "\u1cdb", "\u1cdc", "\u1cdd", "\u1cde", "\u1cdf", "\u1ce0", "\u1ce2", "\u1ce3", "\u1ce4", "\u1ce5", "\u1ce6", "\u1ce7", "\u1ce8", "\u1ced", "\u1cf4", "\u1cf8", "\u1cf9", "\u1dc0", "\u1dc1", "\u1dc2", "\u1dc3", "\u1dc4", "\u1dc5", "\u1dc6", "\u1dc7", "\u1dc8", "\u1dc9", "\u1dca", "\u1dcb", "\u1dcc", "\u1dcd", "\u1dce", "\u1dcf", "\u1dd0", "\u1dd1", "\u1dd2", "\u1dd3", "\u1dd4", "\u1dd5", "\u1dd6", "\u1dd7", "\u1dd8", "\u1dd9", "\u1dda", "\u1ddb", "\u1ddc", "\u1ddd", "\u1dde", "\u1ddf", "\u1de0", "\u1de1", "\u1de2", "\u1de3", "\u1de4", "\u1de5", "\u1de6", "\u1de7", "\u1de8", "\u1de9", "\u1dea", "\u1deb", "\u1dec", "\u1ded", "\u1dee", "\u1def", "\u1df0", "\u1df1", "\u1df2", "\u1df3", "\u1df4", "\u1df5", "\u1dfb", "\u1dfc", "\u1dfd", "\u1dfe", "\u1dff", "\u20d0", "\u20d1", "\u20d2", "\u20d3", "\u20d4", "\u20d5", "\u20d6", "\u20d7", "\u20d8", "\u20d9", "\u20da", "\u20db", "\u20dc", "\u20e1", "\u20e5", "\u20e6", "\u20e7", "\u20e8", "\u20e9", "\u20ea", "\u20eb", "\u20ec", "\u20ed", "\u20ee", "\u20ef", "\u20f0", "\u2cef", "\u2cf0", "\u2cf1", "\u2d7f", "\u2de0", "\u2de1", "\u2de2", "\u2de3", "\u2de4", "\u2de5", "\u2de6", "\u2de7", "\u2de8", "\u2de9", "\u2dea", "\u2deb", "\u2dec", "\u2ded", "\u2dee", "\u2def", "\u2df0", "\u2df1", "\u2df2", "\u2df3", "\u2df4", "\u2df5", "\u2df6", "\u2df7", "\u2df8", "\u2df9", "\u2dfa", "\u2dfb", "\u2dfc", "\u2dfd", "\u2dfe", "\u2dff", "\u302a", "\u302b", "\u302c", "\u302d", "\u3099", "\u309a", "\ua66f", "\ua674", "\ua675", "\ua676", "\ua677", "\ua678", "\ua679", "\ua67a", "\ua67b", "\ua67c", "\ua67d", "\ua69e", "\ua69f", "\ua6f0", "\ua6f1", "\ua802", "\ua806", "\ua80b", "\ua825", "\ua826", "\ua8c4", "\ua8c5", "\ua8e0", "\ua8e1", "\ua8e2", "\ua8e3", "\ua8e4", "\ua8e5", "\ua8e6", "\ua8e7", "\ua8e8", "\ua8e9", "\ua8ea", "\ua8eb", "\ua8ec", "\ua8ed", "\ua8ee", "\ua8ef", "\ua8f0", "\ua8f1", "\ua926", "\ua927", "\ua928", "\ua929", "\ua92a", "\ua92b", "\ua92c", "\ua92d", "\ua947", "\ua948", "\ua949", "\ua94a", "\ua94b", "\ua94c", "\ua94d", "\ua94e", "\ua94f", "\ua950", "\ua951", "\ua980", "\ua981", "\ua982", "\ua9b3", "\ua9b6", "\ua9b7", "\ua9b8", "\ua9b9", "\ua9bc", "\ua9e5", "\uaa29", "\uaa2a", "\uaa2b", "\uaa2c", "\uaa2d", "\uaa2e", "\uaa31", "\uaa32", "\uaa35", "\uaa36", "\uaa43", "\uaa4c", "\uaa7c", "\uaab0", "\uaab2", "\uaab3", "\uaab4", "\uaab7", "\uaab8", "\uaabe", "\uaabf", "\uaac1", "\uaaec", "\uaaed", "\uaaf6", "\uabe5", "\uabe8", "\uabed", "\ufb1e", "\ufe00", "\ufe01", "\ufe02", "\ufe03", "\ufe04", "\ufe05", "\ufe06", "\ufe07", "\ufe08", "\ufe09", "\ufe0a", "\ufe0b", "\ufe0c", "\ufe0d", "\ufe0e", "\ufe0f", "\ufe20", "\ufe21", "\ufe22", "\ufe23", "\ufe24", "\ufe25", "\ufe26", "\ufe27", "\ufe28", "\ufe29", "\ufe2a", "\ufe2b", "\ufe2c", "\ufe2d", "\ufe2e", "\ufe2f", "\ud800\uddfd", "\ud800\udee0", "\ud800\udf76", "\ud800\udf77", "\ud800\udf78", "\ud800\udf79", "\ud800\udf7a", "\ud802\ude01", "\ud802\ude02", "\ud802\ude03", "\ud802\ude05", "\ud802\ude06", "\ud802\ude0c", "\ud802\ude0d", "\ud802\ude0e", "\ud802\ude0f", "\ud802\ude38", "\ud802\ude39", "\ud802\ude3a", "\ud802\ude3f", "\ud802\udee5", "\ud802\udee6", "\ud804\udc01", "\ud804\udc38", "\ud804\udc39", "\ud804\udc3a", "\ud804\udc3b", "\ud804\udc3c", "\ud804\udc3d", "\ud804\udc3e", "\ud804\udc3f", "\ud804\udc40", "\ud804\udc41", "\ud804\udc42", "\ud804\udc43", "\ud804\udc44", "\ud804\udc45", "\ud804\udc46", "\ud804\udc7f", "\ud804\udc80", "\ud804\udc81", "\ud804\udcb3", "\ud804\udcb4", "\ud804\udcb5", "\ud804\udcb6", "\ud804\udcb9", "\ud804\udcba", "\ud804\udd00", "\ud804\udd01", "\ud804\udd02", "\ud804\udd27", "\ud804\udd28", "\ud804\udd29", "\ud804\udd2a", "\ud804\udd2b", "\ud804\udd2d", "\ud804\udd2e", "\ud804\udd2f", "\ud804\udd30", "\ud804\udd31", "\ud804\udd32", "\ud804\udd33", "\ud804\udd34", "\ud804\udd73", "\ud804\udd80", "\ud804\udd81", "\ud804\uddb6", "\ud804\uddb7", "\ud804\uddb8", "\ud804\uddb9", "\ud804\uddba", "\ud804\uddbb", "\ud804\uddbc", "\ud804\uddbd", "\ud804\uddbe", "\ud804\uddca", "\ud804\uddcb", "\ud804\uddcc", "\ud804\ude2f", "\ud804\ude30", "\ud804\ude31", "\ud804\ude34", "\ud804\ude36", "\ud804\ude37", "\ud804\ude3e", "\ud804\udedf", "\ud804\udee3", "\ud804\udee4", "\ud804\udee5", "\ud804\udee6", "\ud804\udee7", "\ud804\udee8", "\ud804\udee9", "\ud804\udeea", "\ud804\udf00", "\ud804\udf01", "\ud804\udf3c", "\ud804\udf40", "\ud804\udf66", "\ud804\udf67", "\ud804\udf68", "\ud804\udf69", "\ud804\udf6a", "\ud804\udf6b", "\ud804\udf6c", "\ud804\udf70", "\ud804\udf71", "\ud804\udf72", "\ud804\udf73", "\ud804\udf74", "\ud805\udc38", "\ud805\udc39", "\ud805\udc3a", "\ud805\udc3b", "\ud805\udc3c", "\ud805\udc3d", "\ud805\udc3e", "\ud805\udc3f", "\ud805\udc42", "\ud805\udc43", "\ud805\udc44", "\ud805\udc46", "\ud805\udcb3", "\ud805\udcb4", "\ud805\udcb5", "\ud805\udcb6", "\ud805\udcb7", "\ud805\udcb8", "\ud805\udcba", "\ud805\udcbf", "\ud805\udcc0", "\ud805\udcc2", "\ud805\udcc3", "\ud805\uddb2", "\ud805\uddb3", "\ud805\uddb4", "\ud805\uddb5", "\ud805\uddbc", "\ud805\uddbd", "\ud805\uddbf", "\ud805\uddc0", "\ud805\udddc", "\ud805\udddd", "\ud805\ude33", "\ud805\ude34", "\ud805\ude35", "\ud805\ude36", "\ud805\ude37", "\ud805\ude38", "\ud805\ude39", "\ud805\ude3a", "\ud805\ude3d", "\ud805\ude3f", "\ud805\ude40", "\ud805\udeab", "\ud805\udead", "\ud805\udeb0", "\ud805\udeb1", "\ud805\udeb2", "\ud805\udeb3", "\ud805\udeb4", "\ud805\udeb5", "\ud805\udeb7", "\ud805\udf1d", "\ud805\udf1e", "\ud805\udf1f", "\ud805\udf22", "\ud805\udf23", "\ud805\udf24", "\ud805\udf25", "\ud805\udf27", "\ud805\udf28", "\ud805\udf29", "\ud805\udf2a", "\ud805\udf2b", "\ud807\udc30", "\ud807\udc31", "\ud807\udc32", "\ud807\udc33", "\ud807\udc34", "\ud807\udc35", "\ud807\udc36", "\ud807\udc38", "\ud807\udc39", "\ud807\udc3a", "\ud807\udc3b", "\ud807\udc3c", "\ud807\udc3d", "\ud807\udc3f", "\ud807\udc92", "\ud807\udc93", "\ud807\udc94", "\ud807\udc95", "\ud807\udc96", "\ud807\udc97", "\ud807\udc98", "\ud807\udc99", "\ud807\udc9a", "\ud807\udc9b", "\ud807\udc9c", "\ud807\udc9d", "\ud807\udc9e", "\ud807\udc9f", "\ud807\udca0", "\ud807\udca1", "\ud807\udca2", "\ud807\udca3", "\ud807\udca4", "\ud807\udca5", "\ud807\udca6", "\ud807\udca7", "\ud807\udcaa", "\ud807\udcab", "\ud807\udcac", "\ud807\udcad", "\ud807\udcae", "\ud807\udcaf", "\ud807\udcb0", "\ud807\udcb2", "\ud807\udcb3", "\ud807\udcb5", "\ud807\udcb6", "\ud81a\udef0", "\ud81a\udef1", "\ud81a\udef2", "\ud81a\udef3", "\ud81a\udef4", "\ud81a\udf30", "\ud81a\udf31", "\ud81a\udf32", "\ud81a\udf33", "\ud81a\udf34", "\ud81a\udf35", "\ud81a\udf36", "\ud81b\udf8f", "\ud81b\udf90", "\ud81b\udf91", "\ud81b\udf92", "\ud82f\udc9d", "\ud82f\udc9e", "\ud834\udd67", "\ud834\udd68", "\ud834\udd69", "\ud834\udd7b", "\ud834\udd7c", "\ud834\udd7d", "\ud834\udd7e", "\ud834\udd7f", "\ud834\udd80", "\ud834\udd81", "\ud834\udd82", "\ud834\udd85", "\ud834\udd86", "\ud834\udd87", "\ud834\udd88", "\ud834\udd89", "\ud834\udd8a", "\ud834\udd8b", "\ud834\uddaa", "\ud834\uddab", "\ud834\uddac", "\ud834\uddad", "\ud834\ude42", "\ud834\ude43", "\ud834\ude44", "\ud836\ude00", "\ud836\ude01", "\ud836\ude02", "\ud836\ude03", "\ud836\ude04", "\ud836\ude05", "\ud836\ude06", "\ud836\ude07", "\ud836\ude08", "\ud836\ude09", "\ud836\ude0a", "\ud836\ude0b", "\ud836\ude0c", "\ud836\ude0d", "\ud836\ude0e", "\ud836\ude0f", "\ud836\ude10", "\ud836\ude11", "\ud836\ude12", "\ud836\ude13", "\ud836\ude14", "\ud836\ude15", "\ud836\ude16", "\ud836\ude17", "\ud836\ude18", "\ud836\ude19", "\ud836\ude1a", "\ud836\ude1b", "\ud836\ude1c", "\ud836\ude1d", "\ud836\ude1e", "\ud836\ude1f", "\ud836\ude20", "\ud836\ude21", "\ud836\ude22", "\ud836\ude23", "\ud836\ude24", "\ud836\ude25", "\ud836\ude26", "\ud836\ude27", "\ud836\ude28", "\ud836\ude29", "\ud836\ude2a", "\ud836\ude2b", "\ud836\ude2c", "\ud836\ude2d", "\ud836\ude2e", "\ud836\ude2f", "\ud836\ude30", "\ud836\ude31", "\ud836\ude32", "\ud836\ude33", "\ud836\ude34", "\ud836\ude35", "\ud836\ude36", "\ud836\ude3b", "\ud836\ude3c", "\ud836\ude3d", "\ud836\ude3e", "\ud836\ude3f", "\ud836\ude40", "\ud836\ude41", "\ud836\ude42", "\ud836\ude43", "\ud836\ude44", "\ud836\ude45", "\ud836\ude46", "\ud836\ude47", "\ud836\ude48", "\ud836\ude49", "\ud836\ude4a", "\ud836\ude4b", "\ud836\ude4c", "\ud836\ude4d", "\ud836\ude4e", "\ud836\ude4f", "\ud836\ude50", "\ud836\ude51", "\ud836\ude52", "\ud836\ude53", "\ud836\ude54", "\ud836\ude55", "\ud836\ude56", "\ud836\ude57", "\ud836\ude58", "\ud836\ude59", "\ud836\ude5a", "\ud836\ude5b", "\ud836\ude5c", "\ud836\ude5d", "\ud836\ude5e", "\ud836\ude5f", "\ud836\ude60", "\ud836\ude61", "\ud836\ude62", "\ud836\ude63", "\ud836\ude64", "\ud836\ude65", "\ud836\ude66", "\ud836\ude67", "\ud836\ude68", "\ud836\ude69", "\ud836\ude6a", "\ud836\ude6b", "\ud836\ude6c", "\ud836\ude75", "\ud836\ude84", "\ud836\ude9b", "\ud836\ude9c", "\ud836\ude9d", "\ud836\ude9e", "\ud836\ude9f", "\ud836\udea1", "\ud836\udea2", "\ud836\udea3", "\ud836\udea4", "\ud836\udea5", "\ud836\udea6", "\ud836\udea7", "\ud836\udea8", "\ud836\udea9", "\ud836\udeaa", "\ud836\udeab", "\ud836\udeac", "\ud836\udead", "\ud836\udeae", "\ud836\udeaf", "\ud838\udc00", "\ud838\udc01", "\ud838\udc02", "\ud838\udc03", "\ud838\udc04", "\ud838\udc05", "\ud838\udc06", "\ud838\udc08", "\ud838\udc09", "\ud838\udc0a", "\ud838\udc0b", "\ud838\udc0c", "\ud838\udc0d", "\ud838\udc0e", "\ud838\udc0f", "\ud838\udc10", "\ud838\udc11", "\ud838\udc12", "\ud838\udc13", "\ud838\udc14", "\ud838\udc15", "\ud838\udc16", "\ud838\udc17", "\ud838\udc18", "\ud838\udc1b", "\ud838\udc1c", "\ud838\udc1d", "\ud838\udc1e", "\ud838\udc1f", "\ud838\udc20", "\ud838\udc21", "\ud838\udc23", "\ud838\udc24", "\ud838\udc26", "\ud838\udc27", "\ud838\udc28", "\ud838\udc29", "\ud838\udc2a", "\ud83a\udcd0", "\ud83a\udcd1", "\ud83a\udcd2", "\ud83a\udcd3", "\ud83a\udcd4", "\ud83a\udcd5", "\ud83a\udcd6", "\ud83a\udd44", "\ud83a\udd45", "\ud83a\udd46", "\ud83a\udd47", "\ud83a\udd48", "\ud83a\udd49", "\ud83a\udd4a", "\udb40\udd00", "\udb40\udd01", "\udb40\udd02", "\udb40\udd03", "\udb40\udd04", "\udb40\udd05", "\udb40\udd06", "\udb40\udd07", "\udb40\udd08", "\udb40\udd09", "\udb40\udd0a", "\udb40\udd0b", "\udb40\udd0c", "\udb40\udd0d", "\udb40\udd0e", "\udb40\udd0f", "\udb40\udd10", "\udb40\udd11", "\udb40\udd12", "\udb40\udd13", "\udb40\udd14", "\udb40\udd15", "\udb40\udd16", "\udb40\udd17", "\udb40\udd18", "\udb40\udd19", "\udb40\udd1a", "\udb40\udd1b", "\udb40\udd1c", "\udb40\udd1d", "\udb40\udd1e", "\udb40\udd1f", "\udb40\udd20", "\udb40\udd21", "\udb40\udd22", "\udb40\udd23", "\udb40\udd24", "\udb40\udd25", "\udb40\udd26", "\udb40\udd27", "\udb40\udd28", "\udb40\udd29", "\udb40\udd2a", "\udb40\udd2b", "\udb40\udd2c", "\udb40\udd2d", "\udb40\udd2e", "\udb40\udd2f", "\udb40\udd30", "\udb40\udd31", "\udb40\udd32", "\udb40\udd33", "\udb40\udd34", "\udb40\udd35", "\udb40\udd36", "\udb40\udd37", "\udb40\udd38", "\udb40\udd39", "\udb40\udd3a", "\udb40\udd3b", "\udb40\udd3c", "\udb40\udd3d", "\udb40\udd3e", "\udb40\udd3f", "\udb40\udd40", "\udb40\udd41", "\udb40\udd42", "\udb40\udd43", "\udb40\udd44", "\udb40\udd45", "\udb40\udd46", "\udb40\udd47", "\udb40\udd48", "\udb40\udd49", "\udb40\udd4a", "\udb40\udd4b", "\udb40\udd4c", "\udb40\udd4d", "\udb40\udd4e", "\udb40\udd4f", "\udb40\udd50", "\udb40\udd51", "\udb40\udd52", "\udb40\udd53", "\udb40\udd54", "\udb40\udd55", "\udb40\udd56", "\udb40\udd57", "\udb40\udd58", "\udb40\udd59", "\udb40\udd5a", "\udb40\udd5b", "\udb40\udd5c", "\udb40\udd5d", "\udb40\udd5e", "\udb40\udd5f", "\udb40\udd60", "\udb40\udd61", "\udb40\udd62", "\udb40\udd63", "\udb40\udd64", "\udb40\udd65", "\udb40\udd66", "\udb40\udd67", "\udb40\udd68", "\udb40\udd69", "\udb40\udd6a", "\udb40\udd6b", "\udb40\udd6c", "\udb40\udd6d", "\udb40\udd6e", "\udb40\udd6f", "\udb40\udd70", "\udb40\udd71", "\udb40\udd72", "\udb40\udd73", "\udb40\udd74", "\udb40\udd75", "\udb40\udd76", "\udb40\udd77", "\udb40\udd78", "\udb40\udd79", "\udb40\udd7a", "\udb40\udd7b", "\udb40\udd7c", "\udb40\udd7d", "\udb40\udd7e", "\udb40\udd7f", "\udb40\udd80", "\udb40\udd81", "\udb40\udd82", "\udb40\udd83", "\udb40\udd84", "\udb40\udd85", "\udb40\udd86", "\udb40\udd87", "\udb40\udd88", "\udb40\udd89", "\udb40\udd8a", "\udb40\udd8b", "\udb40\udd8c", "\udb40\udd8d", "\udb40\udd8e", "\udb40\udd8f", "\udb40\udd90", "\udb40\udd91", "\udb40\udd92", "\udb40\udd93", "\udb40\udd94", "\udb40\udd95", "\udb40\udd96", "\udb40\udd97", "\udb40\udd98", "\udb40\udd99", "\udb40\udd9a", "\udb40\udd9b", "\udb40\udd9c", "\udb40\udd9d", "\udb40\udd9e", "\udb40\udd9f", "\udb40\udda0", "\udb40\udda1", "\udb40\udda2", "\udb40\udda3", "\udb40\udda4", "\udb40\udda5", "\udb40\udda6", "\udb40\udda7", "\udb40\udda8", "\udb40\udda9", "\udb40\uddaa", "\udb40\uddab", "\udb40\uddac", "\udb40\uddad", "\udb40\uddae", "\udb40\uddaf", "\udb40\uddb0", "\udb40\uddb1", "\udb40\uddb2", "\udb40\uddb3", "\udb40\uddb4", "\udb40\uddb5", "\udb40\uddb6", "\udb40\uddb7", "\udb40\uddb8", "\udb40\uddb9", "\udb40\uddba", "\udb40\uddbb", "\udb40\uddbc", "\udb40\uddbd", "\udb40\uddbe", "\udb40\uddbf", "\udb40\uddc0", "\udb40\uddc1", "\udb40\uddc2", "\udb40\uddc3", "\udb40\uddc4", "\udb40\uddc5", "\udb40\uddc6", "\udb40\uddc7", "\udb40\uddc8", "\udb40\uddc9", "\udb40\uddca", "\udb40\uddcb", "\udb40\uddcc", "\udb40\uddcd", "\udb40\uddce", "\udb40\uddcf", "\udb40\uddd0", "\udb40\uddd1", "\udb40\uddd2", "\udb40\uddd3", "\udb40\uddd4", "\udb40\uddd5", "\udb40\uddd6", "\udb40\uddd7", "\udb40\uddd8", "\udb40\uddd9", "\udb40\uddda", "\udb40\udddb", "\udb40\udddc", "\udb40\udddd", "\udb40\uddde", "\udb40\udddf", "\udb40\udde0", "\udb40\udde1", "\udb40\udde2", "\udb40\udde3", "\udb40\udde4", "\udb40\udde5", "\udb40\udde6", "\udb40\udde7", "\udb40\udde8", "\udb40\udde9", "\udb40\uddea", "\udb40\uddeb", "\udb40\uddec", "\udb40\udded", "\udb40\uddee", "\udb40\uddef"] \ No newline at end of file diff --git a/build/lib/better_profanity/profanity.py b/build/lib/better_profanity/profanity.py deleted file mode 100644 index feabdc5..0000000 --- a/build/lib/better_profanity/profanity.py +++ /dev/null @@ -1,179 +0,0 @@ -from itertools import product -from typing import Set, List -from .utils import (ALLOWED_CHARACTERS, get_complete_path_of_file, - get_next_words, get_start_index_of_next_word, - load_unicode_symbols, any_next_words_form_swear_word) - -## GLOBAL VARIABLES ## -CENSOR_WORDSET = set() -CHARS_MAPPING = { - 'a': ('a', '@', '*', '4'), - 'i': ('i', '*', 'l', '1'), - 'o': ('o', '*', '0', '@'), - 'u': ('u', '*', 'v'), - 'v': ('v', '*', 'u'), - 'l': ('l', '1'), - 'e': ('e', '*', '3'), - 's': ('s', '$'), -} - -# Pre-load the unicode characters -load_unicode_symbols() - -# The max number of additional words forming a swear word. For example: -# - hand job = 1 -# - this is a fish = 3 -MAX_NUMBER_COMBINATIONS = 1 - - -def count_non_allowed_characters(word: str) -> int: - count = 0 - for char in iter(word): - if char not in ALLOWED_CHARACTERS: - count += 1 - return count - -def load_censor_words(custom_words: List = None): - """Generate a set of words that need to be censored.""" - global CENSOR_WORDSET - global MAX_NUMBER_COMBINATIONS - - # Replace the words from `profanity_wordlist.txt` with a custom list - if custom_words: - temp_words = custom_words - else: - temp_words = read_wordlist() - - all_censor_words = set() - for word in temp_words: - # All words in CENSOR_WORDSET must be in lowercase - word = word.lower() - num_of_non_allowed_chars = count_non_allowed_characters(word) - if num_of_non_allowed_chars > MAX_NUMBER_COMBINATIONS: - MAX_NUMBER_COMBINATIONS = num_of_non_allowed_chars - - all_censor_words.update(set(generate_patterns_from_word(word))) - - # The default wordlist takes ~5MB+ of memory - CENSOR_WORDSET = all_censor_words - - -def generate_patterns_from_word(word: str) -> Set[str]: - """Return all patterns can be generated from the word.""" - combos = [ - (char,) if char not in CHARS_MAPPING else CHARS_MAPPING[char] - for char in iter(word) - ] - return (''.join(pattern) for pattern in product(*combos)) - - -def read_wordlist() -> Set[str]: - """Return words from file `profanity_wordlist.txt`.""" - wordlist_filename = 'profanity_wordlist.txt' - wordlist_path = get_complete_path_of_file(wordlist_filename) - try: - with open(wordlist_path, encoding='utf-8') as wordlist_file: - for row in iter(wordlist_file): - row = row.strip() - if row != "": - yield row - except FileNotFoundError: - print('Unable to find profanity_wordlist.txt') - pass - - -def get_replacement_for_swear_word(censor_char: str) -> str: - return censor_char * 4 - - -def contains_profanity(text: str) -> bool: - """Return True if the input text has any swear words.""" - return text != censor(text) - - -def update_next_words_indices( - text: str, words_indices: List[tuple], start_idx: int -) -> List[tuple]: - """Return a list of next words_indices after the input index.""" - if not words_indices: - words_indices = get_next_words(text, start_idx, MAX_NUMBER_COMBINATIONS) - else: - del words_indices[:2] - if words_indices and words_indices[-1][0] != "": - words_indices += get_next_words(text, words_indices[-1][1], 1) - return words_indices - - -def hide_swear_words(text: str, censor_char: str) -> str: - """Replace the swear words with censor characters.""" - censored_text = "" - cur_word = "" - skip_index = -1 - skip_cur_char = False - next_words_indices = [] - start_idx_of_next_word = get_start_index_of_next_word(text, 0) - - # If there are no words in the text, return the raw text without parsing - if start_idx_of_next_word >= len(text) - 1: - return text - - # Left strip the text, to avoid inaccurate parsing - if start_idx_of_next_word > 0: - censored_text = text[:start_idx_of_next_word] - text = text[start_idx_of_next_word:] - - # Splitting each word in the text to compare with censored words - # for index in iter(range(start_idx_of_next_word, len(text))): - # char = text[index] - for index, char in iter(enumerate(text)): - if index < skip_index: - continue - if char in ALLOWED_CHARACTERS: - cur_word += char - continue - - # Skip continuous non-allowed characters - if cur_word.strip() == "": - censored_text += char - cur_word = "" - continue - - # Iterate the next words combined with the current one - # to check if it forms a swear word - next_words_indices = update_next_words_indices(text, next_words_indices, index) - contains_swear_word, end_index = any_next_words_form_swear_word( - cur_word, text, next_words_indices, CENSOR_WORDSET - ) - if contains_swear_word: - cur_word = get_replacement_for_swear_word(censor_char) - skip_index = end_index - char = "" - next_words_indices = [] - - # If the current a swear word - if cur_word.lower() in CENSOR_WORDSET: - cur_word = get_replacement_for_swear_word(censor_char) - - censored_text += cur_word - censored_text += char - cur_word = "" - - # Final check - if cur_word != "" and skip_index < len(text): - if cur_word.lower() in CENSOR_WORDSET: - cur_word = get_replacement_for_swear_word(censor_char) - censored_text += cur_word - return censored_text - - -def censor(text: str, censor_char: str = '*') -> str: - """Replace the swear words in the text with `censor_char`.""" - - if not isinstance(text, str): - text = str(text) - if not isinstance(censor_char, str): - censor_char = str(censor_char) - - if not CENSOR_WORDSET: - load_censor_words() - return hide_swear_words(text, censor_char) diff --git a/build/lib/better_profanity/profanity_wordlist.txt b/build/lib/better_profanity/profanity_wordlist.txt deleted file mode 100644 index 9e0ed26..0000000 --- a/build/lib/better_profanity/profanity_wordlist.txt +++ /dev/null @@ -1,140 +0,0 @@ -2g1c -2 girls 1 cup -anal -ass -asshole -arsehole -assmunch -auto erotic -autoerotic -ballsack -bastard -bdsm -bestiality -bitch -bich -bimbo -bimbos -blowjob -blow job -blue waffle -boob -boobs -booty call -brown shower -brown showers -boner -bondage -bukake -bukkake -bullshit -bull shit -busty -clitoris -cock -cocks -cow girl -cow girls -cowgirl -cowgirls -crotch -cum -cumming -cuming -cunt -dick -dildo -deepthroat -deep throat -dog style -doggie style -doggiestyle -doggy style -doggystyle -dyke -erotic -erotism -fag -faggot -femdom -fingering -footjob -foot job -fuck -futanari -futanary -gangbang -gang bang -gokkun -golden shower -goldenshower -gay -handjob -hand job -hentai -hooker -horny -incest -jerkoff -jerk off -jizz -kinbaku -lesbian -masturbate -motherfucker -milf -muff -nigga -nigger -nigg -nipple -nipples -nude -nudes -orgy -panty -panties -penis -piss -playboy -porn -porno -pornography -pussy -rape -raping -rapist -s&m -s & m -sadism -scrotum -sex -semen -shemale -she male -shibari -shibary -shit -shota -slut -smegma -spunk -strip club -stripclub -suck -sucks -tit -tits -titties -titty -threesome -three some -throating -twat -vagina -wank -whore -xxx -xx -yaoi -yury \ No newline at end of file diff --git a/build/lib/better_profanity/utils.py b/build/lib/better_profanity/utils.py deleted file mode 100644 index 42463b8..0000000 --- a/build/lib/better_profanity/utils.py +++ /dev/null @@ -1,95 +0,0 @@ -import json -import os.path -from collections import defaultdict -from string import ascii_letters, digits -from typing import List, Set, Tuple - - -## GLOBAL VARIABLES ## -ALLOWED_CHARACTERS = set(ascii_letters) -ALLOWED_CHARACTERS.update(set(digits)) -ALLOWED_CHARACTERS.update({'@', '$', '*', '\"', '\''}) - - -def get_complete_path_of_file(filename: str) -> str: - """Join the path of the current directory with the input filename.""" - root = os.path.abspath(os.path.dirname(__file__)) - return os.path.join(root, filename) - - -def load_unicode_symbols(unicode_symbols_json: str = "alphabetic_unicode.json"): - """Load the unicode characters from categories Ll, Lu, Mc, Mn into `ALLOWED_CHARACTERS`.""" - # More about Unicode categories can be found at - # https://en.wikipedia.org/wiki/Template:General_Category_(Unicode) - with open(get_complete_path_of_file(unicode_symbols_json), "r") as json_file: - ALLOWED_CHARACTERS.update(json.load(json_file)) - - -def get_start_index_of_next_word(text: str, start_idx: int) -> int: - start_idx_of_next_word = len(text) - for index in iter(range(start_idx, len(text))): - if text[index] not in ALLOWED_CHARACTERS: - continue - start_idx_of_next_word = index - break - - return start_idx_of_next_word - - -def get_next_word_and_end_index(text: str, start_idx: int): - next_word = "" - index = start_idx - for index in iter(range(start_idx, len(text))): - char = text[index] - if char in ALLOWED_CHARACTERS: - next_word += char - continue - break - return next_word, index - - -def any_next_words_form_swear_word( - cur_word: str, text: str, words_indices: List[tuple], censor_words: Set[str] -) -> Tuple[bool, int]: - """Return True, and the end index of the word in the text, if any word formed in words_indices is in `CENSOR_WORDSET`.""" - full_word = cur_word.lower() - full_word_with_separators = cur_word.lower() - - # Check both words in the pairs - for index in iter(range(0, len(words_indices), 2)): - single_word, end_index = words_indices[index] - word_with_separators, _ = words_indices[index + 1] - if single_word == "": - continue - - full_word = "%s%s" % (full_word, single_word.lower()) - full_word_with_separators = "%s%s" % (full_word_with_separators, word_with_separators.lower()) - if full_word in censor_words or full_word_with_separators in censor_words: - return True, end_index - return False, -1 - - -def get_next_words(text: str, start_idx: int, num_of_next_words: int = 1) -> List[Tuple[str, int]]: - """ - Return a list of pairs of next words and next words included with separators, combined with their end indices. - For example: Word `hand_job` has next words pairs: `job`, `_job`. - """ - - # Find the starting index of the next word - start_idx_of_next_word = get_start_index_of_next_word(text, start_idx) - - # Return an empty string if there are no other words - if start_idx_of_next_word >= len(text) - 1: - return [("", start_idx_of_next_word), ("", start_idx_of_next_word)] - - # Combine the words into a list - next_word, end_index = get_next_word_and_end_index(text, start_idx_of_next_word) - - words = [ - (next_word, end_index), - ("%s%s" % (text[start_idx:start_idx_of_next_word], next_word), end_index) - ] - if num_of_next_words > 1: - words.extend(get_next_words(text, end_index, num_of_next_words - 1)) - - return words diff --git a/dist/better_profanity-0.3.2-py3-none-any.whl b/dist/better_profanity-0.3.2-py3-none-any.whl deleted file mode 100644 index af7d8537a5e170fc61956e65445c6802730297b1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39970 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 diff --git a/dist/better_profanity-0.3.2.tar.gz b/dist/better_profanity-0.3.2.tar.gz deleted file mode 100644 index a5838a7c87f20c858d52f9330fdb2ab4a5ca7811..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23474 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

g7;BUpVA&#PFyN8Fcqyp;) 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 Date: Mon, 13 May 2019 20:30:42 +0800 Subject: [PATCH 13/30] Version 0.3.3 - Fix incompatibility with Python 3.5 --- better_profanity/__init__.py | 2 +- better_profanity/profanity.py | 7 ++----- setup.py | 2 +- tests.py | 15 +++++++++++++++ 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/better_profanity/__init__.py b/better_profanity/__init__.py index 56d7564..954e344 100644 --- a/better_profanity/__init__.py +++ b/better_profanity/__init__.py @@ -1,2 +1,2 @@ name = 'better_profanity' -__version__ = '0.3.2' +__version__ = '0.3.3' diff --git a/better_profanity/profanity.py b/better_profanity/profanity.py index feabdc5..1d95136 100644 --- a/better_profanity/profanity.py +++ b/better_profanity/profanity.py @@ -123,8 +123,6 @@ def hide_swear_words(text: str, censor_char: str) -> str: text = text[start_idx_of_next_word:] # Splitting each word in the text to compare with censored words - # for index in iter(range(start_idx_of_next_word, len(text))): - # char = text[index] for index, char in iter(enumerate(text)): if index < skip_index: continue @@ -154,12 +152,11 @@ def hide_swear_words(text: str, censor_char: str) -> str: if cur_word.lower() in CENSOR_WORDSET: cur_word = get_replacement_for_swear_word(censor_char) - censored_text += cur_word - censored_text += char + censored_text += cur_word + char cur_word = "" # Final check - if cur_word != "" and skip_index < len(text): + if cur_word != "" and skip_index < len(text) - 1: if cur_word.lower() in CENSOR_WORDSET: cur_word = get_replacement_for_swear_word(censor_char) censored_text += cur_word diff --git a/setup.py b/setup.py index 0a450ba..54410c3 100644 --- a/setup.py +++ b/setup.py @@ -21,7 +21,7 @@ "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", ], - python_requires=">3.6", + python_requires=">=3.5", packages=setuptools.find_packages(), data_files=[ ("wordlist", ["better_profanity/profanity_wordlist.txt"]), diff --git a/tests.py b/tests.py index e60434a..607957f 100644 --- a/tests.py +++ b/tests.py @@ -40,6 +40,21 @@ def test_censorship_2(self): censored_text = "That **** gave m3 a very good ****, dude. You gotta check." self.assertEqual(profanity.censor(bad_text), censored_text) + def test_censorship_3(self): + bad_text = "Those 2 girls 1 cup. You gotta check. " + censored_text = "Those ****. You gotta check. " + self.assertEqual(profanity.censor(bad_text), censored_text) + + def test_censorship_4(self): + bad_text = "2 girls 1 cup" + censored_text = "****" + self.assertEqual(profanity.censor(bad_text), censored_text) + + def test_censorship_5(self): + bad_text = 'fuck 2 girls 1 cup' + censored_text = '**** ****' + self.assertEqual(profanity.censor(bad_text), censored_text) + def test_censorship_with_starting_swear_word(self): bad_text = " wh0re gave m3 a very good H@nD j0b." censored_text = " **** gave m3 a very good ****." From 0f6bdbd2a96afbae66564360c90f26e98cd2370a Mon Sep 17 00:00:00 2001 From: Son Nguyen Thanh Date: Mon, 13 May 2019 20:32:34 +0800 Subject: [PATCH 14/30] Version 0.3.3 - Fix incompatibility with Python 3.5 --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a8ced38..02ed816 100644 --- a/README.md +++ b/README.md @@ -132,6 +132,7 @@ $ python tests.py ``` ## Versions +- [v0.3.3](https://github.com/snguyenthanh/better_profanity/releases/tag/0.3.3) - Fix incompatibility with Python 3.5. - [v0.3.2](https://github.com/snguyenthanh/better_profanity/releases/tag/0.3.2) - Fix a typo in documentation. - [v0.3.1](https://github.com/snguyenthanh/better_profanity/releases/tag/0.3.1) - Remove unused dependencies. - [v0.3.0](https://github.com/snguyenthanh/better_profanity/releases/tag/0.3.0) - Add support for Unicode characters (Categories: Ll, Lu, Mc and Mn) [#2](https://github.com/snguyenthanh/better_profanity/pull/2). From cb0eaac92875ae64e66822288151742764dfd327 Mon Sep 17 00:00:00 2001 From: Son Date: Wed, 15 May 2019 22:52:15 +0700 Subject: [PATCH 15/30] Version 0.3.4 - Add significantly more swear words to wordlist --- README.md | 32 +++- better_profanity/__init__.py | 2 +- better_profanity/profanity.py | 8 +- better_profanity/profanity_wordlist.txt | 193 +++++++++++++++++++++++- better_profanity/utils.py | 11 +- tests.py | 2 +- 6 files changed, 223 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 02ed816..7415036 100644 --- a/README.md +++ b/README.md @@ -7,12 +7,12 @@ [![license](https://img.shields.io/github/license/mashape/apistatus.svg?style=popout)](https://github.com/snguyenthanh/better_profanity/blob/master/LICENSE) -Inspired from package [profanity](https://github.com/ben174/profanity) of [Ben Friedland](https://github.com/ben174), this library is much faster than the original one, by using string comparison instead of regex. +Inspired from package [profanity](https://github.com/ben174/profanity) of [Ben Friedland](https://github.com/ben174), this library is significantly faster than the original one, by using string comparison instead of regex. -It supports [modified spellings](https://en.wikipedia.org/wiki/Leet) (such as `p0rn`, `h4ndjob` and `handj0b`). +It supports [modified spellings](https://en.wikipedia.org/wiki/Leet) (such as `p0rn`, `h4ndjob`, `handj0b` and `b*tch`). ## Requirements -To make use of Python static typing, this package only works with `Python 3.5+`. +To make use of static typing and many relevant optimisations, this package only works with `Python 3.5+`. ## Installation ``` @@ -31,12 +31,18 @@ However, this library has not supported all languages yet, such as *Chinese*. By default, on the first `.censor()` call, function `.load_censor_words()` generates all possible [leetspeak](https://en.wikipedia.org/wiki/Leet) words, from [profanity_wordlist.txt](./better_profanity/profanity_wordlist.txt), to be used to compare against the input texts. The full mapping of the library can be found in [profanity.py](./better_profanity/profanity.py#L9-L18). For example, the word `handjob` would be loaded into: + ``` -'h@ndjob', 'handj0b', 'handj@b', 'h*ndj*b', 'h*ndjob', 'h@ndj0b', 'h@ndj*b', 'h4ndj*b', -'h@ndj@b', 'handjob', 'h4ndj0b', 'h4ndjob', 'h4ndj@b', 'h*ndj0b', 'handj*b', 'h*ndj@b' +'handjob', 'handj*b', 'handj0b', 'handj@b', 'h@ndjob', 'h@ndj*b', 'h@ndj0b', 'h@ndj@b', +'h*ndjob', 'h*ndj*b', 'h*ndj0b', 'h*ndj@b', 'h4ndjob', 'h4ndj*b', 'h4ndj0b', 'h4ndj@b' ``` -This set of words will be stored in memory (~5MB+). +## Wordlist +Most of the words in the default [wordlist](./better_profanity/profanity_wordlist.txt) are referred from [Full List of Bad Words and Top Swear Words Banned by Google](https://github.com/RobertJGabriel/Google-profanity-words). + +The wordlist contains a total of __106,992 words__, including 317 words from the default [profanity_wordlist.txt](./better_profanity/profanity_wordlist.txt) and their variants by modified spellings. + +Its total size in memory is 10.49+MB. ### 1. Censor swear words from a text By default, `profanity` replaces each swear words with 4 asterisks `****`. @@ -126,6 +132,20 @@ if __name__ == "__main__": # Эффекти́вного **** от я́да фу́гу не существу́ет до сих пор ``` +## Limitations + +1. As the library compares each word by characters, the censor could easily be bypassed by adding any character(s) to the word: + +``` +profanity.censor('I just have sexx') +# returns 'I just have sexx' + +profanity.censor('jerkk off') +# returns 'jerkk off' +``` + +2. Any word in [wordlist](https://github.com/snguyenthanh/better_profanity/blob/master/better_profanity/profanity_wordlist.txt) that have non-space separators cannot be recognised, such as `s & m`, and therefore, won't be filtered out. This problem was raised in [issue #5](https://github.com/snguyenthanh/better_profanity/issues/5). + ## Testing ``` $ python tests.py diff --git a/better_profanity/__init__.py b/better_profanity/__init__.py index 954e344..fbba154 100644 --- a/better_profanity/__init__.py +++ b/better_profanity/__init__.py @@ -1,2 +1,2 @@ name = 'better_profanity' -__version__ = '0.3.3' +__version__ = '0.3.4' diff --git a/better_profanity/profanity.py b/better_profanity/profanity.py index 1d95136..9fc8ccc 100644 --- a/better_profanity/profanity.py +++ b/better_profanity/profanity.py @@ -14,7 +14,7 @@ 'v': ('v', '*', 'u'), 'l': ('l', '1'), 'e': ('e', '*', '3'), - 's': ('s', '$'), + 's': ('s', '$', '5'), } # Pre-load the unicode characters @@ -79,7 +79,6 @@ def read_wordlist() -> Set[str]: yield row except FileNotFoundError: print('Unable to find profanity_wordlist.txt') - pass def get_replacement_for_swear_word(censor_char: str) -> str: @@ -92,8 +91,8 @@ def contains_profanity(text: str) -> bool: def update_next_words_indices( - text: str, words_indices: List[tuple], start_idx: int -) -> List[tuple]: + text: str, words_indices: List[tuple], start_idx: int + ) -> List[tuple]: """Return a list of next words_indices after the input index.""" if not words_indices: words_indices = get_next_words(text, start_idx, MAX_NUMBER_COMBINATIONS) @@ -109,7 +108,6 @@ def hide_swear_words(text: str, censor_char: str) -> str: censored_text = "" cur_word = "" skip_index = -1 - skip_cur_char = False next_words_indices = [] start_idx_of_next_word = get_start_index_of_next_word(text, 0) diff --git a/better_profanity/profanity_wordlist.txt b/better_profanity/profanity_wordlist.txt index 9e0ed26..0c2be30 100644 --- a/better_profanity/profanity_wordlist.txt +++ b/better_profanity/profanity_wordlist.txt @@ -1,49 +1,97 @@ 2g1c 2 girls 1 cup anal +anus +arse ass asshole arsehole +asswhole assmunch auto erotic autoerotic ballsack bastard +beastial +bellend bdsm bestiality bitch -bich +bitches +bitchin +bitching bimbo bimbos -blowjob blow job +blowjob +blowjobs blue waffle boob boobs +booobs +boooobs +booooobs +booooooobs +breasts booty call brown shower brown showers boner bondage +buceta bukake bukkake bullshit bull shit busty +butthole +carpet muncher +cawk +chink +cipa +clit +clits clitoris +cnut cock cocks +cockface +cockhead +cockmunch +cockmuncher +cocksuck +cocksucked +cocksucking +cocksucks +cocksucker +cokmuncher +coon cow girl cow girls cowgirl cowgirls +crap crotch cum +cummer cumming cuming +cums +cumshot +cunilingus +cunillingus +cunnilingus cunt +cuntlicker +cuntlicking +cunts +damn dick +dickhead dildo +dildos +dink +dinks deepthroat deep throat dog style @@ -51,37 +99,126 @@ doggie style doggiestyle doggy style doggystyle +donkeyribber +doosh +douche +duche dyke +ejaculate +ejaculated +ejaculates +ejaculating +ejaculatings +ejaculation +ejakulate erotic erotism fag faggot +fagging +faggit +faggitt +faggs +fagot +fagots +fags +fatass femdom fingering footjob foot job fuck +fucks +fucker +fuckers +fucked +fuckhead +fuckheads +fuckin +fucking +fcuk +fcuker +fcuking +felching +fellate +fellatio +fingerfuck +fingerfucked +fingerfucker +fingerfuckers +fingerfucking +fingerfucks +fistfuck +fistfucked +fistfucker +fistfuckers +fistfucking +fistfuckings +fistfucks +flange +fook +fooker +fucka +fuk +fuks +fuker +fukker +fukkin +fukking futanari futanary gangbang +gangbanged gang bang gokkun golden shower goldenshower gay +gaylord +gaysex +goatse handjob hand job hentai hooker +hoer +homo horny incest +jackoff +jack off jerkoff jerk off jizz +knob kinbaku +labia lesbian masturbate +masochist +mofo +mothafuck +motherfuck motherfucker +mothafucka +mothafuckas +mothafuckaz +mothafucked +mothafucker +mothafuckers +mothafuckin +mothafucking +mothafuckings +mothafucks +mother fucker +motherfucked +motherfucker +motherfuckers +motherfuckin +motherfucking +motherfuckings +motherfuckka +motherfucks milf muff nigga @@ -89,24 +226,37 @@ nigger nigg nipple nipples +nob +nob jokey +nobhead +nobjocky +nobjokey +numbnuts +nutsack nude nudes orgy +orgasm +orgasms panty panties penis -piss playboy porn porno pornography +pron pussy +pussies rape raping rapist -s&m -s & m +rectum +retard +rimming +sadist sadism +schlong scrotum sex semen @@ -115,26 +265,53 @@ she male shibari shibary shit +shitdick +shitfuck +shitfull +shithead +shiting +shitings +shits +shitted +shitters +shitting +shittings +shitty shota +skank slut +sluts +smut smegma spunk strip club stripclub -suck -sucks tit tits titties titty +titfuck +tittiefucker +titties +tittyfuck +tittywank +titwank threesome three some throating twat +twathead +twatty +twunt +viagra vagina +vulva wank +wanker +wanky whore +whoar xxx xx yaoi -yury \ No newline at end of file +yury diff --git a/better_profanity/utils.py b/better_profanity/utils.py index 42463b8..0d0bd6d 100644 --- a/better_profanity/utils.py +++ b/better_profanity/utils.py @@ -1,6 +1,5 @@ import json import os.path -from collections import defaultdict from string import ascii_letters, digits from typing import List, Set, Tuple @@ -26,6 +25,7 @@ def load_unicode_symbols(unicode_symbols_json: str = "alphabetic_unicode.json"): def get_start_index_of_next_word(text: str, start_idx: int) -> int: + """Return the index of the first character of the next word in the given text.""" start_idx_of_next_word = len(text) for index in iter(range(start_idx, len(text))): if text[index] not in ALLOWED_CHARACTERS: @@ -37,6 +37,7 @@ def get_start_index_of_next_word(text: str, start_idx: int) -> int: def get_next_word_and_end_index(text: str, start_idx: int): + """Return the next word in the given text, and the index of its last character.""" next_word = "" index = start_idx for index in iter(range(start_idx, len(text))): @@ -49,9 +50,11 @@ def get_next_word_and_end_index(text: str, start_idx: int): def any_next_words_form_swear_word( - cur_word: str, text: str, words_indices: List[tuple], censor_words: Set[str] -) -> Tuple[bool, int]: - """Return True, and the end index of the word in the text, if any word formed in words_indices is in `CENSOR_WORDSET`.""" + cur_word: str, text: str, words_indices: List[tuple], censor_words: Set[str] + ) -> Tuple[bool, int]: + """ + Return True, and the end index of the word in the text, if any word formed in words_indices is in `CENSOR_WORDSET`. + """ full_word = cur_word.lower() full_word_with_separators = cur_word.lower() diff --git a/tests.py b/tests.py index 607957f..3cb62dc 100644 --- a/tests.py +++ b/tests.py @@ -54,7 +54,7 @@ def test_censorship_5(self): bad_text = 'fuck 2 girls 1 cup' censored_text = '**** ****' self.assertEqual(profanity.censor(bad_text), censored_text) - + def test_censorship_with_starting_swear_word(self): bad_text = " wh0re gave m3 a very good H@nD j0b." censored_text = " **** gave m3 a very good ****." From 3330910e0fbde3e87ce6c13d25b0471cc0e2cd8c Mon Sep 17 00:00:00 2001 From: Son Nguyen Thanh Date: Wed, 15 May 2019 23:06:58 +0700 Subject: [PATCH 16/30] Version 0.3.4 - Add significantly more swear words to wordlist --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 7415036..44e1a02 100644 --- a/README.md +++ b/README.md @@ -152,6 +152,8 @@ $ python tests.py ``` ## Versions + +- [v0.3.4](https://github.com/snguyenthanh/better_profanity/releases/tag/0.3.4) - Add significantly more swear words. - [v0.3.3](https://github.com/snguyenthanh/better_profanity/releases/tag/0.3.3) - Fix incompatibility with Python 3.5. - [v0.3.2](https://github.com/snguyenthanh/better_profanity/releases/tag/0.3.2) - Fix a typo in documentation. - [v0.3.1](https://github.com/snguyenthanh/better_profanity/releases/tag/0.3.1) - Remove unused dependencies. From b4d05963b3ff971a93c452868cd3d250dde83969 Mon Sep 17 00:00:00 2001 From: Son Nguyen Thanh Date: Thu, 16 May 2019 14:25:49 +0700 Subject: [PATCH 17/30] Update README.md --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 44e1a02..c57a458 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,13 @@ Only Unicode characters from categories `Ll`, `Lu`, `Mc` and `Mn` are added. Mor However, this library has not supported all languages yet, such as *Chinese*. +## Wordlist +Most of the words in the default [wordlist](./better_profanity/profanity_wordlist.txt) are referred from [Full List of Bad Words and Top Swear Words Banned by Google](https://github.com/RobertJGabriel/Google-profanity-words). + +The wordlist contains a total of __106,992 words__, including 317 words from the default [profanity_wordlist.txt](./better_profanity/profanity_wordlist.txt) and their variants by modified spellings. + +Its total size in memory is 10.49+MB. + ## Usage By default, on the first `.censor()` call, function `.load_censor_words()` generates all possible [leetspeak](https://en.wikipedia.org/wiki/Leet) words, from [profanity_wordlist.txt](./better_profanity/profanity_wordlist.txt), to be used to compare against the input texts. The full mapping of the library can be found in [profanity.py](./better_profanity/profanity.py#L9-L18). @@ -37,13 +44,6 @@ For example, the word `handjob` would be loaded into: 'h*ndjob', 'h*ndj*b', 'h*ndj0b', 'h*ndj@b', 'h4ndjob', 'h4ndj*b', 'h4ndj0b', 'h4ndj@b' ``` -## Wordlist -Most of the words in the default [wordlist](./better_profanity/profanity_wordlist.txt) are referred from [Full List of Bad Words and Top Swear Words Banned by Google](https://github.com/RobertJGabriel/Google-profanity-words). - -The wordlist contains a total of __106,992 words__, including 317 words from the default [profanity_wordlist.txt](./better_profanity/profanity_wordlist.txt) and their variants by modified spellings. - -Its total size in memory is 10.49+MB. - ### 1. Censor swear words from a text By default, `profanity` replaces each swear words with 4 asterisks `****`. From 17bc8673cdee604886382e3c0a5a2d891617e3fc Mon Sep 17 00:00:00 2001 From: Son Nguyen Thanh Date: Thu, 16 May 2019 14:28:08 +0700 Subject: [PATCH 18/30] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c57a458..146d4f9 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Only Unicode characters from categories `Ll`, `Lu`, `Mc` and `Mn` are added. Mor [unicode category link]: https://en.wikipedia.org/wiki/Template:General_Category_(Unicode) -However, this library has not supported all languages yet, such as *Chinese*. +Not all languages are supported yet, such as *Chinese*. ## Wordlist Most of the words in the default [wordlist](./better_profanity/profanity_wordlist.txt) are referred from [Full List of Bad Words and Top Swear Words Banned by Google](https://github.com/RobertJGabriel/Google-profanity-words). From 635d3e0063edcdb0918f15172536a18ce8ff1929 Mon Sep 17 00:00:00 2001 From: Son Nguyen Thanh Date: Mon, 20 May 2019 01:39:28 +0800 Subject: [PATCH 19/30] Rephrase adding load_censor_word at init --- README.md | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 146d4f9..5f8f147 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # better_profanity -*A Python library to clean swear words (and their leetspeak) in strings* +*Blazingly fast cleaning swear words (and their leetspeak) in strings* [![release](https://img.shields.io/badge/dynamic/json.svg?label=release&url=https%3A%2F%2Fpypi.org%2Fpypi%2Fbetter-profanity%2Fjson&query=%24.info.version&colorB=blue)](https://github.com/snguyenthanh/better_profanity/releases/latest) [![Build Status](https://travis-ci.com/snguyenthanh/better_profanity.svg?branch=master)](https://travis-ci.com/snguyenthanh/better_profanity) @@ -35,15 +35,29 @@ The wordlist contains a total of __106,992 words__, including 317 words from the Its total size in memory is 10.49+MB. ## Usage -By default, on the first `.censor()` call, function `.load_censor_words()` generates all possible [leetspeak](https://en.wikipedia.org/wiki/Leet) words, from [profanity_wordlist.txt](./better_profanity/profanity_wordlist.txt), to be used to compare against the input texts. The full mapping of the library can be found in [profanity.py](./better_profanity/profanity.py#L9-L18). +It is highly recommended to call `profanity.load_censor_words()` at initialization, to reduce the runtime for the first `profanity.censor()` call. -For example, the word `handjob` would be loaded into: +``` +from better_profanity import profanity + +if __name__ == "__main__": + profanity.load_censor_words() + + text = "You p1ec3 of sHit." + censored_text = profanity.censor(text) + print(censored_text) + # You **** of ****. +``` + +All modified spellings of words in [profanity_wordlist.txt](./better_profanity/profanity_wordlist.txt) will be generated. For example, the word `handjob` would be loaded into: ``` 'handjob', 'handj*b', 'handj0b', 'handj@b', 'h@ndjob', 'h@ndj*b', 'h@ndj0b', 'h@ndj@b', 'h*ndjob', 'h*ndj*b', 'h*ndj0b', 'h*ndj@b', 'h4ndjob', 'h4ndj*b', 'h4ndj0b', 'h4ndj@b' ``` +The full mapping of the library can be found in [profanity.py](./better_profanity/profanity.py#L9-L18). + ### 1. Censor swear words from a text By default, `profanity` replaces each swear words with 4 asterisks `****`. @@ -144,7 +158,7 @@ profanity.censor('jerkk off') # returns 'jerkk off' ``` -2. Any word in [wordlist](https://github.com/snguyenthanh/better_profanity/blob/master/better_profanity/profanity_wordlist.txt) that have non-space separators cannot be recognised, such as `s & m`, and therefore, won't be filtered out. This problem was raised in [issue #5](https://github.com/snguyenthanh/better_profanity/issues/5). +2. Any word in [wordlist](https://github.com/snguyenthanh/better_profanity/blob/master/better_profanity/profanity_wordlist.txt) that have non-space separators cannot be recognised, such as `s & m`, and therefore, it won't be filtered out. This problem was raised in [issue #5](https://github.com/snguyenthanh/better_profanity/issues/5). ## Testing ``` From 777d3a229f21af0947e5117ad1ab3fedb3db3348 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 26 May 2019 11:16:08 +0800 Subject: [PATCH 20/30] Add compatibility to all versions of Python 3 --- README.md | 11 ++++--- better_profanity/__init__.py | 4 +-- better_profanity/profanity.py | 62 ++++++++++++++++++++--------------- better_profanity/utils.py | 27 +++++++-------- setup.py | 18 +++++----- tests.py | 11 ++++--- 6 files changed, 73 insertions(+), 60 deletions(-) diff --git a/README.md b/README.md index 5f8f147..b214a0e 100644 --- a/README.md +++ b/README.md @@ -3,16 +3,16 @@ [![release](https://img.shields.io/badge/dynamic/json.svg?label=release&url=https%3A%2F%2Fpypi.org%2Fpypi%2Fbetter-profanity%2Fjson&query=%24.info.version&colorB=blue)](https://github.com/snguyenthanh/better_profanity/releases/latest) [![Build Status](https://travis-ci.com/snguyenthanh/better_profanity.svg?branch=master)](https://travis-ci.com/snguyenthanh/better_profanity) -![python](https://img.shields.io/badge/python-3.5%2B-blue.svg) +![python](https://img.shields.io/badge/python-3-blue.svg) [![license](https://img.shields.io/github/license/mashape/apistatus.svg?style=popout)](https://github.com/snguyenthanh/better_profanity/blob/master/LICENSE) Inspired from package [profanity](https://github.com/ben174/profanity) of [Ben Friedland](https://github.com/ben174), this library is significantly faster than the original one, by using string comparison instead of regex. -It supports [modified spellings](https://en.wikipedia.org/wiki/Leet) (such as `p0rn`, `h4ndjob`, `handj0b` and `b*tch`). +It supports [modified spellings](https://en.wikipedia.org/wiki/Leet) (such as `p0rn`, `h4NDjob`, `handj0b` and `b*tCh`). ## Requirements -To make use of static typing and many relevant optimisations, this package only works with `Python 3.5+`. +This package only works with `Python 3`. ## Installation ``` @@ -42,7 +42,7 @@ from better_profanity import profanity if __name__ == "__main__": profanity.load_censor_words() - + text = "You p1ec3 of sHit." censored_text = profanity.censor(text) print(censored_text) @@ -158,7 +158,7 @@ profanity.censor('jerkk off') # returns 'jerkk off' ``` -2. Any word in [wordlist](https://github.com/snguyenthanh/better_profanity/blob/master/better_profanity/profanity_wordlist.txt) that have non-space separators cannot be recognised, such as `s & m`, and therefore, it won't be filtered out. This problem was raised in [issue #5](https://github.com/snguyenthanh/better_profanity/issues/5). +2. Any word in [wordlist](https://github.com/snguyenthanh/better_profanity/blob/master/better_profanity/profanity_wordlist.txt) that have non-space separators cannot be recognised, such as `s & m`, and therefore, it won't be filtered out. This problem was raised in [#5](https://github.com/snguyenthanh/better_profanity/issues/5). ## Testing ``` @@ -167,6 +167,7 @@ $ python tests.py ## Versions +- [v0.4.0](https://github.com/snguyenthanh/better_profanity/releases/tag/0.4.0) - Add compatibility to all versions of Python 3. - [v0.3.4](https://github.com/snguyenthanh/better_profanity/releases/tag/0.3.4) - Add significantly more swear words. - [v0.3.3](https://github.com/snguyenthanh/better_profanity/releases/tag/0.3.3) - Fix incompatibility with Python 3.5. - [v0.3.2](https://github.com/snguyenthanh/better_profanity/releases/tag/0.3.2) - Fix a typo in documentation. diff --git a/better_profanity/__init__.py b/better_profanity/__init__.py index fbba154..f13c7ea 100644 --- a/better_profanity/__init__.py +++ b/better_profanity/__init__.py @@ -1,2 +1,2 @@ -name = 'better_profanity' -__version__ = '0.3.4' +name = "better_profanity" +__version__ = "0.4.0" diff --git a/better_profanity/profanity.py b/better_profanity/profanity.py index 9fc8ccc..8f982d2 100644 --- a/better_profanity/profanity.py +++ b/better_profanity/profanity.py @@ -1,22 +1,31 @@ +# -*- coding: utf-8 -*- + +from io import open from itertools import product -from typing import Set, List -from .utils import (ALLOWED_CHARACTERS, get_complete_path_of_file, - get_next_words, get_start_index_of_next_word, - load_unicode_symbols, any_next_words_form_swear_word) + +from .utils import (ALLOWED_CHARACTERS, any_next_words_form_swear_word, + get_complete_path_of_file, get_next_words, + get_start_index_of_next_word, load_unicode_symbols) ## GLOBAL VARIABLES ## CENSOR_WORDSET = set() CHARS_MAPPING = { - 'a': ('a', '@', '*', '4'), - 'i': ('i', '*', 'l', '1'), - 'o': ('o', '*', '0', '@'), - 'u': ('u', '*', 'v'), - 'v': ('v', '*', 'u'), - 'l': ('l', '1'), - 'e': ('e', '*', '3'), - 's': ('s', '$', '5'), + "a": ("a", "@", "*", "4"), + "i": ("i", "*", "l", "1"), + "o": ("o", "*", "0", "@"), + "u": ("u", "*", "v"), + "v": ("v", "*", "u"), + "l": ("l", "1"), + "e": ("e", "*", "3"), + "s": ("s", "$", "5"), } +# Compatibility with Python 2 +try: + FileNotFoundError +except NameError: + FileNotFoundError = IOError + # Pre-load the unicode characters load_unicode_symbols() @@ -26,14 +35,15 @@ MAX_NUMBER_COMBINATIONS = 1 -def count_non_allowed_characters(word: str) -> int: +def count_non_allowed_characters(word): count = 0 for char in iter(word): if char not in ALLOWED_CHARACTERS: count += 1 return count -def load_censor_words(custom_words: List = None): + +def load_censor_words(custom_words=None): """Generate a set of words that need to be censored.""" global CENSOR_WORDSET global MAX_NUMBER_COMBINATIONS @@ -58,41 +68,39 @@ def load_censor_words(custom_words: List = None): CENSOR_WORDSET = all_censor_words -def generate_patterns_from_word(word: str) -> Set[str]: +def generate_patterns_from_word(word): """Return all patterns can be generated from the word.""" combos = [ (char,) if char not in CHARS_MAPPING else CHARS_MAPPING[char] for char in iter(word) ] - return (''.join(pattern) for pattern in product(*combos)) + return ("".join(pattern) for pattern in product(*combos)) -def read_wordlist() -> Set[str]: +def read_wordlist(): """Return words from file `profanity_wordlist.txt`.""" - wordlist_filename = 'profanity_wordlist.txt' + wordlist_filename = "profanity_wordlist.txt" wordlist_path = get_complete_path_of_file(wordlist_filename) try: - with open(wordlist_path, encoding='utf-8') as wordlist_file: + with open(wordlist_path, encoding="utf-8") as wordlist_file: for row in iter(wordlist_file): row = row.strip() if row != "": yield row except FileNotFoundError: - print('Unable to find profanity_wordlist.txt') + print("Unable to find profanity_wordlist.txt") -def get_replacement_for_swear_word(censor_char: str) -> str: +def get_replacement_for_swear_word(censor_char): return censor_char * 4 -def contains_profanity(text: str) -> bool: +def contains_profanity(text): """Return True if the input text has any swear words.""" return text != censor(text) -def update_next_words_indices( - text: str, words_indices: List[tuple], start_idx: int - ) -> List[tuple]: +def update_next_words_indices(text, words_indices, start_idx): """Return a list of next words_indices after the input index.""" if not words_indices: words_indices = get_next_words(text, start_idx, MAX_NUMBER_COMBINATIONS) @@ -103,7 +111,7 @@ def update_next_words_indices( return words_indices -def hide_swear_words(text: str, censor_char: str) -> str: +def hide_swear_words(text, censor_char): """Replace the swear words with censor characters.""" censored_text = "" cur_word = "" @@ -161,7 +169,7 @@ def hide_swear_words(text: str, censor_char: str) -> str: return censored_text -def censor(text: str, censor_char: str = '*') -> str: +def censor(text, censor_char="*"): """Replace the swear words in the text with `censor_char`.""" if not isinstance(text, str): diff --git a/better_profanity/utils.py b/better_profanity/utils.py index 0d0bd6d..a326a4f 100644 --- a/better_profanity/utils.py +++ b/better_profanity/utils.py @@ -1,22 +1,22 @@ +# -*- coding: utf-8 -*- + import json import os.path from string import ascii_letters, digits -from typing import List, Set, Tuple - ## GLOBAL VARIABLES ## ALLOWED_CHARACTERS = set(ascii_letters) ALLOWED_CHARACTERS.update(set(digits)) -ALLOWED_CHARACTERS.update({'@', '$', '*', '\"', '\''}) +ALLOWED_CHARACTERS.update({"@", "$", "*", '"', "'"}) -def get_complete_path_of_file(filename: str) -> str: +def get_complete_path_of_file(filename): """Join the path of the current directory with the input filename.""" root = os.path.abspath(os.path.dirname(__file__)) return os.path.join(root, filename) -def load_unicode_symbols(unicode_symbols_json: str = "alphabetic_unicode.json"): +def load_unicode_symbols(unicode_symbols_json="alphabetic_unicode.json"): """Load the unicode characters from categories Ll, Lu, Mc, Mn into `ALLOWED_CHARACTERS`.""" # More about Unicode categories can be found at # https://en.wikipedia.org/wiki/Template:General_Category_(Unicode) @@ -24,7 +24,7 @@ def load_unicode_symbols(unicode_symbols_json: str = "alphabetic_unicode.json"): ALLOWED_CHARACTERS.update(json.load(json_file)) -def get_start_index_of_next_word(text: str, start_idx: int) -> int: +def get_start_index_of_next_word(text, start_idx): """Return the index of the first character of the next word in the given text.""" start_idx_of_next_word = len(text) for index in iter(range(start_idx, len(text))): @@ -36,7 +36,7 @@ def get_start_index_of_next_word(text: str, start_idx: int) -> int: return start_idx_of_next_word -def get_next_word_and_end_index(text: str, start_idx: int): +def get_next_word_and_end_index(text, start_idx): """Return the next word in the given text, and the index of its last character.""" next_word = "" index = start_idx @@ -49,9 +49,7 @@ def get_next_word_and_end_index(text: str, start_idx: int): return next_word, index -def any_next_words_form_swear_word( - cur_word: str, text: str, words_indices: List[tuple], censor_words: Set[str] - ) -> Tuple[bool, int]: +def any_next_words_form_swear_word(cur_word, text, words_indices, censor_words): """ Return True, and the end index of the word in the text, if any word formed in words_indices is in `CENSOR_WORDSET`. """ @@ -66,13 +64,16 @@ def any_next_words_form_swear_word( continue full_word = "%s%s" % (full_word, single_word.lower()) - full_word_with_separators = "%s%s" % (full_word_with_separators, word_with_separators.lower()) + full_word_with_separators = "%s%s" % ( + full_word_with_separators, + word_with_separators.lower(), + ) if full_word in censor_words or full_word_with_separators in censor_words: return True, end_index return False, -1 -def get_next_words(text: str, start_idx: int, num_of_next_words: int = 1) -> List[Tuple[str, int]]: +def get_next_words(text, start_idx, num_of_next_words=1): """ Return a list of pairs of next words and next words included with separators, combined with their end indices. For example: Word `hand_job` has next words pairs: `job`, `_job`. @@ -90,7 +91,7 @@ def get_next_words(text: str, start_idx: int, num_of_next_words: int = 1) -> Lis words = [ (next_word, end_index), - ("%s%s" % (text[start_idx:start_idx_of_next_word], next_word), end_index) + ("%s%s" % (text[start_idx:start_idx_of_next_word], next_word), end_index), ] if num_of_next_words > 1: words.extend(get_next_words(text, end_index, num_of_next_words - 1)) diff --git a/setup.py b/setup.py index 54410c3..b10e558 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from better_profanity import __version__ -with open("README.md", encoding='utf-8') as fh: +with open("README.md", encoding="utf-8") as fh: long_description = fh.read() setuptools.setup( @@ -10,23 +10,23 @@ version=__version__, author="Son Nguyen Thanh", author_email="thanhson16198@gmail.com", - description="A Python library to clean swear words (and their leetspeak) in strings", + description="Blazingly fast cleaning swear words (and their leetspeak) in strings", long_description=long_description, long_description_content_type="text/markdown", url="https://github.com/snguyenthanh/better_profanity", classifiers=[ - "Programming Language :: Python :: 3.5", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", ], - python_requires=">=3.5", + python_requires="==3.*", packages=setuptools.find_packages(), data_files=[ ("wordlist", ["better_profanity/profanity_wordlist.txt"]), - ("unicode_characters", ["better_profanity/alphabetic_unicode.json"]) + ("unicode_characters", ["better_profanity/alphabetic_unicode.json"]), ], - package_data={"better_profanity": ["profanity_wordlist.txt", "alphabetic_unicode.json"]}, - include_package_data=True + package_data={ + "better_profanity": ["profanity_wordlist.txt", "alphabetic_unicode.json"] + }, + include_package_data=True, ) diff --git a/tests.py b/tests.py index 3cb62dc..13899ed 100644 --- a/tests.py +++ b/tests.py @@ -1,3 +1,5 @@ +# -*- coding: utf-8 -*- + import unittest from better_profanity import profanity @@ -10,7 +12,7 @@ def setUp(self): profanity.load_censor_words() def test_contains_profanity(self): - profane = profanity.contains_profanity('he is a m0th3rf*cker') + profane = profanity.contains_profanity("he is a m0th3rf*cker") self.assertTrue(profane) def test_leaves_paragraphs_untouched(self): @@ -51,8 +53,8 @@ def test_censorship_4(self): self.assertEqual(profanity.censor(bad_text), censored_text) def test_censorship_5(self): - bad_text = 'fuck 2 girls 1 cup' - censored_text = '**** ****' + bad_text = "fuck 2 girls 1 cup" + censored_text = "**** ****" self.assertEqual(profanity.censor(bad_text), censored_text) def test_censorship_with_starting_swear_word(self): @@ -81,7 +83,7 @@ def test_censorship_for_clean_text(self): self.assertEqual(profanity.censor(clean_text), clean_text) def test_custom_wordlist(self): - custom_badwords = ['happy', 'jolly', 'merry'] + custom_badwords = ["happy", "jolly", "merry"] profanity.load_censor_words(custom_badwords) # make sure it doesn't find real profanity anymore self.assertFalse(profanity.contains_profanity("Fuck you!")) @@ -128,6 +130,7 @@ def test_unicode_censorship_5(self): bad_text = "Маргаре́та (э́то бы́ло её настоя́щее и́мя) родила́сь в 1876 (ты́сяча восемьсо́т се́мьдесят шесто́м) году́ в Нидерла́ндах. В 18 (восемна́дцать) лет Маргаре́та вы́шла за́муж и перее́хала в Индоне́зию. Там она́ изуча́ла ме́стную культу́ру и та́нцы." censored_text = "Маргаре́та (э́то бы́ло её настоя́щее и́мя) родила́сь в 1876 (ты́сяча восемьсо́т се́мьдесят ****) году́ в ****. В 18 (восемна́дцать) лет Маргаре́та вы́шла за́муж и **** в Индоне́зию. Там она́ изуча́ла ме́стную культу́ру и ****." profanity.load_censor_words(["шесто́м", "Нидерла́ндах", "перее́хала", "та́нцы"]) + self.assertEqual(profanity.censor(bad_text), censored_text) From a459153a71934c79932bc1d365a929f951b2e66d Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Sun, 4 Aug 2019 16:49:15 -0700 Subject: [PATCH 21/30] Add the capability to add to the word list rather than replace it. --- better_profanity/profanity.py | 6 ++++++ tests.py | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/better_profanity/profanity.py b/better_profanity/profanity.py index 8f982d2..32127ff 100644 --- a/better_profanity/profanity.py +++ b/better_profanity/profanity.py @@ -67,6 +67,12 @@ def load_censor_words(custom_words=None): # The default wordlist takes ~5MB+ of memory CENSOR_WORDSET = all_censor_words +def add_censor_words(custom_words=None): + global CENSOR_WORDSET + + if custom_words: + CENSOR_WORDSET.update(custom_words) + def generate_patterns_from_word(word): """Return all patterns can be generated from the word.""" diff --git a/tests.py b/tests.py index 13899ed..2d72484 100644 --- a/tests.py +++ b/tests.py @@ -133,6 +133,12 @@ def test_unicode_censorship_5(self): self.assertEqual(profanity.censor(bad_text), censored_text) + def test_custom_words(self): + bad_text = 'supremacia ariana' + censored_text = '****' + profanity.add_censor_words([bad_text]) + self.assertEqual(profanity.censor(bad_text), censored_text) + class ProfanityUnicodeTestVietnamese(unittest.TestCase): def setUp(self): From 3586900e7db5ff78825b03a3e6920070a9d0f4cf Mon Sep 17 00:00:00 2001 From: Thomas David Baker Date: Sun, 4 Aug 2019 20:05:53 -0700 Subject: [PATCH 22/30] Refactoring for elegance/sense. --- better_profanity/profanity.py | 7 ++++--- tests.py | 19 ++++++++++++------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/better_profanity/profanity.py b/better_profanity/profanity.py index 32127ff..43a7f2b 100644 --- a/better_profanity/profanity.py +++ b/better_profanity/profanity.py @@ -67,11 +67,12 @@ def load_censor_words(custom_words=None): # The default wordlist takes ~5MB+ of memory CENSOR_WORDSET = all_censor_words -def add_censor_words(custom_words=None): +def add_censor_words(custom_words): global CENSOR_WORDSET - if custom_words: - CENSOR_WORDSET.update(custom_words) + if not isinstance(custom_words, (list, tuple, set)): + raise TypeError("Function 'add_censor_words' only accepts list, tuple or set.") + CENSOR_WORDSET.update(custom_words) def generate_patterns_from_word(word): diff --git a/tests.py b/tests.py index 2d72484..b4da1dc 100644 --- a/tests.py +++ b/tests.py @@ -95,6 +95,18 @@ def test_censorship_without_spaces(self): censored_text = "...****...hello_cat_****,,,,qew" self.assertEqual(profanity.censor(bad_text), censored_text) + def test_custom_words(self): + bad_text = 'supremacia ariana' + censored_text = '****' + profanity.add_censor_words([bad_text]) + self.assertEqual(profanity.censor(bad_text), censored_text) + + def test_custom_words_doesnt_remove_initial_words(self): + bad_text = 'fuck and heck' + censored_text = '**** and heck' + profanity.add_censor_words(['supremacia ariana']) + self.assertEqual(profanity.censor(bad_text), censored_text) + class ProfanityUnicodeTestRussian(unittest.TestCase): def setUp(self): @@ -133,13 +145,6 @@ def test_unicode_censorship_5(self): self.assertEqual(profanity.censor(bad_text), censored_text) - def test_custom_words(self): - bad_text = 'supremacia ariana' - censored_text = '****' - profanity.add_censor_words([bad_text]) - self.assertEqual(profanity.censor(bad_text), censored_text) - - class ProfanityUnicodeTestVietnamese(unittest.TestCase): def setUp(self): self.maxDiff = None From 3702f801568b796fb5fa4eb1a293fe68dfd9eba7 Mon Sep 17 00:00:00 2001 From: Son Date: Mon, 5 Aug 2019 22:24:35 +0800 Subject: [PATCH 23/30] Version 0.5.0 - Add the capability to add to the word list rather than replace it --- README.md | 16 +++++++++++++++- better_profanity/__init__.py | 2 +- better_profanity/profanity.py | 4 ++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b214a0e..2ac3824 100644 --- a/README.md +++ b/README.md @@ -131,7 +131,20 @@ if __name__ == "__main__": # Have a **** day! :) ``` -### 6. Censor Unicode characters +### 6. Use both default and your custom wordlist +``` +from better_profanity import profanity + +if __name__ == "__main__": + custom_badwords = ['happy', 'jolly', 'merry'] + profanity.add_censor_words(custom_badwords) + + print(profanity.contains_profanity("Happy you, fuck!")) + # **** you, ****! +``` + + +### 7. Censor Unicode characters No extra steps needed! ``` @@ -167,6 +180,7 @@ $ python tests.py ## Versions +- [v0.5.0](https://github.com/snguyenthanh/better_profanity/releases/tag/0.5.0) - Add the capability to add to the word list rather than replace it. #8 - [v0.4.0](https://github.com/snguyenthanh/better_profanity/releases/tag/0.4.0) - Add compatibility to all versions of Python 3. - [v0.3.4](https://github.com/snguyenthanh/better_profanity/releases/tag/0.3.4) - Add significantly more swear words. - [v0.3.3](https://github.com/snguyenthanh/better_profanity/releases/tag/0.3.3) - Fix incompatibility with Python 3.5. diff --git a/better_profanity/__init__.py b/better_profanity/__init__.py index f13c7ea..f09b705 100644 --- a/better_profanity/__init__.py +++ b/better_profanity/__init__.py @@ -1,2 +1,2 @@ name = "better_profanity" -__version__ = "0.4.0" +__version__ = "0.5.0" diff --git a/better_profanity/profanity.py b/better_profanity/profanity.py index 43a7f2b..7c7bd27 100644 --- a/better_profanity/profanity.py +++ b/better_profanity/profanity.py @@ -187,3 +187,7 @@ def censor(text, censor_char="*"): if not CENSOR_WORDSET: load_censor_words() return hide_swear_words(text, censor_char) + + +# Pre-load the default wordlist +load_censor_words() From 40d3e60a6234275b205e5d4e490135d14accd39a Mon Sep 17 00:00:00 2001 From: Son Date: Tue, 31 Mar 2020 17:29:40 +0800 Subject: [PATCH 24/30] Version 0.6.0 - Allow custom wordlist as a file and add whitelist_words kwarg --- .gitignore | 3 + README.md | 60 +++---- better_profanity/__init__.py | 10 +- better_profanity/better_profanity.py | 245 +++++++++++++++++++++++++++ better_profanity/constants.py | 16 ++ better_profanity/profanity.py | 193 --------------------- better_profanity/utils.py | 77 ++------- tests.py | 20 ++- 8 files changed, 328 insertions(+), 296 deletions(-) create mode 100644 better_profanity/better_profanity.py create mode 100644 better_profanity/constants.py delete mode 100644 better_profanity/profanity.py diff --git a/.gitignore b/.gitignore index eeb8a6e..c7f7d28 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ **/__pycache__ +better_profanity.egg-info/ +build/ +dist/ diff --git a/README.md b/README.md index 2ac3824..bbf52c3 100644 --- a/README.md +++ b/README.md @@ -32,10 +32,7 @@ Most of the words in the default [wordlist](./better_profanity/profanity_wordlis The wordlist contains a total of __106,992 words__, including 317 words from the default [profanity_wordlist.txt](./better_profanity/profanity_wordlist.txt) and their variants by modified spellings. -Its total size in memory is 10.49+MB. - ## Usage -It is highly recommended to call `profanity.load_censor_words()` at initialization, to reduce the runtime for the first `profanity.censor()` call. ``` from better_profanity import profanity @@ -56,7 +53,7 @@ All modified spellings of words in [profanity_wordlist.txt](./better_profanity/p 'h*ndjob', 'h*ndj*b', 'h*ndj0b', 'h*ndj@b', 'h4ndjob', 'h4ndj*b', 'h4ndj0b', 'h4ndj@b' ``` -The full mapping of the library can be found in [profanity.py](./better_profanity/profanity.py#L9-L18). +The full mapping of the library can be found in [profanity.py](./better_profanity/better_profanity.py#L18-L26). ### 1. Censor swear words from a text By default, `profanity` replaces each swear words with 4 asterisks `****`. @@ -114,7 +111,9 @@ if __name__ == "__main__": ``` ### 5. Censor swear words with a custom wordlist -Function `.load_censor_words()` takes a `List` of strings as censored words. + +#### 5.1. Wordlist as a `List` +Function `load_censor_words` takes a `List` of strings as censored words. The provided list will replace the default wordlist. ``` @@ -124,39 +123,48 @@ if __name__ == "__main__": custom_badwords = ['happy', 'jolly', 'merry'] profanity.load_censor_words(custom_badwords) - print(profanity.contains_profanity("Fuck you!")) - # Fuck you - print(profanity.contains_profanity("Have a merry day! :)")) # Have a **** day! :) ``` -### 6. Use both default and your custom wordlist +#### 5.2. Wordlist as a file +Function `load_censor_words_from_file takes a filename, which is a text file and each word is separated by lines. + ``` from better_profanity import profanity if __name__ == "__main__": - custom_badwords = ['happy', 'jolly', 'merry'] - profanity.add_censor_words(custom_badwords) + profanity.load_censor_words_from_file('/path/to/my/project/my_wordlist.txt') +``` + +### 6. Whitelist + +Function `load_censor_words` and `load_censor_words_from_file` takes a keyword argument `whitelist_words` to ignore words in a wordlist. + +It is best used when there are only a few words that you would like to ignore in the wordlist. - print(profanity.contains_profanity("Happy you, fuck!")) - # **** you, ****! ``` +# Use the default wordlist +profanity.load_censor_words(whitelist_words=['gay', 'lesbian']) +# or with your custom words as a List +custom_badwords = ['happy', 'jolly', 'merry'] +profanity.load_censor_words(custom_badwords, whitelist_words=['merry']) -### 7. Censor Unicode characters -No extra steps needed! +# or with your custom words as a text file +profanity.load_censor_words_from_file('/path/to/my/project/my_wordlist.txt', whitelist_words=['merry']) +``` +### 7. Add more censor words ``` from better_profanity import profanity if __name__ == "__main__": - bad_text = "Эффекти́вного противоя́дия от я́да фу́гу не существу́ет до сих пор" - profanity.load_censor_words(["противоя́дия"]) + custom_badwords = ['happy', 'jolly', 'merry'] + profanity.add_censor_words(custom_badwords) - censored_text = profanity.censor(text) - print(censored_text) - # Эффекти́вного **** от я́да фу́гу не существу́ет до сих пор + print(profanity.contains_profanity("Happy you, fuck!")) + # **** you, ****! ``` ## Limitations @@ -178,18 +186,6 @@ profanity.censor('jerkk off') $ python tests.py ``` -## Versions - -- [v0.5.0](https://github.com/snguyenthanh/better_profanity/releases/tag/0.5.0) - Add the capability to add to the word list rather than replace it. #8 -- [v0.4.0](https://github.com/snguyenthanh/better_profanity/releases/tag/0.4.0) - Add compatibility to all versions of Python 3. -- [v0.3.4](https://github.com/snguyenthanh/better_profanity/releases/tag/0.3.4) - Add significantly more swear words. -- [v0.3.3](https://github.com/snguyenthanh/better_profanity/releases/tag/0.3.3) - Fix incompatibility with Python 3.5. -- [v0.3.2](https://github.com/snguyenthanh/better_profanity/releases/tag/0.3.2) - Fix a typo in documentation. -- [v0.3.1](https://github.com/snguyenthanh/better_profanity/releases/tag/0.3.1) - Remove unused dependencies. -- [v0.3.0](https://github.com/snguyenthanh/better_profanity/releases/tag/0.3.0) - Add support for Unicode characters (Categories: Ll, Lu, Mc and Mn) [#2](https://github.com/snguyenthanh/better_profanity/pull/2). -- [v0.2.0](https://github.com/snguyenthanh/better_profanity/releases/tag/0.2) - Bug fix + faster censoring -- [v0.1.0](https://github.com/snguyenthanh/better_profanity/releases/tag/v0.1) - Initial release - ## Contributing Please read [CONTRIBUTING.md](./CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to us. diff --git a/better_profanity/__init__.py b/better_profanity/__init__.py index f09b705..2f0a913 100644 --- a/better_profanity/__init__.py +++ b/better_profanity/__init__.py @@ -1,2 +1,10 @@ +# -*- coding: utf-8 -*- + +from .better_profanity import Profanity + +__all__ = ["name", "__version__", "profanity"] + name = "better_profanity" -__version__ = "0.5.0" +__version__ = "0.6.0" + +profanity = Profanity() diff --git a/better_profanity/better_profanity.py b/better_profanity/better_profanity.py new file mode 100644 index 0000000..15964e2 --- /dev/null +++ b/better_profanity/better_profanity.py @@ -0,0 +1,245 @@ +# -*- coding: utf-8 -*- + +from itertools import product + +from .constants import ALLOWED_CHARACTERS + +from .utils import ( + read_wordlist, + get_replacement_for_swear_word, + any_next_words_form_swear_word, + get_complete_path_of_file, +) + + +class Profanity: + def __init__(self): + self.CENSOR_WORDSET = set() + self.CHARS_MAPPING = { + "a": ("a", "@", "*", "4"), + "i": ("i", "*", "l", "1"), + "o": ("o", "*", "0", "@"), + "u": ("u", "*", "v"), + "v": ("v", "*", "u"), + "l": ("l", "1"), + "e": ("e", "*", "3"), + "s": ("s", "$", "5"), + } + self.MAX_NUMBER_COMBINATIONS = 1 + self.ALLOWED_CHARACTERS = ALLOWED_CHARACTERS + self._default_wordlist_filename = get_complete_path_of_file( + "profanity_wordlist.txt" + ) + self.load_censor_words() + + ## PUBLIC ## + + def censor(self, text, censor_char="*"): + """Replace the swear words in the text with `censor_char`.""" + + if not isinstance(text, str): + text = str(text) + if not isinstance(censor_char, str): + censor_char = str(censor_char) + + if not self.CENSOR_WORDSET: + self.load_censor_words() + return self._hide_swear_words(text, censor_char) + + def load_censor_words_from_file(self, filename, **kwargs): + words = read_wordlist(filename) + self._populate_words_to_wordset(words, **kwargs) + + def load_censor_words(self, custom_words=None, **kwargs): + """Generate a set of words that need to be censored.""" + # Replace the words from `profanity_wordlist.txt` with a custom list + custom_words = custom_words or read_wordlist(self._default_wordlist_filename) + self._populate_words_to_wordset(custom_words, **kwargs) + + def add_censor_words(self, custom_words): + if not isinstance(custom_words, (list, tuple, set)): + raise TypeError( + "Function 'add_censor_words' only accepts list, tuple or set." + ) + + self.CENSOR_WORDSET.update(custom_words) + + def contains_profanity(self, text): + """Return True if the input text has any swear words.""" + return text != self.censor(text) + + ## PRIVATE ## + + def _populate_words_to_wordset(self, words, *, whitelist_words=None): + if whitelist_words is not None and not isinstance( + whitelist_words, (list, set, tuple) + ): + raise TypeError( + "The 'whitelist_words' keyword argument only accepts list, tuple or set." + ) + + # Validation + whitelist_words = whitelist_words or [] + for index, word in enumerate(whitelist_words): + if not isinstance(word, str): + raise ValueError( + f"Each word in 'whitelist_words' must be 'str' type, " + f"but '{type(word)}' found." + ) + whitelist_words[index] = word.lower() + + # Populate the words into an internal wordset + whitelist_words = set(whitelist_words) + all_censor_words = set() + for word in words: + # All words in CENSOR_WORDSET must be in lowercase + word = word.lower() + + if word in whitelist_words: + continue + + num_of_non_allowed_chars = self._count_non_allowed_characters(word) + if num_of_non_allowed_chars > self.MAX_NUMBER_COMBINATIONS: + self.MAX_NUMBER_COMBINATIONS = num_of_non_allowed_chars + + all_censor_words.update(set(self._generate_patterns_from_word(word))) + + # The default wordlist takes ~5MB+ of memory + self.CENSOR_WORDSET = all_censor_words + + def _count_non_allowed_characters(self, word): + count = 0 + for char in iter(word): + if char not in self.ALLOWED_CHARACTERS: + count += 1 + return count + + def _generate_patterns_from_word(self, word): + """Return all patterns can be generated from the word.""" + combos = [ + (char,) if char not in self.CHARS_MAPPING else self.CHARS_MAPPING[char] + for char in iter(word) + ] + return ("".join(pattern) for pattern in product(*combos)) + + def _update_next_words_indices(self, text, words_indices, start_idx): + """Return a list of next words_indices after the input index.""" + if not words_indices: + words_indices = self._get_next_words( + text, start_idx, self.MAX_NUMBER_COMBINATIONS + ) + else: + del words_indices[:2] + if words_indices and words_indices[-1][0] != "": + words_indices += self._get_next_words(text, words_indices[-1][1], 1) + return words_indices + + def _hide_swear_words(self, text, censor_char): + """Replace the swear words with censor characters.""" + censored_text = "" + cur_word = "" + skip_index = -1 + next_words_indices = [] + start_idx_of_next_word = self._get_start_index_of_next_word(text, 0) + + # If there are no words in the text, return the raw text without parsing + if start_idx_of_next_word >= len(text) - 1: + return text + + # Left strip the text, to avoid inaccurate parsing + if start_idx_of_next_word > 0: + censored_text = text[:start_idx_of_next_word] + text = text[start_idx_of_next_word:] + + # Splitting each word in the text to compare with censored words + for index, char in iter(enumerate(text)): + if index < skip_index: + continue + if char in ALLOWED_CHARACTERS: + cur_word += char + continue + + # Skip continuous non-allowed characters + if cur_word.strip() == "": + censored_text += char + cur_word = "" + continue + + # Iterate the next words combined with the current one + # to check if it forms a swear word + next_words_indices = self._update_next_words_indices( + text, next_words_indices, index + ) + contains_swear_word, end_index = any_next_words_form_swear_word( + cur_word, next_words_indices, self.CENSOR_WORDSET + ) + if contains_swear_word: + cur_word = get_replacement_for_swear_word(censor_char) + skip_index = end_index + char = "" + next_words_indices = [] + + # If the current a swear word + if cur_word.lower() in self.CENSOR_WORDSET: + cur_word = get_replacement_for_swear_word(censor_char) + + censored_text += cur_word + char + cur_word = "" + + # Final check + if cur_word != "" and skip_index < len(text) - 1: + if cur_word.lower() in self.CENSOR_WORDSET: + cur_word = get_replacement_for_swear_word(censor_char) + censored_text += cur_word + return censored_text + + def _get_start_index_of_next_word(self, text, start_idx): + """Return the index of the first character of the next word in the given text.""" + start_idx_of_next_word = len(text) + for index in iter(range(start_idx, len(text))): + if text[index] not in self.ALLOWED_CHARACTERS: + continue + start_idx_of_next_word = index + break + + return start_idx_of_next_word + + def _get_next_word_and_end_index(self, text, start_idx): + """Return the next word in the given text, and the index of its last character.""" + next_word = "" + index = start_idx + for index in iter(range(start_idx, len(text))): + char = text[index] + if char in self.ALLOWED_CHARACTERS: + next_word += char + continue + break + return next_word, index + + def _get_next_words(self, text, start_idx, num_of_next_words=1): + """ + Return a list of pairs of next words and next words included with separators, + combined with their end indices. + For example: Word `hand_job` has next words pairs: `job`, `_job`. + """ + + # Find the starting index of the next word + start_idx_of_next_word = self._get_start_index_of_next_word(text, start_idx) + + # Return an empty string if there are no other words + if start_idx_of_next_word >= len(text) - 1: + return [("", start_idx_of_next_word), ("", start_idx_of_next_word)] + + # Combine the words into a list + next_word, end_index = self._get_next_word_and_end_index( + text, start_idx_of_next_word + ) + + words = [ + (next_word, end_index), + ("%s%s" % (text[start_idx:start_idx_of_next_word], next_word), end_index), + ] + if num_of_next_words > 1: + words.extend(self._get_next_words(text, end_index, num_of_next_words - 1)) + + return words diff --git a/better_profanity/constants.py b/better_profanity/constants.py new file mode 100644 index 0000000..929a361 --- /dev/null +++ b/better_profanity/constants.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from io import open +from json import load +from string import ascii_letters, digits + +from .utils import get_complete_path_of_file + + +ALLOWED_CHARACTERS = set(ascii_letters) +ALLOWED_CHARACTERS.update(set(digits)) +ALLOWED_CHARACTERS.update({"@", "$", "*", '"', "'"}) + +# Pre-load the unicode characters +with open(get_complete_path_of_file("alphabetic_unicode.json"), "r") as json_file: + ALLOWED_CHARACTERS.update(load(json_file)) diff --git a/better_profanity/profanity.py b/better_profanity/profanity.py deleted file mode 100644 index 7c7bd27..0000000 --- a/better_profanity/profanity.py +++ /dev/null @@ -1,193 +0,0 @@ -# -*- coding: utf-8 -*- - -from io import open -from itertools import product - -from .utils import (ALLOWED_CHARACTERS, any_next_words_form_swear_word, - get_complete_path_of_file, get_next_words, - get_start_index_of_next_word, load_unicode_symbols) - -## GLOBAL VARIABLES ## -CENSOR_WORDSET = set() -CHARS_MAPPING = { - "a": ("a", "@", "*", "4"), - "i": ("i", "*", "l", "1"), - "o": ("o", "*", "0", "@"), - "u": ("u", "*", "v"), - "v": ("v", "*", "u"), - "l": ("l", "1"), - "e": ("e", "*", "3"), - "s": ("s", "$", "5"), -} - -# Compatibility with Python 2 -try: - FileNotFoundError -except NameError: - FileNotFoundError = IOError - -# Pre-load the unicode characters -load_unicode_symbols() - -# The max number of additional words forming a swear word. For example: -# - hand job = 1 -# - this is a fish = 3 -MAX_NUMBER_COMBINATIONS = 1 - - -def count_non_allowed_characters(word): - count = 0 - for char in iter(word): - if char not in ALLOWED_CHARACTERS: - count += 1 - return count - - -def load_censor_words(custom_words=None): - """Generate a set of words that need to be censored.""" - global CENSOR_WORDSET - global MAX_NUMBER_COMBINATIONS - - # Replace the words from `profanity_wordlist.txt` with a custom list - if custom_words: - temp_words = custom_words - else: - temp_words = read_wordlist() - - all_censor_words = set() - for word in temp_words: - # All words in CENSOR_WORDSET must be in lowercase - word = word.lower() - num_of_non_allowed_chars = count_non_allowed_characters(word) - if num_of_non_allowed_chars > MAX_NUMBER_COMBINATIONS: - MAX_NUMBER_COMBINATIONS = num_of_non_allowed_chars - - all_censor_words.update(set(generate_patterns_from_word(word))) - - # The default wordlist takes ~5MB+ of memory - CENSOR_WORDSET = all_censor_words - -def add_censor_words(custom_words): - global CENSOR_WORDSET - - if not isinstance(custom_words, (list, tuple, set)): - raise TypeError("Function 'add_censor_words' only accepts list, tuple or set.") - CENSOR_WORDSET.update(custom_words) - - -def generate_patterns_from_word(word): - """Return all patterns can be generated from the word.""" - combos = [ - (char,) if char not in CHARS_MAPPING else CHARS_MAPPING[char] - for char in iter(word) - ] - return ("".join(pattern) for pattern in product(*combos)) - - -def read_wordlist(): - """Return words from file `profanity_wordlist.txt`.""" - wordlist_filename = "profanity_wordlist.txt" - wordlist_path = get_complete_path_of_file(wordlist_filename) - try: - with open(wordlist_path, encoding="utf-8") as wordlist_file: - for row in iter(wordlist_file): - row = row.strip() - if row != "": - yield row - except FileNotFoundError: - print("Unable to find profanity_wordlist.txt") - - -def get_replacement_for_swear_word(censor_char): - return censor_char * 4 - - -def contains_profanity(text): - """Return True if the input text has any swear words.""" - return text != censor(text) - - -def update_next_words_indices(text, words_indices, start_idx): - """Return a list of next words_indices after the input index.""" - if not words_indices: - words_indices = get_next_words(text, start_idx, MAX_NUMBER_COMBINATIONS) - else: - del words_indices[:2] - if words_indices and words_indices[-1][0] != "": - words_indices += get_next_words(text, words_indices[-1][1], 1) - return words_indices - - -def hide_swear_words(text, censor_char): - """Replace the swear words with censor characters.""" - censored_text = "" - cur_word = "" - skip_index = -1 - next_words_indices = [] - start_idx_of_next_word = get_start_index_of_next_word(text, 0) - - # If there are no words in the text, return the raw text without parsing - if start_idx_of_next_word >= len(text) - 1: - return text - - # Left strip the text, to avoid inaccurate parsing - if start_idx_of_next_word > 0: - censored_text = text[:start_idx_of_next_word] - text = text[start_idx_of_next_word:] - - # Splitting each word in the text to compare with censored words - for index, char in iter(enumerate(text)): - if index < skip_index: - continue - if char in ALLOWED_CHARACTERS: - cur_word += char - continue - - # Skip continuous non-allowed characters - if cur_word.strip() == "": - censored_text += char - cur_word = "" - continue - - # Iterate the next words combined with the current one - # to check if it forms a swear word - next_words_indices = update_next_words_indices(text, next_words_indices, index) - contains_swear_word, end_index = any_next_words_form_swear_word( - cur_word, text, next_words_indices, CENSOR_WORDSET - ) - if contains_swear_word: - cur_word = get_replacement_for_swear_word(censor_char) - skip_index = end_index - char = "" - next_words_indices = [] - - # If the current a swear word - if cur_word.lower() in CENSOR_WORDSET: - cur_word = get_replacement_for_swear_word(censor_char) - - censored_text += cur_word + char - cur_word = "" - - # Final check - if cur_word != "" and skip_index < len(text) - 1: - if cur_word.lower() in CENSOR_WORDSET: - cur_word = get_replacement_for_swear_word(censor_char) - censored_text += cur_word - return censored_text - - -def censor(text, censor_char="*"): - """Replace the swear words in the text with `censor_char`.""" - - if not isinstance(text, str): - text = str(text) - if not isinstance(censor_char, str): - censor_char = str(censor_char) - - if not CENSOR_WORDSET: - load_censor_words() - return hide_swear_words(text, censor_char) - - -# Pre-load the default wordlist -load_censor_words() diff --git a/better_profanity/utils.py b/better_profanity/utils.py index a326a4f..b13c62b 100644 --- a/better_profanity/utils.py +++ b/better_profanity/utils.py @@ -1,13 +1,5 @@ # -*- coding: utf-8 -*- - -import json import os.path -from string import ascii_letters, digits - -## GLOBAL VARIABLES ## -ALLOWED_CHARACTERS = set(ascii_letters) -ALLOWED_CHARACTERS.update(set(digits)) -ALLOWED_CHARACTERS.update({"@", "$", "*", '"', "'"}) def get_complete_path_of_file(filename): @@ -16,42 +8,23 @@ def get_complete_path_of_file(filename): return os.path.join(root, filename) -def load_unicode_symbols(unicode_symbols_json="alphabetic_unicode.json"): - """Load the unicode characters from categories Ll, Lu, Mc, Mn into `ALLOWED_CHARACTERS`.""" - # More about Unicode categories can be found at - # https://en.wikipedia.org/wiki/Template:General_Category_(Unicode) - with open(get_complete_path_of_file(unicode_symbols_json), "r") as json_file: - ALLOWED_CHARACTERS.update(json.load(json_file)) - - -def get_start_index_of_next_word(text, start_idx): - """Return the index of the first character of the next word in the given text.""" - start_idx_of_next_word = len(text) - for index in iter(range(start_idx, len(text))): - if text[index] not in ALLOWED_CHARACTERS: - continue - start_idx_of_next_word = index - break - - return start_idx_of_next_word +def read_wordlist(filename: str): + """Return words from a wordlist file.""" + with open(filename, encoding="utf-8") as wordlist_file: + for row in iter(wordlist_file): + row = row.strip() + if row != "": + yield row -def get_next_word_and_end_index(text, start_idx): - """Return the next word in the given text, and the index of its last character.""" - next_word = "" - index = start_idx - for index in iter(range(start_idx, len(text))): - char = text[index] - if char in ALLOWED_CHARACTERS: - next_word += char - continue - break - return next_word, index +def get_replacement_for_swear_word(censor_char): + return censor_char * 4 -def any_next_words_form_swear_word(cur_word, text, words_indices, censor_words): +def any_next_words_form_swear_word(cur_word, words_indices, censor_words): """ - Return True, and the end index of the word in the text, if any word formed in words_indices is in `CENSOR_WORDSET`. + Return True, and the end index of the word in the text, + if any word formed in words_indices is in `CENSOR_WORDSET`. """ full_word = cur_word.lower() full_word_with_separators = cur_word.lower() @@ -71,29 +44,3 @@ def any_next_words_form_swear_word(cur_word, text, words_indices, censor_words): if full_word in censor_words or full_word_with_separators in censor_words: return True, end_index return False, -1 - - -def get_next_words(text, start_idx, num_of_next_words=1): - """ - Return a list of pairs of next words and next words included with separators, combined with their end indices. - For example: Word `hand_job` has next words pairs: `job`, `_job`. - """ - - # Find the starting index of the next word - start_idx_of_next_word = get_start_index_of_next_word(text, start_idx) - - # Return an empty string if there are no other words - if start_idx_of_next_word >= len(text) - 1: - return [("", start_idx_of_next_word), ("", start_idx_of_next_word)] - - # Combine the words into a list - next_word, end_index = get_next_word_and_end_index(text, start_idx_of_next_word) - - words = [ - (next_word, end_index), - ("%s%s" % (text[start_idx:start_idx_of_next_word], next_word), end_index), - ] - if num_of_next_words > 1: - words.extend(get_next_words(text, end_index, num_of_next_words - 1)) - - return words diff --git a/tests.py b/tests.py index b4da1dc..d16d616 100644 --- a/tests.py +++ b/tests.py @@ -96,15 +96,15 @@ def test_censorship_without_spaces(self): self.assertEqual(profanity.censor(bad_text), censored_text) def test_custom_words(self): - bad_text = 'supremacia ariana' - censored_text = '****' + bad_text = "supremacia ariana" + censored_text = "****" profanity.add_censor_words([bad_text]) self.assertEqual(profanity.censor(bad_text), censored_text) def test_custom_words_doesnt_remove_initial_words(self): - bad_text = 'fuck and heck' - censored_text = '**** and heck' - profanity.add_censor_words(['supremacia ariana']) + bad_text = "fuck and heck" + censored_text = "**** and heck" + profanity.add_censor_words(["supremacia ariana"]) self.assertEqual(profanity.censor(bad_text), censored_text) @@ -145,6 +145,7 @@ def test_unicode_censorship_5(self): self.assertEqual(profanity.censor(bad_text), censored_text) + class ProfanityUnicodeTestVietnamese(unittest.TestCase): def setUp(self): self.maxDiff = None @@ -164,5 +165,14 @@ def test_unicode_vietnamese_2(self): self.assertEqual(profanity.censor(bad_text), censored_text) +class ProfanityFileTest(unittest.TestCase): + def setUp(self): + self.maxDiff = None + + def test_read_wordlist_not_found(self): + with self.assertRaises(FileNotFoundError): + profanity.load_censor_words_from_file("not_found_file.txt") + + if __name__ == "__main__": unittest.main() From 62c0e30bf829ea1864c01f8b296cf3fa7e2c5677 Mon Sep 17 00:00:00 2001 From: Son Date: Tue, 31 Mar 2020 17:37:32 +0800 Subject: [PATCH 25/30] Add test case for whitelist --- tests.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests.py b/tests.py index d16d616..3a877d3 100644 --- a/tests.py +++ b/tests.py @@ -165,6 +165,22 @@ def test_unicode_vietnamese_2(self): self.assertEqual(profanity.censor(bad_text), censored_text) +class ProfanityWhitelistTest(unittest.TestCase): + def setUp(self): + self.maxDiff = None + # Pre-load CENSOR_WORDSET + profanity.load_censor_words() + + def test_whitelist_words(self): + bad_text = "I am gay" + censored_text = "I am ****" + self.assertEqual(profanity.censor(bad_text), censored_text) + + # Whitelist the word `gay` + profanity.load_censor_words(whitelist_words=["gay"]) + self.assertEqual(profanity.censor(bad_text), bad_text) + + class ProfanityFileTest(unittest.TestCase): def setUp(self): self.maxDiff = None From fbf770fe945c6ee9e5194ee497847931096e70d3 Mon Sep 17 00:00:00 2001 From: Giovanni Alzetta Date: Wed, 8 Apr 2020 15:45:07 +0200 Subject: [PATCH 26/30] Fixed compatibility with python 3.5 --- better_profanity/better_profanity.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/better_profanity/better_profanity.py b/better_profanity/better_profanity.py index 15964e2..4541be6 100644 --- a/better_profanity/better_profanity.py +++ b/better_profanity/better_profanity.py @@ -83,8 +83,8 @@ def _populate_words_to_wordset(self, words, *, whitelist_words=None): for index, word in enumerate(whitelist_words): if not isinstance(word, str): raise ValueError( - f"Each word in 'whitelist_words' must be 'str' type, " - f"but '{type(word)}' found." + "Each word in 'whitelist_words' must be 'str' type, " + "but '{word}' found.".format(word=type(word)) ) whitelist_words[index] = word.lower() From 008650f2310455bbf1aa19787bd80c109ced3ffd Mon Sep 17 00:00:00 2001 From: Son Date: Thu, 9 Apr 2020 00:03:44 +0800 Subject: [PATCH 27/30] Add tox to test multiple Python versions --- .travis.yml | 32 ++++++++++++++++++++++++++++--- tox.ini | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 tox.ini diff --git a/.travis.yml b/.travis.yml index 44739ca..545fce8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,32 @@ +dist: xenial + language: python -python: - - "3.6" + +services: + - redis-server + +matrix: + include: + - python: 3.8 + env: TOXENV=black + + - python: 3.3 + env: TOXENV=py33 + - python: 3.4 + env: TOXENV=py34 + - python: 3.5 + env: TOXENV=py35 + - python: 3.6 + env: TOXENV=py36 + - python: 3.7 + env: TOXENV=py37 + - python: 3.8 + env: TOXENV=py38 + - python: pypy3 + env: TOXENV=pypy3 + +install: + - pip install tox script: - - python tests.py + - tox diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..7a7cb92 --- /dev/null +++ b/tox.ini @@ -0,0 +1,55 @@ +[tox] +envlist = + black + py33, py34, py35, py36, py37, py38, pypy3 + +skipsdist = True +skip_missing_interpreters = True + +[default] +basepython = python3.8 + +setenv = + PY_MODULE=better_profanity + PYTHONPYCACHEPREFIX={envtmpdir}/pycache + +[testenv] +parallel_show_output = True +whitelist_externals = make + /bin/bash + +basepython = + py33: python3.3 + py34: python3.4 + py35: python3.5 + py36: python3.6 + py37: python3.7 + py38: python3.8 + + pypy3: pypy3 + + +# run the tests +# ... or run any other command line tool you need to run here +commands = python tests.py + + +[testenv:black] +description = run Black (linter) +basepython = {[default]basepython} +skip_install = True +deps = + black==19.10b0 +setenv = + BLACK_LINT_ARGS=--check +commands = + black {env:BLACK_LINT_ARGS:} better_profanity + + +[testenv:black-reformat] + +description = {[testenv:black]description} and reformat +basepython = {[testenv:black]basepython} +skip_install = {[testenv:black]skip_install} +deps = {[testenv:black]deps} +commands = {[testenv:black]commands} From 174057bce74bb140b6a64b0e0821cd53cb0c720b Mon Sep 17 00:00:00 2001 From: Son Date: Thu, 9 Apr 2020 00:06:17 +0800 Subject: [PATCH 28/30] Remove Python 3.3 in tox and travis --- .travis.yml | 2 -- tox.ini | 3 +-- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 545fce8..6e7dd4d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,8 +10,6 @@ matrix: - python: 3.8 env: TOXENV=black - - python: 3.3 - env: TOXENV=py33 - python: 3.4 env: TOXENV=py34 - python: 3.5 diff --git a/tox.ini b/tox.ini index 7a7cb92..b8b3a58 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,7 @@ [tox] envlist = black - py33, py34, py35, py36, py37, py38, pypy3 + py34, py35, py36, py37, py38, pypy3 skipsdist = True skip_missing_interpreters = True @@ -19,7 +19,6 @@ whitelist_externals = make /bin/bash basepython = - py33: python3.3 py34: python3.4 py35: python3.5 py36: python3.6 From f1dd47bb4ded08e1e567a376e67c7dcdd0950f90 Mon Sep 17 00:00:00 2001 From: Son Date: Thu, 9 Apr 2020 00:25:52 +0800 Subject: [PATCH 29/30] Version 0.6.1 - Update wordlist --- README.md | 4 ++-- better_profanity/__init__.py | 2 +- better_profanity/better_profanity.py | 1 + better_profanity/profanity_wordlist.txt | 9 ++++++--- tests.py | 10 ++++++---- 5 files changed, 16 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index bbf52c3..bef7ec0 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Not all languages are supported yet, such as *Chinese*. ## Wordlist Most of the words in the default [wordlist](./better_profanity/profanity_wordlist.txt) are referred from [Full List of Bad Words and Top Swear Words Banned by Google](https://github.com/RobertJGabriel/Google-profanity-words). -The wordlist contains a total of __106,992 words__, including 317 words from the default [profanity_wordlist.txt](./better_profanity/profanity_wordlist.txt) and their variants by modified spellings. +The wordlist contains a total of __181,590 words__, including 320 words from the default [profanity_wordlist.txt](./better_profanity/profanity_wordlist.txt) and their variants by modified spellings. ## Usage @@ -145,7 +145,7 @@ It is best used when there are only a few words that you would like to ignore in ``` # Use the default wordlist -profanity.load_censor_words(whitelist_words=['gay', 'lesbian']) +profanity.load_censor_words(whitelist_words=['happy', 'merry']) # or with your custom words as a List custom_badwords = ['happy', 'jolly', 'merry'] diff --git a/better_profanity/__init__.py b/better_profanity/__init__.py index 2f0a913..20827a0 100644 --- a/better_profanity/__init__.py +++ b/better_profanity/__init__.py @@ -5,6 +5,6 @@ __all__ = ["name", "__version__", "profanity"] name = "better_profanity" -__version__ = "0.6.0" +__version__ = "0.6.1" profanity = Profanity() diff --git a/better_profanity/better_profanity.py b/better_profanity/better_profanity.py index 4541be6..0e2d722 100644 --- a/better_profanity/better_profanity.py +++ b/better_profanity/better_profanity.py @@ -24,6 +24,7 @@ def __init__(self): "l": ("l", "1"), "e": ("e", "*", "3"), "s": ("s", "$", "5"), + "t": ("t", "7",), } self.MAX_NUMBER_COMBINATIONS = 1 self.ALLOWED_CHARACTERS = ALLOWED_CHARACTERS diff --git a/better_profanity/profanity_wordlist.txt b/better_profanity/profanity_wordlist.txt index 0c2be30..c0dc0ed 100644 --- a/better_profanity/profanity_wordlist.txt +++ b/better_profanity/profanity_wordlist.txt @@ -1,9 +1,13 @@ 2g1c 2 girls 1 cup +4r5e anal anus arse ass +asses +assfucker +assfukka asshole arsehole asswhole @@ -13,8 +17,10 @@ autoerotic ballsack bastard beastial +bestial bellend bdsm +beastiality bestiality bitch bitches @@ -173,8 +179,6 @@ gang bang gokkun golden shower goldenshower -gay -gaylord gaysex goatse handjob @@ -193,7 +197,6 @@ jizz knob kinbaku labia -lesbian masturbate masochist mofo diff --git a/tests.py b/tests.py index 3a877d3..56ce5fd 100644 --- a/tests.py +++ b/tests.py @@ -10,6 +10,8 @@ def setUp(self): self.maxDiff = None # Pre-load CENSOR_WORDSET profanity.load_censor_words() + print(len(profanity.CENSOR_WORDSET)) + assert 0 def test_contains_profanity(self): profane = profanity.contains_profanity("he is a m0th3rf*cker") @@ -172,12 +174,12 @@ def setUp(self): profanity.load_censor_words() def test_whitelist_words(self): - bad_text = "I am gay" - censored_text = "I am ****" + bad_text = "I have boobs" + censored_text = "I have ****" self.assertEqual(profanity.censor(bad_text), censored_text) - # Whitelist the word `gay` - profanity.load_censor_words(whitelist_words=["gay"]) + # Whitelist the word `boobs` + profanity.load_censor_words(whitelist_words=["boobs"]) self.assertEqual(profanity.censor(bad_text), bad_text) From f59238f440ba617fc80d45491571dd970ed25df4 Mon Sep 17 00:00:00 2001 From: Son Date: Thu, 9 Apr 2020 00:27:55 +0800 Subject: [PATCH 30/30] Fix assert print in tests --- tests.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests.py b/tests.py index 56ce5fd..0a635d4 100644 --- a/tests.py +++ b/tests.py @@ -10,8 +10,6 @@ def setUp(self): self.maxDiff = None # Pre-load CENSOR_WORDSET profanity.load_censor_words() - print(len(profanity.CENSOR_WORDSET)) - assert 0 def test_contains_profanity(self): profane = profanity.contains_profanity("he is a m0th3rf*cker")