Last active
April 19, 2025 09:21
-
-
Save phineas-pta/05cad38a29fea000ab6d9e13a6f7e623 to your computer and use it in GitHub Desktop.
Revisions
-
phineas-pta revised this gist
May 25, 2023 . 2 changed files with 112 additions and 11 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -18,6 +18,21 @@ def xoa_dau(txt: str) -> str: return txt.translate(BANG_XOA_DAU) ``` **hãy xem phần giải thích bên dưới để hiểu về các dạng unicode chuẩn C và D** phiên bản không phụ thuộc vào các dạng unicode cho ai muốn mì ăn liền ```python BANG_XOA_DAU_FULL = str.maketrans( "ÁÀẢÃẠĂẮẰẲẴẶÂẤẦẨẪẬĐÈÉẺẼẸÊẾỀỂỄỆÍÌỈĨỊÓÒỎÕỌÔỐỒỔỖỘƠỚỜỞỠỢÚÙỦŨỤƯỨỪỬỮỰÝỲỶỸỴáàảãạăắằẳẵặâấầẩẫậđèéẻẽẹêếềểễệíìỉĩịóòỏõọôốồổỗộơớờởỡợúùủũụưứừửữựýỳỷỹỵ", "A"*17 + "D" + "E"*11 + "I"*5 + "O"*17 + "U"*11 + "Y"*5 + "a"*17 + "d" + "e"*11 + "i"*5 + "o"*17 + "u"*11 + "y"*5, chr(774) + chr(770) + chr(795) + chr(769) + chr(768) + chr(777) + chr(771) + chr(803) # 8 kí tự dấu dưới dạng unicode chuẩn D ) def xoa_dau_full(txt: str) -> str: return txt.translate(BANG_XOA_DAU_FULL) ``` ## 1. giải thích quá trình cần package `unicodedata` (có sẵn trong python): @@ -78,22 +93,26 @@ txt.translate(BANG_XOA_DAU) # "“ Dao duc kinh”" ``` lệnh thay thế kí tự trên toàn chuỗi được thực thi đúng 1 lần duy nhất ngoài ra parameter thứ 3 của `str.maketrans` là 1 string của các kí tự muốn xoá, ta có thể đưa vào 8 kí tự dấu vào đó (xem code đầu bài) ## 2. so sánh với các cách xoá dấu thường sử dụng khác ### 2.1. cài thêm package phụ trợ điểm mạnh là tốc độ, do thực thi lệnh thay thế kí tự trên toàn chuỗi đúng 1 lần duy nhất (giống như trên), nhưng còn nhiều thiếu sót - *package [`unidecode`](https://github.com/avian2/unidecode)*: vốn được dùng để chuyển kí tự unicode về bảng ASCII, vậy nên sẽ **làm mất các kí tự khác**, ví dụ ở trên 2 kí tự `“ ”` sẽ thành `" "`; phương pháp duy nhất ko bị ảnh hưởng bởi dạng unicode chuẩn C hay D ```python from unidecode import unidecode txt = "“Đạo đức kinh”" unidecode(txt) # '" Dao duc kinh"' ``` - *thuật toán FlashText với package [`flashtext`](https://github.com/vi3k6i5/flashtext)*: hứa hẹn tốc độ nhanh hơn cả `.replace()` và `RegEx`, **nhưng không thể thực thi thay thế kí tự** (có thể thay thế từ - word) - *thuật toán Aho-Corasick với package [`fsed`](https://github.com/wroberts/fsed) hoặc [`cyac`](https://github.com/nppoly/cyac)*: trong số các package sử dụng thuật toán Aho-Corasick, chỉ có 2 package này có chức năng thay thế kí tự, còn lại chỉ có chức năng tìm, tốc độ cũng rất tốt ### 2.2. xử lí kí tự bằng công cụ có sẵn trong python -
phineas-pta revised this gist
May 24, 2023 . 2 changed files with 230 additions and 4 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,6 +1,6 @@ # xoá dấu tiếng Việt với Python phương pháp nhanh hơn và không cần cài thêm package code hoàn chỉnh: @@ -97,10 +97,12 @@ unidecode(txt) # '" Dao duc kinh"' ### 2.2. xử lí kí tự bằng công cụ có sẵn trong python xem kết quả benchmark ở notebook phía dưới - khởi tạo chuỗi mới ```python BANG_XOA_DAU_1 = {"Á": "A", "À": "A", …} txt = "“Đạo đức kinh”" txt = "".join([BANG_XOA_DAU_1.get(s, s) for s in txt]) ``` @@ -110,15 +112,15 @@ về cơ bản thì tương đương với phương pháp mình trình bày ở - *dùng `.replace()`*: ```python BANG_XOA_DAU_2 = {"Á": "A", "À": "A", …} # total 134 items txt = "“Đạo đức kinh”" for k, v in BANG_XOA_DAU_2.items(): txt = txt.replace(k, v) ``` lệnh thay thế kí tự trên toàn chuỗi (`.replace()`) được thực thi 134 lần - *dùng `RegEx`*: viết code ngắn hơn, ~~tốc độ nhanh hơn `.replace()`~~ ```python import re This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,224 @@ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Benchmark xóa dấu" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import re, random\n", "from timeit import timeit\n", "from matplotlib import pyplot as plt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "mặc định văn bản dưới dạng unicode chuẩn C (unicode normal form C)" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "CHU_CO_DAU = \"ÁÀẢÃẠĂẮẰẲẴẶÂẤẦẨẪẬĐÈÉẺẼẸÊẾỀỂỄỆÍÌỈĨỊÓÒỎÕỌÔỐỒỔỖỘƠỚỜỞỠỢÚÙỦŨỤƯỨỪỬỮỰÝỲỶỸỴáàảãạăắằẳẵặâấầẩẫậđèéẻẽẹêếềểễệíìỉĩịóòỏõọôốồổỗộơớờởỡợúùủũụưứừửữựýỳỷỹỵ\"\n", "CHU_KO_DAU = \"A\"*17 + \"D\" + \"E\"*11 + \"I\"*5 + \"O\"*17 + \"U\"*11 + \"Y\"*5 + \"a\"*17 + \"d\" + \"e\"*11 + \"i\"*5 + \"o\"*17 + \"u\"*11 + \"y\"*5\n", "assert(len(CHU_CO_DAU) == len(CHU_KO_DAU))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "№ 0: `str.maketrans() + .translate()`" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "BANG_XOA_DAU_0 = str.maketrans(CHU_CO_DAU, CHU_KO_DAU)\n", "def xoa_dau_0(txt):\n", " return txt.translate(BANG_XOA_DAU_0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "№ 1: `new string`" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "BANG_XOA_DAU_1 = dict(zip(CHU_CO_DAU, CHU_KO_DAU))\n", "def xoa_dau_1(txt):\n", " return \"\".join(BANG_XOA_DAU_1.get(s, s) for s in txt)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "№ 2: `.replace()`" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "BANG_XOA_DAU_2 = dict(zip(CHU_CO_DAU, CHU_KO_DAU))\n", "def xoa_dau_2(txt):\n", " for k, v in BANG_XOA_DAU_2.items():\n", " txt = txt.replace(k, v)\n", " return txt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "№ 3: `RegEx`" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "BANG_XOA_DAU_3 = {\n", " \"A\": re.compile(\"[ÁÀẢÃẠĂẮẰẲẴẶÂẤẦẨẪẬ]\"),\n", " \"D\": re.compile(\"Đ\"),\n", " \"E\": re.compile(\"[ÈÉẺẼẸÊẾỀỂỄỆ]\"),\n", " \"I\": re.compile(\"[ÍÌỈĨỊ]\"),\n", " \"O\": re.compile(\"[ÓÒỎÕỌÔỐỒỔỖỘƠỚỜỞỠỢ]\"),\n", " \"U\": re.compile(\"[ÚÙỦŨỤƯỨỪỬỮỰ]\"),\n", " \"Y\": re.compile(\"[ÝỲỶỸỴ]\"),\n", " \"a\": re.compile(\"[áàảãạăắằẳẵặâấầẩẫậ]\"),\n", " \"d\": re.compile(\"đ\"),\n", " \"e\": re.compile(\"[èéẻẽẹêếềểễệ]\"),\n", " \"i\": re.compile(\"[íìỉĩị]\"),\n", " \"o\": re.compile(\"[óòỏõọôốồổỗộơớờởỡợ]\"),\n", " \"u\": re.compile(\"[úùủũụưứừửữự]\"),\n", " \"y\": re.compile(\"[ýỳỷỹỵ]\")\n", "}\n", "def xoa_dau_3(txt):\n", " for k, v in BANG_XOA_DAU_3.items():\n", " txt = v.sub(k, txt)\n", " return txt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "array chứa thời gian thực thi từng phương pháp để so sánh" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "vec_N, vec_0, vec_1, vec_2, vec_3 = [], [], [], [], []" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "benchmark với văn bản có độ dài 10<sup>0</sup>, 10<sup>1</sup>, 10<sup>2</sup>, …, 10<sup>8</sup> kí tự, với tất cả kí tự đều có dấu (worst-case scenario)" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "N_repet = 10 # sồ lần thực thi (lấy thời gian trung bình)\n", "for i in range(9):\n", " N = 10**i\n", " txt = \"\".join(random.choices(CHU_CO_DAU, k=N))\n", "\n", " t_0 = timeit(\"xoa_dau_0(txt)\", number=N_repet, globals=globals())\n", " t_1 = timeit(\"xoa_dau_1(txt)\", number=N_repet, globals=globals())\n", " t_2 = timeit(\"xoa_dau_2(txt)\", number=N_repet, globals=globals())\n", " t_3 = timeit(\"xoa_dau_3(txt)\", number=N_repet, globals=globals())\n", "\n", " vec_N.append(N)\n", " vec_0.append(t_0)\n", " vec_1.append(t_1)\n", " vec_2.append(t_2)\n", " vec_3.append(t_3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "đồ thị so sánh" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "<Figure size 640x480 with 1 Axes>" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "plt.plot(vec_N, vec_0, color=\"red\", label=\"str.maketrans()\")\n", "plt.plot(vec_N, vec_1, color=\"green\", label=\"new string\")\n", "plt.plot(vec_N, vec_2, color=\"blue\", label=\".replace()\")\n", "plt.plot(vec_N, vec_3, color=\"black\", label=\"RegEx\")\n", "plt.xscale(\"log\")\n", "plt.xlabel(\"string length\")\n", "plt.ylabel(\"time (s)\")\n", "plt.legend()\n", "plt.show()" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "name": "python" } }, "nbformat": 4, "nbformat_minor": 5 } -
phineas-pta revised this gist
May 23, 2023 . 1 changed file with 32 additions and 22 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -7,15 +7,15 @@ code hoàn chỉnh: ```python import unicodedata BANG_XOA_DAU = str.maketrans( "ÁÀẢÃẠĂẮẰẲẴẶÂẤẦẨẪẬĐÈÉẺẼẸÊẾỀỂỄỆÍÌỈĨỊÓÒỎÕỌÔỐỒỔỖỘƠỚỜỞỠỢÚÙỦŨỤƯỨỪỬỮỰÝỲỶỸỴáàảãạăắằẳẵặâấầẩẫậđèéẻẽẹêếềểễệíìỉĩịóòỏõọôốồổỗộơớờởỡợúùủũụưứừửữựýỳỷỹỵ", "A"*17 + "D" + "E"*11 + "I"*5 + "O"*17 + "U"*11 + "Y"*5 + "a"*17 + "d" + "e"*11 + "i"*5 + "o"*17 + "u"*11 + "y"*5 ) def xoa_dau(txt: str) -> str: if not unicodedata.is_normalized("NFC", txt): txt = unicodedata.normalize("NFC", txt) return txt.translate(BANG_XOA_DAU) ``` ## 1. giải thích quá trình @@ -65,7 +65,7 @@ unicodedata.is_normalized("NFC", txt) # True nhờ vào dạng unicode chuẩn C, thay thế kí tự là “1 kí tự thay thế 1 kí tự” (thay vì “1 kí tự thay thế nhiều kí tự” nếu không đúng chuẩn), cách làm hiệu quả nhất là tạo bảng tương ứng kí tự có dấu - ko dấu với hàm `str.maketrans()` có sẵn trong python: ```python BANG_XOA_DAU = str.maketrans( "ÁÀẢÃẠĂẮẰẲẴẶÂẤẦẨẪẬĐÈÉẺẼẸÊẾỀỂỄỆÍÌỈĨỊÓÒỎÕỌÔỐỒỔỖỘƠỚỜỞỠỢÚÙỦŨỤƯỨỪỬỮỰÝỲỶỸỴáàảãạăắằẳẵặâấầẩẫậđèéẻẽẹêếềểễệíìỉĩịóòỏõọôốồổỗộơớờởỡợúùủũụưứừửữựýỳỷỹỵ", "A"*17 + "D" + "E"*11 + "I"*5 + "O"*17 + "U"*11 + "Y"*5 + "a"*17 + "d" + "e"*11 + "i"*5 + "o"*17 + "u"*11 + "y"*5 ) @@ -74,10 +74,10 @@ DIACRITICS_TABLE = str.maketrans( xoá dấu với hàm `.translate()` có sẵn trong python: ```python txt.translate(BANG_XOA_DAU) # "“ Dao duc kinh”" ``` lệnh thay thế kí tự trên toàn chuỗi được thực thi đúng 1 lần duy nhất ## 2. so sánh với các cách xoá dấu thường sử dụng khác @@ -89,40 +89,50 @@ lệnh thay thế kí tự trên toàn chuỗi đúng 1 lần duy nhất ```python from unidecode import unidecode txt = "“Đạo đức kinh”" unidecode(txt) # '" Dao duc kinh"' ``` - *dùng `flashtext.KeywordProcessor()`*: hứa hẹn tốc độ nhanh hơn cả `.replace()` và `RegEx` do có thuật toán đặc biệt, **nhưng chưa tương thích với kí tự ngoài bảng ASCII** ### 2.2. xử lí kí tự bằng công cụ có sẵn trong python - khởi tạo chuỗi mới ```python BANG_XOA_DAU_1 = {"Á": "A" , "À": "A" , …} txt = "“Đạo đức kinh”" txt = "".join([BANG_XOA_DAU_1.get(s, s) for s in txt]) ``` về cơ bản thì tương đương với phương pháp mình trình bày ở trên, nhưng chậm hơn do sử dụng vòng lặp `for` - *dùng `.replace()`*: ```python BANG_XOA_DAU_2 = {"Á": "A" , "À": "A" , …} # total 134 items txt = "“Đạo đức kinh”" for k, v in BANG_XOA_DAU_2.items(): txt = txt.replace(k, v) ``` lệnh thay thế kí tự trên toàn chuỗi (`.replace()`) được thực thi 134 lần - *dùng `RegEx`*: viết code ngắn hơn, tốc độ nhanh hơn `.replace()` ```python import re BANG_XOA_DAU_3 = { # compile and save regex object for reuse is more efficient "A": re.compile("[ÁÀẢÃẠĂẮẰẲẴẶÂẤẦẨẪẬ]"), "a": re.compile("[áàảãạăắằẳẵặâấầẩẫậ]"), … # total 14 items } txt = "“Đạo đức kinh”" for k, v in BANG_XOA_DAU_3.items(): txt = v.sub(k, txt) ``` lệnh thay thế kí tự trên toàn chuỗi (`re.sub()`) được thực thi 14 lần ## 3. phụ lục: bảng unicode codepoint kí tự tiếng Việt @@ -133,19 +143,19 @@ không tồn tại dạng này đối với kí tự Đđ dấu đổi nguyên âm <table><tbody><tr> <td>̆ (dấu ă)<br /><code>U+0306</code></td> <td>̂ (dấu mũ âêô)<br /><code>U+0302</code></td> <td>̛ (dấu móc ơư)<br /><code>U+031B</code></td> </tr></tbody></table> dấu thanh điệu <table><tbody><tr> <td>́ (dấu sắc)<br /><code>U+0301</code></td> <td>̀ (dấu huyền)<br /><code>U+0300</code></td> <td>̉ (dấu hỏi)<br /><code>U+0309</code></td> <td>̃ (dấu ngã)<br /><code>U+0303</code></td> <td>̣ (dấu nặng)<br /><code>U+0323</code></td> </tr></tbody></table> ### 3.2. dạng unicode chuẩn C -
phineas-pta created this gist
May 23, 2023 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,356 @@ # xoá dấu tiếng Việt với Python phương pháp nhanh hơn, hiệu quả hơn và không cần cài thêm package code hoàn chỉnh: ```python import unicodedata DIACRITICS_TABLE = str.maketrans( "ÁÀẢÃẠĂẮẰẲẴẶÂẤẦẨẪẬĐÈÉẺẼẸÊẾỀỂỄỆÍÌỈĨỊÓÒỎÕỌÔỐỒỔỖỘƠỚỜỞỠỢÚÙỦŨỤƯỨỪỬỮỰÝỲỶỸỴáàảãạăắằẳẵặâấầẩẫậđèéẻẽẹêếềểễệíìỉĩịóòỏõọôốồổỗộơớờởỡợúùủũụưứừửữựýỳỷỹỵ", "A"*17 + "D" + "E"*11 + "I"*5 + "O"*17 + "U"*11 + "Y"*5 + "a"*17 + "d" + "e"*11 + "i"*5 + "o"*17 + "u"*11 + "y"*5 ) def xoa_dau(txt: str) -> str: if not unicodedata.is_normalized("NFC", txt): txt = unicodedata.normalize("NFC", txt) return txt.translate(DIACRITICS_TABLE) ``` ## 1. giải thích quá trình cần package `unicodedata` (có sẵn trong python): ```python import unicodedata ``` cho 1 string trong python như sau: ```python raw_txt = "“Đạo đức kinh”" ``` ### bước 1: đưa về dạng unicode chuẩn C (unicode normal form C) nếu bạn cho rằng `raw_txt` rất bình thường thì bạn đã lầm, bởi vì dấu là 1 kí tự riêng ví dụ thay vì là 1 kí tự `ứ` (`U+1EE9`) văn bản lại là chuỗi: - 3 kí tự `u` (`U+0075`) + `◌̛` (dấu móc ư `U+031B`) + `◌́` (dấu sắc `U+0301`), do khi crawl text online dạng HTML là `ứ`, lưu ý thứ tự của `U+031B` và `U+0301` không quan trọng 👉 đây gọi là dạng unicode chuẩn D (unicode normal form D) - 2 kí tự `ư` (`U+01B0`) + `◌́` (dấu sắc `U+0301`) 👉 không thuộc về dạng unicode chuẩn nào - 2 kí tự `ú` (`U+00FA`) + `◌̛` (dấu móc ư `U+031B`) 👉 không thuộc về dạng unicode chuẩn nào bước này ít người biết đến, nếu bỏ qua bước này, kế tiếp khi ta xoá dấu (thay thế `ứ` bằng `u`) sẽ gặp rắc rối do không biết `ứ` là 1 kí tự hay chuỗi 2/3 kí tự mình phát hiện ra vấn đề này khi crawl text truyện chữ về đọc dạng unicode chuẩn C là khi các dấu nhập chung thành 1 kí tự duy nhất `ứ` (`U+1EE9`) để kiểm tra xem text đã đúng dạng unicode chuẩn C (viết tắt NFC): ```python unicodedata.is_normalized("NFC", raw_txt) # False ``` chuyển sang dạng unicode chuẩn C ```python txt = unicodedata.normalize("NFC", raw_txt) # "“Đạo đức kinh”" unicodedata.is_normalized("NFC", txt) # True ``` ### bước 2: xoá dấu cực nhanh nhờ vào dạng unicode chuẩn C, thay thế kí tự là “1 kí tự thay thế 1 kí tự” (thay vì “1 kí tự thay thế nhiều kí tự” nếu không đúng chuẩn), cách làm hiệu quả nhất là tạo bảng tương ứng kí tự có dấu - ko dấu với hàm `str.maketrans()` có sẵn trong python: ```python DIACRITICS_TABLE = str.maketrans( "ÁÀẢÃẠĂẮẰẲẴẶÂẤẦẨẪẬĐÈÉẺẼẸÊẾỀỂỄỆÍÌỈĨỊÓÒỎÕỌÔỐỒỔỖỘƠỚỜỞỠỢÚÙỦŨỤƯỨỪỬỮỰÝỲỶỸỴáàảãạăắằẳẵặâấầẩẫậđèéẻẽẹêếềểễệíìỉĩịóòỏõọôốồổỗộơớờởỡợúùủũụưứừửữựýỳỷỹỵ", "A"*17 + "D" + "E"*11 + "I"*5 + "O"*17 + "U"*11 + "Y"*5 + "a"*17 + "d" + "e"*11 + "i"*5 + "o"*17 + "u"*11 + "y"*5 ) ``` xoá dấu với hàm `.translate()` có sẵn trong python: ```python txt.translate(DIACRITICS_TABLE) # "“ Dao duc kinh”" ``` lệnh thay thế kí tự trên toàn chuỗi đúng 1 lần duy nhất ## 2. so sánh với các cách xoá dấu thường sử dụng khác ### 2.1. cài thêm package phụ trợ điểm mạnh là tốc độ, do thực thi lệnh thay thế kí tự trên toàn chuỗi đúng 1 lần duy nhất (giống như trên), nhưng còn nhiều thiếu sót - *dùng `unidecode.unidecode()`*: vốn được dùng để chuyển kí tự unicode về bảng ASCII, vậy nên sẽ **làm mất các kí tự khác**, ví dụ ở trên 2 kí tự `“ ”` sẽ thành `" "`; phương pháp duy nhất ko bị ảnh hưởng bởi dạng unicode chuẩn C hay D ```python from unidecode import unidecode unidecode(txt) # '" Dao duc kinh"' unidecode(raw_txt) # '" Dao duc kinh"' ``` - *dùng `flashtext.KeywordProcessor()`*: hứa hẹn tốc độ nhanh hơn cả `.replace()` và `RegEx` do có thuật toán đặc biệt, **nhưng chưa tương thích với kí tự ngoài bảng ASCII** ### 2.2. xử lí kí tự bằng công cụ có sẵn trong python lệnh thay thế kí tự trên toàn chuỗi được thực thi nhiều lần nên sẽ chậm hơn - *dùng `.replace()`*: ```python BANG_XOA_DAU = {"Á": "A" , "À": "A" , …} # total 134 items for k, v in BANG_XOA_DAU.items(): txt = txt.replace(k, v) ``` lệnh `.replace()` được thực thi 134 lần - *dùng `RegEx`*: viết code ngắn hơn, tốc độ nhanh hơn `.replace()` ```python import re BANG_XOA_DAU = { # compile and save regex object for reuse is more efficient "A": re.compile("[ÁÀẢÃẠĂẮẰẲẴẶÂẤẦẨẪẬ]"), "a": re.compile("[áàảãạăắằẳẵặâấầẩẫậ]"), … # total 14 items } for k, v in BANG_XOA_DAU.items(): txt = v.sub(k, txt) ``` lệnh `re.sub()` được thực thi 14 lần ## 3. phụ lục: bảng unicode codepoint kí tự tiếng Việt ### 3.1. dạng unicode chuẩn D không tồn tại dạng này đối với kí tự Đđ dấu đổi nguyên âm <table><tbody><tr> <td>̆ (dấu ă)<br /><code>U+0306</code></td> <td>̂ (dấu mũ âêô)<br /><code>U+0302</code></td> <td>̛ (dấu móc ơư)<br /><code>U+031B</code></td> </tr></tbody></table> dấu thanh điệu <table><tbody><tr> <td>́ (dấu sắc)<br /><code>U+0301</code></td> <td>̀ (dấu huyền)<br /><code>U+0300</code></td> <td>̉ (dấu hỏi)<br /><code>U+0309</code></td> <td>̃ (dấu ngã)<br /><code>U+0303</code></td> <td>̣ (dấu nặng)<br /><code>U+0323</code></td> </tr></tbody></table> ### 3.2. dạng unicode chuẩn C tham khảo thêm: https://vietunicode.sourceforge.net/charset/vietcharset.html <table><tbody> <tr> <td></td> <td>Á<br /><code>U+00C1</code></td> <td>À<br /><code>U+00C0</code></td> <td>Ả<br /><code>U+1EA2</code></td> <td>Ã<br /><code>U+00C3</code></td> <td>Ạ<br /><code>U+1EA0</code></td> </tr> <tr> <td>Ă<br /><code>U+0102</code></td> <td>Ắ<br /><code>U+1EAE</code></td> <td>Ằ<br /><code>U+1EB0</code></td> <td>Ẳ<br /><code>U+1EB2</code></td> <td>Ẵ<br /><code>U+1EB4</code></td> <td>Ặ<br /><code>U+1EB6</code></td> </tr> <tr> <td>Â<br /><code>U+00C2</code></td> <td>Ấ<br /><code>U+1EA4</code></td> <td>Ầ<br /><code>U+1EA6</code></td> <td>Ẩ<br /><code>U+1EA8</code></td> <td>Ẫ<br /><code>U+1EAA</code></td> <td>Ậ<br /><code>U+1EAC</code></td> </tr> <tr> <td>Đ<br /><code>U+0110</code></td> <td colspan="5"></td> </tr> <tr> <td></td> <td>È<br /><code>U+00C8</code></td> <td>É<br /><code>U+00C9</code></td> <td>Ẻ<br /><code>U+1EBA</code></td> <td>Ẽ<br /><code>U+1EBC</code></td> <td>Ẹ<br /><code>U+1EB8</code></td> </tr> <tr> <td>Ê<br /><code>U+00CA</code></td> <td>Ế<br /><code>U+1EBE</code></td> <td>Ề<br /><code>U+1EC0</code></td> <td>Ể<br /><code>U+1EC2</code></td> <td>Ễ<br /><code>U+1EC4</code></td> <td>Ệ<br /><code>U+1EC6</code></td> </tr> <tr> <td></td> <td>Í<br /><code>U+00CD</code></td> <td>Ì<br /><code>U+00CC</code></td> <td>Ỉ<br /><code>U+1EC8</code></td> <td>Ĩ<br /><code>U+0128</code></td> <td>Ị<br /><code>U+1ECA</code></td> </tr> <tr> <td></td> <td>Ó<br /><code>U+00D3</code></td> <td>Ò<br /><code>U+00D2</code></td> <td>Ỏ<br /><code>U+1ECE</code></td> <td>Õ<br /><code>U+00D5</code></td> <td>Ọ<br /><code>U+1ECC</code></td> </tr> <tr> <td>Ô<br /><code>U+00D4</code></td> <td>Ố<br /><code>U+1ED0</code></td> <td>Ồ<br /><code>U+1ED2</code></td> <td>Ổ<br /><code>U+1ED4</code></td> <td>Ỗ<br /><code>U+1ED6</code></td> <td>Ộ<br /><code>U+1ED8</code></td> </tr> <tr> <td>Ơ<br /><code>U+01A0</code></td> <td>Ớ<br /><code>U+1EDA</code></td> <td>Ờ<br /><code>U+1EDC</code></td> <td>Ở<br /><code>U+1EDE</code></td> <td>Ỡ<br /><code>U+1EE0</code></td> <td>Ợ<br /><code>U+1EE2</code></td> </tr> <tr> <td></td> <td>Ú<br /><code>U+00DA</code></td> <td>Ù<br /><code>U+00D9</code></td> <td>Ủ<br /><code>U+1EE6</code></td> <td>Ũ<br /><code>U+0168</code></td> <td>Ụ<br /><code>U+1EE4</code></td> </tr> <tr> <td>Ư<br /><code>U+01AF</code></td> <td>Ứ<br /><code>U+1EE8</code></td> <td>Ừ<br /><code>U+1EEA</code></td> <td>Ử<br /><code>U+1EEC</code></td> <td>Ữ<br /><code>U+1EEE</code></td> <td>Ự<br /><code>U+1EF0</code></td> </tr> <tr> <td></td> <td>Ý<br /><code>U+00DD</code></td> <td>Ỳ<br /><code>U+1EF2</code></td> <td>Ỷ<br /><code>U+1EF6</code></td> <td>Ỹ<br /><code>U+1EF8</code></td> <td>Ỵ<br /><code>U+1EF4</code></td> </tr> <tr> <td></td> <td>á<br /><code>U+00E1</code></td> <td>à<br /><code>U+00E0</code></td> <td>ả<br /><code>U+1EA3</code></td> <td>ã<br /><code>U+00E3</code></td> <td>ạ<br /><code>U+1EA1</code></td> </tr> <tr> <td>ă<br /><code>U+0103</code></td> <td>ắ<br /><code>U+1EAF</code></td> <td>ằ<br /><code>U+1EB1</code></td> <td>ẳ<br /><code>U+1EB3</code></td> <td>ẵ<br /><code>U+1EB5</code></td> <td>ặ<br /><code>U+1EB7</code></td> </tr> <tr> <td>â<br /><code>U+00E2</code></td> <td>ấ<br /><code>U+1EA5</code></td> <td>ầ<br /><code>U+1EA7</code></td> <td>ẩ<br /><code>U+1EA9</code></td> <td>ẫ<br /><code>U+1EAB</code></td> <td>ậ<br /><code>U+1EAD</code></td> </tr> <tr> <td>đ<br /><code>U+0111</code></td> <td colspan="5"></td> </tr> <tr> <td></td> <td>è<br /><code>U+00E8</code></td> <td>é<br /><code>U+00E9</code></td> <td>ẻ<br /><code>U+1EBB</code></td> <td>ẽ<br /><code>U+1EBD</code></td> <td>ẹ<br /><code>U+1EB9</code></td> </tr> <tr> <td>ê<br /><code>U+00EA</code></td> <td>ế<br /><code>U+1EBF</code></td> <td>ề<br /><code>U+1EC1</code></td> <td>ể<br /><code>U+1EC3</code></td> <td>ễ<br /><code>U+1EC5</code></td> <td>ệ<br /><code>U+1EC7</code></td> </tr> <tr> <td></td> <td>í<br /><code>U+00ED</code></td> <td>ì<br /><code>U+00EC</code></td> <td>ỉ<br /><code>U+1EC9</code></td> <td>ĩ<br /><code>U+0129</code></td> <td>ị<br /><code>U+1ECB</code></td> </tr> <tr> <td></td> <td>ó<br /><code>U+00F3</code></td> <td>ò<br /><code>U+00F2</code></td> <td>ỏ<br /><code>U+1ECF</code></td> <td>õ<br /><code>U+00F5</code></td> <td>ọ<br /><code>U+1ECD</code></td> </tr> <tr> <td>ô<br /><code>U+00F4</code></td> <td>ố<br /><code>U+1ED1</code></td> <td>ồ<br /><code>U+1ED3</code></td> <td>ổ<br /><code>U+1ED5</code></td> <td>ỗ<br /><code>U+1ED7</code></td> <td>ộ<br /><code>U+1ED9</code></td> </tr> <tr> <td>ơ<br /><code>U+01A1</code></td> <td>ớ<br /><code>U+1EDB</code></td> <td>ờ<br /><code>U+1EDD</code></td> <td>ở<br /><code>U+1EDF</code></td> <td>ỡ<br /><code>U+1EE1</code></td> <td>ợ<br /><code>U+1EE3</code></td> </tr> <tr> <td></td> <td>ú<br /><code>U+00FA</code></td> <td>ù<br /><code>U+00F9</code></td> <td>ủ<br /><code>U+1EE7</code></td> <td>ũ<br /><code>U+0169</code></td> <td>ụ<br /><code>U+1EE5</code></td> </tr> <tr> <td>ư<br /><code>U+01B0</code></td> <td>ứ<br /><code>U+1EE9</code></td> <td>ừ<br /><code>U+1EEB</code></td> <td>ử<br /><code>U+1EED</code></td> <td>ữ<br /><code>U+1EEF</code></td> <td>ự<br /><code>U+1EF1</code></td> </tr> <tr> <td></td> <td>ý<br /><code>U+00FD</code></td> <td>ỳ<br /><code>U+1EF3</code></td> <td>ỷ<br /><code>U+1EF7</code></td> <td>ỹ<br /><code>U+1EF9</code></td> <td>ỵ<br /><code>U+1EF5</code></td> </tr> </tbody></table> This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,69 @@ BANG_DANH_DAU = { "vni": { "Á": "A1" , "À": "A2" , "Ả": "A3" , "Ã": "A4" , "Ạ": "A5" , "Ă": "A8", "Ắ": "A81", "Ằ": "A82", "Ẳ": "A83", "Ẵ": "A84", "Ặ": "A85", "Â": "A6", "Ấ": "A61", "Ầ": "A62", "Ẩ": "A63", "Ẫ": "A64", "Ậ": "A65", "Đ": "D9", "É": "E1" , "È": "E2" , "Ẻ": "E3" , "Ẽ": "E4" , "Ẹ": "E5" , "Ê": "E6", "Ế": "E61", "Ề": "E62", "Ể": "E63", "Ễ": "E64", "Ệ": "E65", "Í": "I1" , "Ì": "I2" , "Ỉ": "I3" , "Ĩ": "I4" , "Ị": "I5" , "Ó": "O1" , "Ò": "O2" , "Ỏ": "O3" , "Õ": "O4" , "Ọ": "O5" , "Ô": "O6", "Ố": "O61", "Ồ": "O62", "Ổ": "O63", "Ỗ": "O64", "Ộ": "O65", "Ơ": "O7", "Ớ": "O71", "Ờ": "O72", "Ở": "O73", "Ỡ": "O74", "Ợ": "O75", "Ú": "U1" , "Ù": "U2" , "Ủ": "U3" , "Ũ": "U4" , "Ụ": "U5" , "Ư": "U7", "Ứ": "U71", "Ừ": "U72", "Ử": "U73", "Ữ": "U74", "Ự": "U75", "Ý": "Y1" , "Ỳ": "Y2" , "Ỷ": "Y3" , "Ỹ": "Y4" , "Ỵ": "Y5" , "á": "a1" , "à": "a2" , "ả": "a3" , "ã": "a4" , "ạ": "a5" , "ă": "a8", "ắ": "a81", "ằ": "a82", "ẳ": "a83", "ẵ": "a84", "ặ": "a85", "â": "a6", "ấ": "a61", "ầ": "a62", "ẩ": "a63", "ẫ": "a64", "ậ": "a65", "đ": "d9", "é": "e1" , "è": "e2" , "ẻ": "e3" , "ẽ": "e4" , "ẹ": "e5" , "ê": "e6", "ế": "e61", "ề": "e62", "ể": "e63", "ễ": "e64", "ệ": "e65", "í": "i1" , "ì": "i2" , "ỉ": "i3" , "ĩ": "i4" , "ị": "i5" , "ó": "o1" , "ò": "o2" , "ỏ": "o3" , "õ": "o4" , "ọ": "o5" , "ô": "o6", "ố": "o61", "ồ": "o62", "ổ": "o63", "ỗ": "o64", "ộ": "o65", "ơ": "o7", "ớ": "o71", "ờ": "o72", "ở": "o73", "ỡ": "o74", "ợ": "o75", "ú": "u1" , "ù": "u2" , "ủ": "u3" , "ũ": "u4" , "ụ": "u5" , "ư": "u7", "ứ": "u71", "ừ": "u72", "ử": "u73", "ữ": "u74", "ự": "u75", "ý": "y1" , "ỳ": "y2" , "ỷ": "y3" , "ỹ": "y4" , "ỵ": "y5" , }, "telex": { "Á": "AS" , "À": "AF" , "Ả": "AR" , "Ã": "AX" , "Ạ": "AJ" , "Ă": "AW", "Ắ": "AWS", "Ằ": "AWF", "Ẳ": "AWR", "Ẵ": "AWX", "Ặ": "AWJ", "Â": "AA", "Ấ": "AAS", "Ầ": "AAF", "Ẩ": "AAR", "Ẫ": "AAX", "Ậ": "AAJ", "Đ": "DD", "É": "ES" , "È": "EF" , "Ẻ": "ER" , "Ẽ": "EX" , "Ẹ": "EJ" , "Ê": "EE", "Ế": "EES", "Ề": "EEF", "Ể": "EER", "Ễ": "EEX", "Ệ": "EEJ", "Í": "IS" , "Ì": "IF" , "Ỉ": "IR" , "Ĩ": "IX" , "Ị": "IJ" , "Ó": "OS" , "Ò": "OF" , "Ỏ": "OR" , "Õ": "OX" , "Ọ": "OJ" , "Ô": "OO", "Ố": "OOS", "Ồ": "OOF", "Ổ": "OOR", "Ỗ": "OOX", "Ộ": "OOJ", "Ơ": "OW", "Ớ": "OWS", "Ờ": "OWF", "Ở": "OWR", "Ỡ": "OWX", "Ợ": "OWJ", "Ú": "US" , "Ù": "UF" , "Ủ": "UR" , "Ũ": "UX" , "Ụ": "UJ" , "Ư": "UW", "Ứ": "UWS", "Ừ": "UWF", "Ử": "UWR", "Ữ": "UWX", "Ự": "UWJ", "Ý": "YS" , "Ỳ": "YF" , "Ỷ": "YR" , "Ỹ": "YX" , "Ỵ": "YJ" , "á": "as" , "à": "af" , "ả": "ar" , "ã": "ax" , "ạ": "aj" , "ă": "aw", "ắ": "aws", "ằ": "awf", "ẳ": "awr", "ẵ": "awx", "ặ": "awj", "â": "aa", "ấ": "aas", "ầ": "aaf", "ẩ": "aar", "ẫ": "aax", "ậ": "aaj", "đ": "dd", "é": "es" , "è": "ef" , "ẻ": "er" , "ẽ": "ex" , "ẹ": "ej" , "ê": "ee", "ế": "ees", "ề": "eef", "ể": "eer", "ễ": "eex", "ệ": "eej", "í": "is" , "ì": "if" , "ỉ": "ir" , "ĩ": "ix" , "ị": "ij" , "ó": "os" , "ò": "of" , "ỏ": "or" , "õ": "ox" , "ọ": "oj" , "ô": "oo", "ố": "oos", "ồ": "oof", "ổ": "oor", "ỗ": "oox", "ộ": "ooj", "ơ": "ow", "ớ": "ows", "ờ": "owf", "ở": "owr", "ỡ": "owx", "ợ": "owj", "ú": "us" , "ù": "uf" , "ủ": "ur" , "ũ": "ux" , "ụ": "uj" , "ư": "uw", "ứ": "uws", "ừ": "uwf", "ử": "uwr", "ữ": "uwx", "ự": "uwj", "ý": "ys" , "ỳ": "yf" , "ỷ": "yr" , "ỹ": "yx" , "ỵ": "yj" , } } def xoa_dau_sang_vni_telex(txt: str, kieu_go: str) -> str: kieu_go = kieu_go.lower() if kieu_go not in BANG_DANH_DAU: raise Exception("kiểu gõ ko hợp lệ") for k, v in BANG_DANH_DAU[kieu_go].items(): txt = txt.replace(k, v) return txt xoa_dau_sang_vni_telex(txt, "vni") # "“D9a5o d9u71c kinh”" xoa_dau_sang_vni_telex(txt, "telex") # "“DDajo dduwsc kinh”"