Skip to content

Instantly share code, notes, and snippets.

@phineas-pta
Last active April 19, 2025 09:21
Show Gist options
  • Select an option

  • Save phineas-pta/05cad38a29fea000ab6d9e13a6f7e623 to your computer and use it in GitHub Desktop.

Select an option

Save phineas-pta/05cad38a29fea000ab6d9e13a6f7e623 to your computer and use it in GitHub Desktop.

Revisions

  1. phineas-pta revised this gist May 25, 2023. 2 changed files with 112 additions and 11 deletions.
    23 changes: 21 additions & 2 deletions xoa_dau.md
    Original 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

    - *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
    - *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"'
    ```

    - *dùng `flashtext.KeywordProcessor()`*: hứa hẹn tốc độ nhanh hơn cả `.replace()``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**
    - *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()``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

    100 changes: 91 additions & 9 deletions xoa_dau_benchmark.ipynb
    91 additions, 9 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
  2. phineas-pta revised this gist May 24, 2023. 2 changed files with 230 additions and 4 deletions.
    10 changes: 6 additions & 4 deletions xoa_dau.md
    Original 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, hiệu quả hơn và không cần cài thêm package
    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" , …}
    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
    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()`
    - *dùng `RegEx`*: viết code ngắn hơn, ~~tốc độ nhanh hơn `.replace()`~~

    ```python
    import re
    224 changes: 224 additions & 0 deletions xoa_dau_benchmark.ipynb
    Original 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
    }
  3. phineas-pta revised this gist May 23, 2023. 1 changed file with 32 additions and 22 deletions.
    54 changes: 32 additions & 22 deletions xoa_dau.md
    Original file line number Diff line number Diff line change
    @@ -7,15 +7,15 @@ code hoàn chỉnh:
    ```python
    import unicodedata

    DIACRITICS_TABLE = str.maketrans(
    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(DIACRITICS_TABLE)
    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
    DIACRITICS_TABLE = str.maketrans(
    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(DIACRITICS_TABLE) # "“ Dao duc kinh”"
    txt.translate(BANG_XOA_DAU) # "“ Dao duc kinh”"
    ```

    lệnh thay thế kí tự trên toàn chuỗi đúng 1 lần duy nhất
    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
    unidecode(txt) # '" Dao duc kinh"'
    unidecode(raw_txt) # '" Dao duc kinh"'
    txt = "“Đạo đức kinh"
    unidecode(txt) # '" Dao duc kinh"'
    ```

    - *dùng `flashtext.KeywordProcessor()`*: hứa hẹn tốc độ nhanh hơn cả `.replace()``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
    - 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 = {"Á": "A" , "À": "A" , …} # total 134 items
    for k, v in BANG_XOA_DAU.items():
    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 `.replace()` được thực thi 134 lần
    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 = { # compile and save regex object for reuse is more efficient
    BANG_XOA_DAU_3 = { # 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 = "“Đạo đức kinh”"
    for k, v in BANG_XOA_DAU_3.items():
    txt = v.sub(k, txt)
    ```

    lệnh `re.sub()` được thực thi 14 lần
    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>
    <td>&#774; (dấu ă)<br /><code>U+0306</code></td>
    <td>&#770; (dấu mũ âêô)<br /><code>U+0302</code></td>
    <td>&#795; (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>
    <td>&#769; (dấu sắc)<br /><code>U+0301</code></td>
    <td>&#768; (dấu huyền)<br /><code>U+0300</code></td>
    <td>&#777; (dấu hỏi)<br /><code>U+0309</code></td>
    <td>&#771; (dấu ngã)<br /><code>U+0303</code></td>
    <td>&#803; (dấu nặng)<br /><code>U+0323</code></td>
    </tr></tbody></table>

    ### 3.2. dạng unicode chuẩn C
  4. phineas-pta created this gist May 23, 2023.
    356 changes: 356 additions & 0 deletions xoa_dau.md
    Original 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à `u&#795;&#769;`, lưu ý thứ tự của `U+031B``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()``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>
    69 changes: 69 additions & 0 deletions xoa_dau_sang_vni_telex.py
    Original 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”"