Upload folder using huggingface_hub
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .gitignore +2 -1
- .idea/.gitignore +8 -0
- .idea/Generate Audio Data.iml +10 -0
- .idea/inspectionProfiles/Project_Default.xml +40 -0
- .idea/inspectionProfiles/profiles_settings.xml +6 -0
- .idea/misc.xml +7 -0
- .idea/modules.xml +8 -0
- .idea/vcs.xml +6 -0
- .idea/workspace.xml +98 -0
- __pycache__/gemini_normalize.cpython-310.pyc +0 -0
- __pycache__/text_nomalize.cpython-310.pyc +0 -0
- converters/Cardinal.py +189 -0
- converters/Date.py +107 -0
- converters/Decimal.py +86 -0
- converters/Digit.py +62 -0
- converters/Fraction.py +110 -0
- converters/Meansure.py +473 -0
- converters/Money.py +155 -0
- converters/Ordinal.py +74 -0
- converters/Range.py +36 -0
- converters/Roman.py +88 -0
- converters/Telephone.py +90 -0
- converters/Time.py +100 -0
- converters/__init__.py +0 -0
- converters/__pycache__/Cardinal.cpython-310.pyc +0 -0
- converters/__pycache__/Date.cpython-310.pyc +0 -0
- converters/__pycache__/Decimal.cpython-310.pyc +0 -0
- converters/__pycache__/Digit.cpython-310.pyc +0 -0
- converters/__pycache__/Fraction.cpython-310.pyc +0 -0
- converters/__pycache__/Meansure.cpython-310.pyc +0 -0
- converters/__pycache__/Money.cpython-310.pyc +0 -0
- converters/__pycache__/Range.cpython-310.pyc +0 -0
- converters/__pycache__/Roman.cpython-310.pyc +0 -0
- converters/__pycache__/Telephone.cpython-310.pyc +0 -0
- converters/__pycache__/Time.cpython-310.pyc +0 -0
- converters/__pycache__/__init__.cpython-310.pyc +0 -0
- download.sh +8 -0
- train_list.txt +0 -0
- val_list.txt +68 -0
- whisperx/SubtitlesProcessor.py +227 -0
- whisperx/__init__.py +4 -0
- whisperx/__main__.py +4 -0
- whisperx/__pycache__/__init__.cpython-310.pyc +0 -0
- whisperx/__pycache__/alignment.cpython-310.pyc +0 -0
- whisperx/__pycache__/asr.cpython-310.pyc +0 -0
- whisperx/__pycache__/audio.cpython-310.pyc +0 -0
- whisperx/__pycache__/diarize.cpython-310.pyc +0 -0
- whisperx/__pycache__/transcribe.cpython-310.pyc +0 -0
- whisperx/__pycache__/types.cpython-310.pyc +0 -0
- whisperx/__pycache__/utils.cpython-310.pyc +0 -0
.gitignore
CHANGED
@@ -2,4 +2,5 @@
|
|
2 |
downloaded_audio/
|
3 |
figures/
|
4 |
audio_cut/
|
5 |
-
audio_cut.zip
|
|
|
|
2 |
downloaded_audio/
|
3 |
figures/
|
4 |
audio_cut/
|
5 |
+
audio_cut.zip
|
6 |
+
dataset/
|
.idea/.gitignore
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Default ignored files
|
2 |
+
/shelf/
|
3 |
+
/workspace.xml
|
4 |
+
# Editor-based HTTP Client requests
|
5 |
+
/httpRequests/
|
6 |
+
# Datasource local storage ignored files
|
7 |
+
/dataSources/
|
8 |
+
/dataSources.local.xml
|
.idea/Generate Audio Data.iml
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
+
<module type="PYTHON_MODULE" version="4">
|
3 |
+
<component name="NewModuleRootManager">
|
4 |
+
<content url="file://$MODULE_DIR$">
|
5 |
+
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
6 |
+
</content>
|
7 |
+
<orderEntry type="jdk" jdkName="Python 3.10 (Explanation)" jdkType="Python SDK" />
|
8 |
+
<orderEntry type="sourceFolder" forTests="false" />
|
9 |
+
</component>
|
10 |
+
</module>
|
.idea/inspectionProfiles/Project_Default.xml
ADDED
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<component name="InspectionProjectProfileManager">
|
2 |
+
<profile version="1.0">
|
3 |
+
<option name="myName" value="Project Default" />
|
4 |
+
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
5 |
+
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
6 |
+
<option name="ignoredPackages">
|
7 |
+
<value>
|
8 |
+
<list size="26">
|
9 |
+
<item index="0" class="java.lang.String" itemvalue="torch" />
|
10 |
+
<item index="1" class="java.lang.String" itemvalue="torchvision" />
|
11 |
+
<item index="2" class="java.lang.String" itemvalue="timm" />
|
12 |
+
<item index="3" class="java.lang.String" itemvalue="ellzaf_ml" />
|
13 |
+
<item index="4" class="java.lang.String" itemvalue="scipy" />
|
14 |
+
<item index="5" class="java.lang.String" itemvalue="scikit_learn" />
|
15 |
+
<item index="6" class="java.lang.String" itemvalue="matplotlib" />
|
16 |
+
<item index="7" class="java.lang.String" itemvalue="opencv_contrib_python" />
|
17 |
+
<item index="8" class="java.lang.String" itemvalue="model" />
|
18 |
+
<item index="9" class="java.lang.String" itemvalue="opencv_python" />
|
19 |
+
<item index="10" class="java.lang.String" itemvalue="Pillow" />
|
20 |
+
<item index="11" class="java.lang.String" itemvalue="pydriver" />
|
21 |
+
<item index="12" class="java.lang.String" itemvalue="pytest-pylint" />
|
22 |
+
<item index="13" class="java.lang.String" itemvalue="opencv-python" />
|
23 |
+
<item index="14" class="java.lang.String" itemvalue="pytest" />
|
24 |
+
<item index="15" class="java.lang.String" itemvalue="sklearn" />
|
25 |
+
<item index="16" class="java.lang.String" itemvalue="attrdict" />
|
26 |
+
<item index="17" class="java.lang.String" itemvalue="numpy" />
|
27 |
+
<item index="18" class="java.lang.String" itemvalue="albumentations" />
|
28 |
+
<item index="19" class="java.lang.String" itemvalue="pandas" />
|
29 |
+
<item index="20" class="java.lang.String" itemvalue="tqdm" />
|
30 |
+
<item index="21" class="java.lang.String" itemvalue="tensorboard" />
|
31 |
+
<item index="22" class="java.lang.String" itemvalue="pylint" />
|
32 |
+
<item index="23" class="java.lang.String" itemvalue="onnx" />
|
33 |
+
<item index="24" class="java.lang.String" itemvalue="opencv_python_headless" />
|
34 |
+
<item index="25" class="java.lang.String" itemvalue="PyQt5" />
|
35 |
+
</list>
|
36 |
+
</value>
|
37 |
+
</option>
|
38 |
+
</inspection_tool>
|
39 |
+
</profile>
|
40 |
+
</component>
|
.idea/inspectionProfiles/profiles_settings.xml
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<component name="InspectionProjectProfileManager">
|
2 |
+
<settings>
|
3 |
+
<option name="USE_PROJECT_PROFILE" value="false" />
|
4 |
+
<version value="1.0" />
|
5 |
+
</settings>
|
6 |
+
</component>
|
.idea/misc.xml
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
+
<project version="4">
|
3 |
+
<component name="Black">
|
4 |
+
<option name="sdkName" value="Python 3.10 (Explanation)" />
|
5 |
+
</component>
|
6 |
+
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (Explanation)" project-jdk-type="Python SDK" />
|
7 |
+
</project>
|
.idea/modules.xml
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
+
<project version="4">
|
3 |
+
<component name="ProjectModuleManager">
|
4 |
+
<modules>
|
5 |
+
<module fileurl="file://$PROJECT_DIR$/.idea/Generate Audio Data.iml" filepath="$PROJECT_DIR$/.idea/Generate Audio Data.iml" />
|
6 |
+
</modules>
|
7 |
+
</component>
|
8 |
+
</project>
|
.idea/vcs.xml
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
+
<project version="4">
|
3 |
+
<component name="VcsDirectoryMappings">
|
4 |
+
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
5 |
+
</component>
|
6 |
+
</project>
|
.idea/workspace.xml
ADDED
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
+
<project version="4">
|
3 |
+
<component name="AutoImportSettings">
|
4 |
+
<option name="autoReloadType" value="SELECTIVE" />
|
5 |
+
</component>
|
6 |
+
<component name="ChangeListManager">
|
7 |
+
<list default="true" id="6348f10b-d99d-4a46-ac7b-9eed28d7fa2a" name="Changes" comment="" />
|
8 |
+
<option name="SHOW_DIALOG" value="false" />
|
9 |
+
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
10 |
+
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
11 |
+
<option name="LAST_RESOLUTION" value="IGNORE" />
|
12 |
+
</component>
|
13 |
+
<component name="FileTemplateManagerImpl">
|
14 |
+
<option name="RECENT_TEMPLATES">
|
15 |
+
<list>
|
16 |
+
<option value="Python Script" />
|
17 |
+
</list>
|
18 |
+
</option>
|
19 |
+
</component>
|
20 |
+
<component name="Git.Settings">
|
21 |
+
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
22 |
+
</component>
|
23 |
+
<component name="ProjectColorInfo">{
|
24 |
+
"associatedIndex": 6
|
25 |
+
}</component>
|
26 |
+
<component name="ProjectId" id="2mZJfy2ux2nxKThwMyI4nltiFiJ" />
|
27 |
+
<component name="ProjectViewState">
|
28 |
+
<option name="hideEmptyMiddlePackages" value="true" />
|
29 |
+
<option name="showLibraryContents" value="true" />
|
30 |
+
</component>
|
31 |
+
<component name="PropertiesComponent"><![CDATA[{
|
32 |
+
"keyToString": {
|
33 |
+
"Python.download_audio.executor": "Run",
|
34 |
+
"Python.gen_dataset.executor": "Run",
|
35 |
+
"Python.main.executor": "Run",
|
36 |
+
"Python.separate.executor": "Run",
|
37 |
+
"Python.test_dataset.executor": "Run",
|
38 |
+
"Python.upload_huggingface.executor": "Run",
|
39 |
+
"RunOnceActivity.ShowReadmeOnStart": "true",
|
40 |
+
"git-widget-placeholder": "master",
|
41 |
+
"last_opened_file_path": "E:/AI/Generate Audio Data",
|
42 |
+
"node.js.detected.package.eslint": "true",
|
43 |
+
"node.js.detected.package.tslint": "true",
|
44 |
+
"node.js.selected.package.eslint": "(autodetect)",
|
45 |
+
"node.js.selected.package.tslint": "(autodetect)",
|
46 |
+
"nodejs_package_manager_path": "npm",
|
47 |
+
"settings.editor.selected.configurable": "project.propVCSSupport.DirectoryMappings",
|
48 |
+
"vue.rearranger.settings.migration": "true"
|
49 |
+
}
|
50 |
+
}]]></component>
|
51 |
+
<component name="RecentsManager">
|
52 |
+
<key name="CopyFile.RECENT_KEYS">
|
53 |
+
<recent name="E:\AI\Generate Audio Data" />
|
54 |
+
<recent name="E:\AI\Generate Audio Data\dataset" />
|
55 |
+
</key>
|
56 |
+
</component>
|
57 |
+
<component name="SharedIndexes">
|
58 |
+
<attachedChunks>
|
59 |
+
<set>
|
60 |
+
<option value="bundled-js-predefined-1d06a55b98c1-91d5c284f522-JavaScript-PY-241.15989.155" />
|
61 |
+
<option value="bundled-python-sdk-babbdf50b680-7c6932dee5e4-com.jetbrains.pycharm.pro.sharedIndexes.bundled-PY-241.15989.155" />
|
62 |
+
</set>
|
63 |
+
</attachedChunks>
|
64 |
+
</component>
|
65 |
+
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
|
66 |
+
<component name="TaskManager">
|
67 |
+
<task active="true" id="Default" summary="Default task">
|
68 |
+
<changelist id="6348f10b-d99d-4a46-ac7b-9eed28d7fa2a" name="Changes" comment="" />
|
69 |
+
<created>1727278271929</created>
|
70 |
+
<option name="number" value="Default" />
|
71 |
+
<option name="presentableId" value="Default" />
|
72 |
+
<updated>1727278271929</updated>
|
73 |
+
<workItem from="1727278276767" duration="149000" />
|
74 |
+
<workItem from="1727309175324" duration="14258000" />
|
75 |
+
<workItem from="1727340993328" duration="20898000" />
|
76 |
+
<workItem from="1727430990250" duration="574000" />
|
77 |
+
<workItem from="1727528882113" duration="16350000" />
|
78 |
+
<workItem from="1727755697813" duration="11670000" />
|
79 |
+
<workItem from="1727846631606" duration="10850000" />
|
80 |
+
<workItem from="1727864061128" duration="1144000" />
|
81 |
+
<workItem from="1727865356249" duration="3690000" />
|
82 |
+
<workItem from="1727870630865" duration="7260000" />
|
83 |
+
<workItem from="1727879789158" duration="302000" />
|
84 |
+
</task>
|
85 |
+
<servers />
|
86 |
+
</component>
|
87 |
+
<component name="TypeScriptGeneratedFilesManager">
|
88 |
+
<option name="version" value="3" />
|
89 |
+
</component>
|
90 |
+
<component name="com.intellij.coverage.CoverageDataManagerImpl">
|
91 |
+
<SUITE FILE_PATH="coverage/Generate_Audio_Data$gen_dataset.coverage" NAME="gen_dataset Coverage Results" MODIFIED="1727855498024" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
|
92 |
+
<SUITE FILE_PATH="coverage/Generate_Audio_Data$download_audio.coverage" NAME="download_audio Coverage Results" MODIFIED="1727855814027" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
|
93 |
+
<SUITE FILE_PATH="coverage/Generate_Audio_Data$separate.coverage" NAME="separate Coverage Results" MODIFIED="1727772728510" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
|
94 |
+
<SUITE FILE_PATH="coverage/Generate_Audio_Data$main.coverage" NAME="main Coverage Results" MODIFIED="1727532107608" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
|
95 |
+
<SUITE FILE_PATH="coverage/Generate_Audio_Data$test_dataset.coverage" NAME="test_dataset Coverage Results" MODIFIED="1727356745919" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
|
96 |
+
<SUITE FILE_PATH="coverage/Generate_Audio_Data$upload_huggingface.coverage" NAME="upload_huggingface Coverage Results" MODIFIED="1727878833042" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
|
97 |
+
</component>
|
98 |
+
</project>
|
__pycache__/gemini_normalize.cpython-310.pyc
ADDED
Binary file (9.31 kB). View file
|
|
__pycache__/text_nomalize.cpython-310.pyc
ADDED
Binary file (3.83 kB). View file
|
|
converters/Cardinal.py
ADDED
@@ -0,0 +1,189 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from typing import List
|
2 |
+
|
3 |
+
from singleton_decorator import singleton
|
4 |
+
import re
|
5 |
+
from .Roman import RomanVietnamese
|
6 |
+
|
7 |
+
|
8 |
+
@singleton
|
9 |
+
class CardinalVietnamese:
|
10 |
+
"""
|
11 |
+
Các bước:
|
12 |
+
- 1 Loại bỏ dấu chấm
|
13 |
+
- 2 Kiểm tra xem có phải là số La Mã không
|
14 |
+
- 3 Nếu đúng, chuyển đổi số La Mã lớn nhất tìm thấy thành số nguyên, sau đó thành chuỗi đại diện cho số nguyên đó
|
15 |
+
- 4 Nếu đúng, kiểm tra xem có nên thêm hậu tố "'s" không (xem trường hợp đặc biệt)
|
16 |
+
- 5 Lọc bỏ các ký tự không phải số, trừ "-"
|
17 |
+
- 6 Kiểm tra xem có nên sử dụng tiền tố "âm" không
|
18 |
+
- 7 Loại bỏ tất cả các ký tự "-" còn lại
|
19 |
+
- 8 Nếu là "0", thêm "không" vào danh sách đầu ra
|
20 |
+
- 9 Nếu không phải "0", chia chuỗi thành các phần tối đa 3 chữ số, sao cho phần nhỏ nhất bao gồm các ký tự ngoài cùng bên trái
|
21 |
+
- 10 Chia mỗi phần thành `trăm` và `phần còn lại`
|
22 |
+
- 11 Thêm "x trăm" nếu `trăm` > 0
|
23 |
+
- 12 Thêm giá trị văn bản đại diện cho `phần còn lại`
|
24 |
+
- 13 Thêm hậu tố cho phần, ví dụ: triệu, tỷ, v.v.
|
25 |
+
- 14 Thêm đầu ra cho phần vào tổng đầu ra
|
26 |
+
- 15 Kết hợp danh sách đầu ra tổng thành một chuỗi
|
27 |
+
- 16 Thêm tiền tố và/hoặc hậu tố
|
28 |
+
|
29 |
+
Trường hợp đặc biệt:
|
30 |
+
II -> hai
|
31 |
+
-2 -> âm hai
|
32 |
+
I. -> một
|
33 |
+
IV's -> bốn's
|
34 |
+
|
35 |
+
Ghi chú:
|
36 |
+
- Không có "và" hoặc dấu gạch ngang trong kết quả, ví dụ: không có "hai mươi mốt" hoặc "một trăm lẻ một"
|
37 |
+
"""
|
38 |
+
|
39 |
+
def __init__(self):
|
40 |
+
super().__init__()
|
41 |
+
self.filter_regex = re.compile("[^0-9\-]")
|
42 |
+
self.filter_strict_regex = re.compile("[^0-9]")
|
43 |
+
self.dot_filter_regex = re.compile("[.]")
|
44 |
+
|
45 |
+
self.scale_suffixes = [
|
46 |
+
"nghìn",
|
47 |
+
"triệu",
|
48 |
+
"tỷ",
|
49 |
+
"nghìn tỷ",
|
50 |
+
"triệu tỷ",
|
51 |
+
"tỷ tỷ"
|
52 |
+
]
|
53 |
+
|
54 |
+
self.small_trans_dict = {
|
55 |
+
"1": "một",
|
56 |
+
"2": "hai",
|
57 |
+
"3": "ba",
|
58 |
+
"4": "bốn",
|
59 |
+
"5": "năm",
|
60 |
+
"6": "sáu",
|
61 |
+
"7": "bảy",
|
62 |
+
"8": "tám",
|
63 |
+
"9": "chín",
|
64 |
+
}
|
65 |
+
|
66 |
+
self.tens_trans_dict = {
|
67 |
+
"1": "mười",
|
68 |
+
"2": "hai mươi",
|
69 |
+
"3": "ba mươi",
|
70 |
+
"4": "bốn mươi",
|
71 |
+
"5": "năm mươi",
|
72 |
+
"6": "sáu mươi",
|
73 |
+
"7": "bảy mươi",
|
74 |
+
"8": "tám mươi",
|
75 |
+
"9": "chín mươi",
|
76 |
+
}
|
77 |
+
|
78 |
+
self.special_trans_dict = {
|
79 |
+
11: "mười một",
|
80 |
+
12: "mười hai",
|
81 |
+
13: "mười ba",
|
82 |
+
14: "mười bốn",
|
83 |
+
15: "mười lăm",
|
84 |
+
16: "mười sáu",
|
85 |
+
17: "mười bảy",
|
86 |
+
18: "mười tám",
|
87 |
+
19: "mười chín"
|
88 |
+
}
|
89 |
+
|
90 |
+
self.roman = RomanVietnamese()
|
91 |
+
|
92 |
+
def _give_chunk(self, num_str: str, size: int = 3) -> str:
|
93 |
+
while num_str:
|
94 |
+
yield num_str[-size:]
|
95 |
+
num_str = num_str[:-size]
|
96 |
+
|
97 |
+
def convert(self, token: str) -> str:
|
98 |
+
token = self.dot_filter_regex.sub("", token)
|
99 |
+
|
100 |
+
suffix = ""
|
101 |
+
if self.roman.check_if_roman(token):
|
102 |
+
token, suffix = self.roman.convert(token)
|
103 |
+
|
104 |
+
token = self.filter_regex.sub("", token)
|
105 |
+
|
106 |
+
prefix = ""
|
107 |
+
while len(token) > 0 and token[0] == "-":
|
108 |
+
token = token[1:]
|
109 |
+
prefix = "âm" if prefix == "" else ""
|
110 |
+
|
111 |
+
token = self.filter_strict_regex.sub("", token)
|
112 |
+
|
113 |
+
if token == len(token) * "0":
|
114 |
+
return "không"
|
115 |
+
|
116 |
+
chunks = list(self._give_chunk(token))
|
117 |
+
text_list = []
|
118 |
+
|
119 |
+
for depth, chunk in enumerate(chunks):
|
120 |
+
chunk_text = self._convert_chunk(chunk, depth == len(chunks) - 1)
|
121 |
+
if chunk_text:
|
122 |
+
if depth > 0:
|
123 |
+
chunk_text.append(self.scale_suffixes[depth - 1])
|
124 |
+
text_list = chunk_text + text_list
|
125 |
+
|
126 |
+
result = " ".join(text_list)
|
127 |
+
|
128 |
+
if prefix:
|
129 |
+
result = f"{prefix} {result}"
|
130 |
+
if suffix:
|
131 |
+
result = f"{result}{suffix}"
|
132 |
+
|
133 |
+
return result
|
134 |
+
|
135 |
+
def _convert_chunk(self, chunk: str, is_last_chunk: bool) -> List[str]:
|
136 |
+
chunk_text = []
|
137 |
+
length = len(chunk)
|
138 |
+
|
139 |
+
# Xử lý hàng trăm
|
140 |
+
if length == 3:
|
141 |
+
if chunk[0] != '0':
|
142 |
+
chunk_text.append(self.small_trans_dict[chunk[0]])
|
143 |
+
chunk_text.append("trăm")
|
144 |
+
elif not is_last_chunk and (chunk[1] != '0' or chunk[2] != '0'):
|
145 |
+
chunk_text.append("không trăm")
|
146 |
+
|
147 |
+
# Xử lý hàng chục và đơn vị
|
148 |
+
if length >= 2:
|
149 |
+
if chunk[-2:] in self.special_trans_dict:
|
150 |
+
chunk_text.append(self.special_trans_dict[chunk[-2:]])
|
151 |
+
else:
|
152 |
+
if chunk[-2] != '0':
|
153 |
+
chunk_text.append(self.tens_trans_dict[chunk[-2]])
|
154 |
+
if chunk[-1] != '0':
|
155 |
+
if chunk[-1] == '1' and chunk[-2] != '1':
|
156 |
+
chunk_text.append("mốt")
|
157 |
+
else:
|
158 |
+
chunk_text.append(self.small_trans_dict[chunk[-1]])
|
159 |
+
elif chunk[-1] != '0':
|
160 |
+
if len(chunk_text) > 0 or not is_last_chunk:
|
161 |
+
chunk_text.append("lẻ")
|
162 |
+
chunk_text.append(self.small_trans_dict[chunk[-1]])
|
163 |
+
elif length == 1 and chunk != '0':
|
164 |
+
chunk_text.append(self.small_trans_dict[chunk])
|
165 |
+
|
166 |
+
return chunk_text
|
167 |
+
|
168 |
+
if __name__ == "__main__":
|
169 |
+
cardinal_converter = CardinalVietnamese()
|
170 |
+
|
171 |
+
# Ví dụ 1
|
172 |
+
example1 = "20211231"
|
173 |
+
result1 = cardinal_converter.convert(example1)
|
174 |
+
print(f"Số: {example1}")
|
175 |
+
print(f"Chuyển đổi: {result1}")
|
176 |
+
print()
|
177 |
+
|
178 |
+
# Ví dụ 2
|
179 |
+
example2 = "-5678213"
|
180 |
+
result2 = cardinal_converter.convert(example2)
|
181 |
+
print(f"Số: {example2}")
|
182 |
+
print(f"Chuyển đổi: {result2}")
|
183 |
+
print()
|
184 |
+
|
185 |
+
# Ví dụ 3
|
186 |
+
example3 = "1000000"
|
187 |
+
result3 = cardinal_converter.convert(example3)
|
188 |
+
print(f"Số: {example3}")
|
189 |
+
print(f"Chuyển đổi: {result3}")
|
converters/Date.py
ADDED
@@ -0,0 +1,107 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import re
|
2 |
+
from singleton_decorator import singleton
|
3 |
+
import re
|
4 |
+
from .Cardinal import CardinalVietnamese
|
5 |
+
import re
|
6 |
+
|
7 |
+
@singleton
|
8 |
+
class DateVietnamese:
|
9 |
+
"""
|
10 |
+
Chuyển đổi ngày tháng từ dạng số sang dạng chữ tiếng Việt.
|
11 |
+
Hỗ trợ các định dạng: "DD/MM/YYYY", "DD-MM-YYYY", "DD.MM.YYYY".
|
12 |
+
Ví dụ: "25/12/2023", "01-01-2024", "10.05.2023"
|
13 |
+
"""
|
14 |
+
|
15 |
+
def __init__(self):
|
16 |
+
self.day_trans_dict = {
|
17 |
+
"1": "một",
|
18 |
+
"2": "hai",
|
19 |
+
"3": "ba",
|
20 |
+
"4": "bốn",
|
21 |
+
"5": "năm",
|
22 |
+
"6": "sáu",
|
23 |
+
"7": "bảy",
|
24 |
+
"8": "tám",
|
25 |
+
"9": "chín",
|
26 |
+
"10": "mười",
|
27 |
+
"11": "mười một",
|
28 |
+
"12": "mười hai",
|
29 |
+
"13": "mười ba",
|
30 |
+
"14": "mười bốn",
|
31 |
+
"15": "mười lăm",
|
32 |
+
"16": "mười sáu",
|
33 |
+
"17": "mười bảy",
|
34 |
+
"18": "mười tám",
|
35 |
+
"19": "mười chín",
|
36 |
+
"20": "hai mươi",
|
37 |
+
"21": "hai mươi mốt",
|
38 |
+
"22": "hai mươi hai",
|
39 |
+
"23": "hai mươi ba",
|
40 |
+
"24": "hai mươi bốn",
|
41 |
+
"25": "hai mươi lăm",
|
42 |
+
"26": "hai mươi sáu",
|
43 |
+
"27": "hai mươi bảy",
|
44 |
+
"28": "hai mươi tám",
|
45 |
+
"29": "hai mươi chín",
|
46 |
+
"30": "ba mươi",
|
47 |
+
"31": "ba mươi mốt"
|
48 |
+
}
|
49 |
+
|
50 |
+
self.month_trans_dict = {
|
51 |
+
"1": "một",
|
52 |
+
"2": "hai",
|
53 |
+
"3": "ba",
|
54 |
+
"4": "tư",
|
55 |
+
"5": "năm",
|
56 |
+
"6": "sáu",
|
57 |
+
"7": "bảy",
|
58 |
+
"8": "tám",
|
59 |
+
"9": "chín",
|
60 |
+
"10": "mười",
|
61 |
+
"11": "mười một",
|
62 |
+
"12": "mười hai"
|
63 |
+
}
|
64 |
+
|
65 |
+
def convert_year(self, year: str) -> str:
|
66 |
+
cardinal_converter = CardinalVietnamese()
|
67 |
+
return cardinal_converter.convert(year)
|
68 |
+
|
69 |
+
def convert_date(self, date: str) -> str:
|
70 |
+
# Tìm và tách ngày, tháng, năm dựa trên dấu phân cách
|
71 |
+
date_parts = re.split(r"[\/\-\.\s]", date)
|
72 |
+
if len(date_parts) ==3 :
|
73 |
+
day, month, year = date_parts[0], date_parts[1], date_parts[2]
|
74 |
+
|
75 |
+
day_text = f"ngày {self.day_trans_dict[day.lstrip('0')]}"
|
76 |
+
month_text = f"tháng {self.month_trans_dict[month.lstrip('0')]}"
|
77 |
+
year_text = f"năm {self.convert_year(year)}"
|
78 |
+
|
79 |
+
return f"{day_text} {month_text} {year_text}"
|
80 |
+
else:
|
81 |
+
month, year = date_parts[0], date_parts[1],
|
82 |
+
month_text = f"tháng {self.month_trans_dict[month.lstrip('0')]}"
|
83 |
+
year_text = f"năm {self.convert_year(year)}"
|
84 |
+
return f"{month_text} {year_text}"
|
85 |
+
if __name__ == "__main__":
|
86 |
+
date_converter = DateVietnamese()
|
87 |
+
|
88 |
+
# Ví dụ 1
|
89 |
+
example1 = "25/12/1990"
|
90 |
+
result1 = date_converter.convert_date(example1)
|
91 |
+
print(f"Ngày tháng: {example1}")
|
92 |
+
print(f"Chuyển đổi: {result1}")
|
93 |
+
print()
|
94 |
+
|
95 |
+
# Ví dụ 2
|
96 |
+
example2 = "01-01-1991"
|
97 |
+
result2 = date_converter.convert_date(example2)
|
98 |
+
print(f"Ngày tháng: {example2}")
|
99 |
+
print(f"Chuyển đổi: {result2}")
|
100 |
+
print()
|
101 |
+
|
102 |
+
# Ví dụ 3
|
103 |
+
example3 = "10.05.2023"
|
104 |
+
result3 = date_converter.convert_date(example3)
|
105 |
+
print(f"Ngày tháng: {example3}")
|
106 |
+
print(f"Chuyển đổi: {result3}")
|
107 |
+
|
converters/Decimal.py
ADDED
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from .Digit import DigitVietnamese
|
2 |
+
from .Cardinal import CardinalVietnamese
|
3 |
+
import re
|
4 |
+
from singleton_decorator import singleton
|
5 |
+
@singleton
|
6 |
+
class Decimal:
|
7 |
+
def __init__(self):
|
8 |
+
super().__init__()
|
9 |
+
self.decimal_regex = re.compile(r"(-?\d*)\.(\d+)(.*)")
|
10 |
+
self.number_regex = re.compile(r"(-?\d+)(.*)")
|
11 |
+
self.filter_regex = re.compile(r"[,]")
|
12 |
+
self.cardinal = CardinalVietnamese()
|
13 |
+
self.digit = DigitVietnamese()
|
14 |
+
self.suffixes = [
|
15 |
+
"nghìn",
|
16 |
+
"triệu",
|
17 |
+
"tỷ",
|
18 |
+
"nghìn tỷ",
|
19 |
+
"triệu tỷ",
|
20 |
+
"tỷ tỷ"
|
21 |
+
]
|
22 |
+
self.suffix_regex = re.compile(f" *({'|'.join(self.suffixes)})")
|
23 |
+
self.e_suffix_regex = re.compile(r" *E(-?\d+)")
|
24 |
+
|
25 |
+
def convert(self, token: str) -> str:
|
26 |
+
token = self.filter_regex.sub("", token)
|
27 |
+
|
28 |
+
number = ""
|
29 |
+
decimal = ""
|
30 |
+
|
31 |
+
match = self.decimal_regex.match(token)
|
32 |
+
if match:
|
33 |
+
number = match.group(1)
|
34 |
+
decimal = match.group(2)
|
35 |
+
token = match.group(3)
|
36 |
+
else:
|
37 |
+
match = self.number_regex.match(token)
|
38 |
+
if match:
|
39 |
+
number = match.group(1)
|
40 |
+
token = match.group(2)
|
41 |
+
|
42 |
+
match = self.suffix_regex.match(token)
|
43 |
+
suffix = ""
|
44 |
+
if match:
|
45 |
+
suffix = match.group(1)
|
46 |
+
else:
|
47 |
+
match = self.e_suffix_regex.match(token)
|
48 |
+
if match:
|
49 |
+
suffix = f"nhân mười mũ {self.cardinal.convert(match.group(1))}"
|
50 |
+
|
51 |
+
result_list = []
|
52 |
+
if len(decimal) > 0:
|
53 |
+
result_list.append("phẩy")
|
54 |
+
result_list.append(self.digit.convert(decimal))
|
55 |
+
|
56 |
+
if number:
|
57 |
+
result_list.insert(0, self.cardinal.convert(number))
|
58 |
+
|
59 |
+
if suffix:
|
60 |
+
result_list.append(suffix)
|
61 |
+
|
62 |
+
result = " ".join(result_list)
|
63 |
+
|
64 |
+
return result
|
65 |
+
|
66 |
+
if __name__ == "__main__":
|
67 |
+
decimal = Decimal()
|
68 |
+
|
69 |
+
test_cases = [
|
70 |
+
"123,456.789",
|
71 |
+
"0.5",
|
72 |
+
"1000000",
|
73 |
+
"3.14",
|
74 |
+
"2.718E3",
|
75 |
+
"1.23E-5",
|
76 |
+
"9.99999",
|
77 |
+
"1234.5678 tỷ",
|
78 |
+
"0.000001",
|
79 |
+
"1000000.000001",
|
80 |
+
]
|
81 |
+
|
82 |
+
for case in test_cases:
|
83 |
+
result = decimal.convert(case)
|
84 |
+
print(f"Số: {case}")
|
85 |
+
print(f"Chuyển đổi: {result}")
|
86 |
+
print()
|
converters/Digit.py
ADDED
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from singleton_decorator import singleton
|
2 |
+
import re
|
3 |
+
|
4 |
+
@singleton
|
5 |
+
class DigitVietnamese:
|
6 |
+
"""
|
7 |
+
Các bước:
|
8 |
+
- 1 Lọc bỏ tất cả các ký tự không phải số
|
9 |
+
- 2 Kiểm tra trường hợp đặc biệt
|
10 |
+
- 3 Chuyển đổi mỗi chữ số thành văn bản tiếng Việt
|
11 |
+
- 4 Thêm khoảng trắng giữa các từ
|
12 |
+
Trường hợp đặc biệt:
|
13 |
+
007 -> không không bảy
|
14 |
+
trong khi 003 -> không không ba
|
15 |
+
"""
|
16 |
+
def __init__(self):
|
17 |
+
super().__init__()
|
18 |
+
# Regex để lọc bỏ các ký tự không phải số
|
19 |
+
self.filter_regex = re.compile("[^0-9]")
|
20 |
+
# Từ điển chuyển đổi chữ số sang chữ tiếng Việt
|
21 |
+
self.trans_dict = {
|
22 |
+
"0": "không",
|
23 |
+
"1": "một",
|
24 |
+
"2": "hai",
|
25 |
+
"3": "ba",
|
26 |
+
"4": "bốn",
|
27 |
+
"5": "năm",
|
28 |
+
"6": "sáu",
|
29 |
+
"7": "bảy",
|
30 |
+
"8": "tám",
|
31 |
+
"9": "chín"
|
32 |
+
}
|
33 |
+
|
34 |
+
def convert(self, token: str) -> str:
|
35 |
+
# 1 Lọc bỏ tất cả các ký tự không phải số
|
36 |
+
token = self.filter_regex.sub("", token)
|
37 |
+
# 2 Kiểm tra trường hợp đặc biệt
|
38 |
+
if token == "007":
|
39 |
+
return "không không bảy"
|
40 |
+
# 3 & 4 Chuyển đổi mỗi chữ số thành văn bản và thêm khoảng trắng
|
41 |
+
token = " ".join([self.trans_dict[c] for c in token])
|
42 |
+
return token
|
43 |
+
if __name__ == "__main__":
|
44 |
+
# Tạo một thể hiện của lớp DigitVietnamese
|
45 |
+
digit_converter = DigitVietnamese()
|
46 |
+
|
47 |
+
# Ví dụ 1: Số điện thoại
|
48 |
+
phone_number = "0912345678"
|
49 |
+
print(f"Số điện thoại: {phone_number}")
|
50 |
+
print(f"Chuyển đổi: {digit_converter.convert(phone_number)}")
|
51 |
+
print()
|
52 |
+
|
53 |
+
# Ví dụ 2: Mã số sinh viên
|
54 |
+
student_id = "SV20210001"
|
55 |
+
print(f"Mã số sinh viên: {student_id}")
|
56 |
+
print(f"Chuyển đổi: {digit_converter.convert(student_id)}")
|
57 |
+
print()
|
58 |
+
|
59 |
+
# Ví dụ 3: Trường hợp đặc biệt
|
60 |
+
special_case = "007"
|
61 |
+
print(f"Số đặc biệt: {special_case}")
|
62 |
+
print(f"Chuyển đổi: {digit_converter.convert(special_case)}")
|
converters/Fraction.py
ADDED
@@ -0,0 +1,110 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from .Cardinal import CardinalVietnamese
|
2 |
+
import re
|
3 |
+
from singleton_decorator import singleton
|
4 |
+
|
5 |
+
@singleton
|
6 |
+
class Fraction:
|
7 |
+
def __init__(self):
|
8 |
+
super().__init__()
|
9 |
+
self.filter_regex = re.compile(",")
|
10 |
+
self.space_filter_regex = re.compile(" ")
|
11 |
+
self.trans_dict = {
|
12 |
+
"½": {"prepended": "một", "single": "một", "text": "phần hai"},
|
13 |
+
"⅓": {"prepended": "một", "single": "một", "text": "phần ba"},
|
14 |
+
"⅔": {"prepended": "hai", "single": "hai", "text": "phần ba"},
|
15 |
+
"¼": {"prepended": "một", "single": "một", "text": "phần tư"},
|
16 |
+
"¾": {"prepended": "ba", "single": "ba", "text": "phần tư"},
|
17 |
+
"⅕": {"prepended": "một", "single": "một", "text": "phần năm"},
|
18 |
+
"⅖": {"prepended": "hai", "single": "hai", "text": "phần năm"},
|
19 |
+
"⅗": {"prepended": "ba", "single": "ba", "text": "phần năm"},
|
20 |
+
"⅘": {"prepended": "bốn", "single": "bốn", "text": "phần năm"},
|
21 |
+
"⅙": {"prepended": "một", "single": "một", "text": "phần sáu"},
|
22 |
+
"⅚": {"prepended": "năm", "single": "năm", "text": "phần sáu"},
|
23 |
+
"⅐": {"prepended": "một", "single": "một", "text": "phần bảy"},
|
24 |
+
"⅛": {"prepended": "một", "single": "một", "text": "phần tám"},
|
25 |
+
"⅜": {"prepended": "ba", "single": "ba", "text": "phần tám"},
|
26 |
+
"⅝": {"prepended": "năm", "single": "năm", "text": "phần tám"},
|
27 |
+
"⅞": {"prepended": "bảy", "single": "bảy", "text": "phần tám"},
|
28 |
+
"⅑": {"prepended": "một", "single": "một", "text": "phần chín"},
|
29 |
+
"⅒": {"prepended": "một", "single": "một", "text": "phần mười"}
|
30 |
+
}
|
31 |
+
self.special_regex = re.compile(f"({'|'.join(self.trans_dict.keys())})")
|
32 |
+
self.cardinal = CardinalVietnamese()
|
33 |
+
self.slash_regex = re.compile(r"(-?\d{1,3}( \d{3})+|-?\d+) *\/ *(-?\d{1,3}( \d{3})+|-?\d+)")
|
34 |
+
|
35 |
+
# Không cần chuyển đổi từ Cardinal sang Ordinal trong tiếng Việt
|
36 |
+
self.trans_denominator = {}
|
37 |
+
|
38 |
+
self.edge_dict = {
|
39 |
+
"1": {"singular": "trên một", "plural": "trên một"},
|
40 |
+
"2": {"singular": "phần hai", "plural": "phần hai"},
|
41 |
+
"4": {"singular": "phần tư", "plural": "phần tư"}
|
42 |
+
}
|
43 |
+
|
44 |
+
def convert(self, token: str) -> str:
|
45 |
+
token = self.filter_regex.sub("", token)
|
46 |
+
match = self.special_regex.search(token)
|
47 |
+
if match:
|
48 |
+
frac = match.group(1)
|
49 |
+
frac_dict = self.trans_dict[frac]
|
50 |
+
|
51 |
+
remainder = self.special_regex.sub("", token)
|
52 |
+
if remainder:
|
53 |
+
prefix = self.cardinal.convert(remainder)
|
54 |
+
result = f"{prefix} và {frac_dict['prepended']} {frac_dict['text']}"
|
55 |
+
else:
|
56 |
+
result = f"{frac_dict['single']} {frac_dict['text']}"
|
57 |
+
|
58 |
+
else:
|
59 |
+
match = self.slash_regex.search(token)
|
60 |
+
if match:
|
61 |
+
numerator = match.group(1)
|
62 |
+
denominator = match.group(3)
|
63 |
+
|
64 |
+
numerator = self.space_filter_regex.sub("", numerator)
|
65 |
+
denominator = self.space_filter_regex.sub("", denominator)
|
66 |
+
|
67 |
+
numerator_text = self.cardinal.convert(numerator)
|
68 |
+
|
69 |
+
if denominator in self.edge_dict:
|
70 |
+
result = f"{numerator_text} {self.edge_dict[denominator]['singular']}"
|
71 |
+
|
72 |
+
else:
|
73 |
+
denominator_text = self.cardinal.convert(denominator)
|
74 |
+
result = f"{numerator_text} phần {denominator_text}"
|
75 |
+
|
76 |
+
remainder = self.slash_regex.sub("", token)
|
77 |
+
if remainder:
|
78 |
+
remainder_text = self.cardinal.convert(remainder)
|
79 |
+
result = f"{remainder_text} và {result}"
|
80 |
+
|
81 |
+
else:
|
82 |
+
result = token
|
83 |
+
|
84 |
+
return result
|
85 |
+
|
86 |
+
if __name__ == "__main__":
|
87 |
+
fraction = Fraction()
|
88 |
+
|
89 |
+
test_cases = [
|
90 |
+
"½",
|
91 |
+
"1½",
|
92 |
+
"2¼",
|
93 |
+
"3⅔",
|
94 |
+
"1/4",
|
95 |
+
"3/4",
|
96 |
+
"5/8",
|
97 |
+
"10/3",
|
98 |
+
"100/25",
|
99 |
+
"1000/999",
|
100 |
+
"2 1/2",
|
101 |
+
"3 3/4",
|
102 |
+
"5 2/3",
|
103 |
+
"100 1/100",
|
104 |
+
]
|
105 |
+
|
106 |
+
for case in test_cases:
|
107 |
+
result = fraction.convert(case)
|
108 |
+
print(f"Phân số: {case}")
|
109 |
+
print(f"Chuyển đổi: {result}")
|
110 |
+
print()
|
converters/Meansure.py
ADDED
@@ -0,0 +1,473 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from singleton_decorator import singleton
|
2 |
+
|
3 |
+
import re
|
4 |
+
|
5 |
+
from .Decimal import Decimal
|
6 |
+
from .Fraction import Fraction
|
7 |
+
|
8 |
+
|
9 |
+
@singleton
|
10 |
+
class Measure:
|
11 |
+
def __init__(self):
|
12 |
+
super().__init__()
|
13 |
+
# Regex để phát hiện phân số
|
14 |
+
self.fraction_regex = re.compile(
|
15 |
+
r"(((?:-?\d* )?-?\d+ *\/ *-? *\d+)|(-?\d* *(?:½|⅓|⅔|¼|¾|⅕|⅖|⅗|⅘|⅙|⅚|⅐|⅛|⅜|⅝|⅞|⅑|⅒)))")
|
16 |
+
# Regex để phát hiện xem có nên sử dụng "của một" hay không
|
17 |
+
self.of_a_regex = re.compile(r"(-?\d+ -?\d+ *\/ *-? *\d+)|(-?\d+ *(?:½|⅓|⅔|¼|¾|⅕|⅖|⅗|⅘|⅙|⅚|⅐|⅛|⅜|⅝|⅞|⅑|⅒))")
|
18 |
+
# Regex để phát hiện đầu vào để chuyển đổi số, bao gồm cả hậu tố triệu/tỷ tiềm năng
|
19 |
+
self.value_regex = re.compile(r"(-?(?: |\d)*\.?\d+ *(?:nghìn|triệu|tỷ|nghìn tỷ|triệu tỷ|tỷ tỷ)?)")
|
20 |
+
# Regex để lọc bỏ dấu phẩy
|
21 |
+
self.filter_regex = re.compile(r"[,]")
|
22 |
+
# Regex để lọc bỏ khoảng trắng
|
23 |
+
self.filter_space_regex = re.compile(r"[ ]")
|
24 |
+
# Regex để lọc bỏ chữ cái
|
25 |
+
self.letter_filter_regex = re.compile(r"[^0-9\-\.]")
|
26 |
+
|
27 |
+
# Từ điển tiền tố cho 10^i với i > 0
|
28 |
+
self.prefix_dict = {
|
29 |
+
"Y": "yotta",
|
30 |
+
"Z": "zetta",
|
31 |
+
"E": "exa",
|
32 |
+
"P": "peta",
|
33 |
+
"T": "tera",
|
34 |
+
"G": "giga",
|
35 |
+
"M": "mega",
|
36 |
+
"k": "kilo",
|
37 |
+
"h": "hecto",
|
38 |
+
"da": "deca",
|
39 |
+
"d": "deci",
|
40 |
+
"c": "centi",
|
41 |
+
"m": "milli",
|
42 |
+
"μ": "micro",
|
43 |
+
"µ": "micro",
|
44 |
+
"n": "nano",
|
45 |
+
"p": "pico",
|
46 |
+
"f": "femto",
|
47 |
+
"a": "atto",
|
48 |
+
"z": "zepto",
|
49 |
+
"y": "yocto"
|
50 |
+
}
|
51 |
+
|
52 |
+
# Từ điển dịch cho các loại đơn vị có thể thêm tiền tố
|
53 |
+
self.prefixable_trans_dict = {
|
54 |
+
"m": {
|
55 |
+
"singular": "mét",
|
56 |
+
"plural": "mét"
|
57 |
+
},
|
58 |
+
"b": {
|
59 |
+
"singular": "bit",
|
60 |
+
"plural": "bit"
|
61 |
+
},
|
62 |
+
"B": {
|
63 |
+
"singular": "byte",
|
64 |
+
"plural": "byte"
|
65 |
+
},
|
66 |
+
"bps": {
|
67 |
+
"singular": "bit trên giây",
|
68 |
+
"plural": "bit trên giây"
|
69 |
+
},
|
70 |
+
"Bps": {
|
71 |
+
"singular": "byte trên giây",
|
72 |
+
"plural": "byte trên giây"
|
73 |
+
},
|
74 |
+
"g": {
|
75 |
+
"singular": "gram",
|
76 |
+
"plural": "gram"
|
77 |
+
},
|
78 |
+
"gf": {
|
79 |
+
"singular": "gram lực",
|
80 |
+
"plural": "gram lực"
|
81 |
+
},
|
82 |
+
"W": {
|
83 |
+
"singular": "oát",
|
84 |
+
"plural": "oát"
|
85 |
+
},
|
86 |
+
"Wh": {
|
87 |
+
"singular": "oát giờ",
|
88 |
+
"plural": "oát giờ"
|
89 |
+
},
|
90 |
+
"Hz": {
|
91 |
+
"singular": "héc",
|
92 |
+
"plural": "héc"
|
93 |
+
},
|
94 |
+
"J": {
|
95 |
+
"singular": "jun",
|
96 |
+
"plural": "jun"
|
97 |
+
},
|
98 |
+
"L": {
|
99 |
+
"singular": "lít",
|
100 |
+
"plural": "lít"
|
101 |
+
},
|
102 |
+
"V": {
|
103 |
+
"singular": "vôn",
|
104 |
+
"plural": "vôn"
|
105 |
+
},
|
106 |
+
"f": {
|
107 |
+
"singular": "fara",
|
108 |
+
"plural": "fara"
|
109 |
+
},
|
110 |
+
"s": {
|
111 |
+
"singular": "giây",
|
112 |
+
"plural": "giây"
|
113 |
+
},
|
114 |
+
"A": {
|
115 |
+
"singular": "ampe",
|
116 |
+
"plural": "ampe"
|
117 |
+
},
|
118 |
+
"Ah": {
|
119 |
+
"singular": "ampe giờ",
|
120 |
+
"plural": "ampe giờ"
|
121 |
+
},
|
122 |
+
"Pa": {
|
123 |
+
"singular": "pascal",
|
124 |
+
"plural": "pascal"
|
125 |
+
},
|
126 |
+
"C": {
|
127 |
+
"singular": "culông",
|
128 |
+
"plural": "culông"
|
129 |
+
},
|
130 |
+
"Bq": {
|
131 |
+
"singular": "becquerel",
|
132 |
+
"plural": "becquerel"
|
133 |
+
},
|
134 |
+
"N": {
|
135 |
+
"singular": "niutơn",
|
136 |
+
"plural": "niutơn"
|
137 |
+
},
|
138 |
+
"bar": {
|
139 |
+
"singular": "bar",
|
140 |
+
"plural": "bar"
|
141 |
+
},
|
142 |
+
"lm": {
|
143 |
+
"singular": "lumen",
|
144 |
+
"plural": "lumen"
|
145 |
+
},
|
146 |
+
"cal": {
|
147 |
+
"singular": "calo",
|
148 |
+
"plural": "calo"
|
149 |
+
},
|
150 |
+
}
|
151 |
+
|
152 |
+
# Từ điển đã được biến đổi sử dụng self.prefixable_trans_dict và từ điển tiền tố
|
153 |
+
self.prefixed_dict = {
|
154 |
+
prefix + prefixed: {"singular": self.prefix_dict[prefix] + self.prefixable_trans_dict[prefixed]["singular"],
|
155 |
+
"plural": self.prefix_dict[prefix] + self.prefixable_trans_dict[prefixed]["plural"]} for
|
156 |
+
prefixed in self.prefixable_trans_dict for prefix in self.prefix_dict}
|
157 |
+
self.prefixed_dict = {**self.prefixed_dict, **self.prefixable_trans_dict}
|
158 |
+
|
159 |
+
# Từ điển dịch cho các loại đơn vị không có tiền tố
|
160 |
+
self.custom_dict = {
|
161 |
+
"%": {
|
162 |
+
"singular": "phần trăm",
|
163 |
+
"plural": "phần trăm"
|
164 |
+
},
|
165 |
+
"pc": {
|
166 |
+
"singular": "phần trăm",
|
167 |
+
"plural": "phần trăm"
|
168 |
+
},
|
169 |
+
"ft": {
|
170 |
+
"singular": "foot",
|
171 |
+
"plural": "foot"
|
172 |
+
},
|
173 |
+
"mi": {
|
174 |
+
"singular": "dặm",
|
175 |
+
"plural": "dặm"
|
176 |
+
},
|
177 |
+
"mb": {
|
178 |
+
"singular": "megabyte",
|
179 |
+
"plural": "megabyte"
|
180 |
+
},
|
181 |
+
"ha": {
|
182 |
+
"singular": "hecta",
|
183 |
+
"plural": "hecta"
|
184 |
+
},
|
185 |
+
"\"": {
|
186 |
+
"singular": "inch",
|
187 |
+
"plural": "inch"
|
188 |
+
},
|
189 |
+
"in": {
|
190 |
+
"singular": "inch",
|
191 |
+
"plural": "inch"
|
192 |
+
},
|
193 |
+
"\'": {
|
194 |
+
"singular": "foot",
|
195 |
+
"plural": "foot"
|
196 |
+
},
|
197 |
+
"rpm": {
|
198 |
+
"singular": "vòng trên phút",
|
199 |
+
"plural": "vòng trên phút"
|
200 |
+
},
|
201 |
+
"hp": {
|
202 |
+
"singular": "mã lực",
|
203 |
+
"plural": "mã lực"
|
204 |
+
},
|
205 |
+
"cc": {
|
206 |
+
"singular": "xăng-ti-mét khối",
|
207 |
+
"plural": "xăng-ti-mét khối"
|
208 |
+
},
|
209 |
+
"oz": {
|
210 |
+
"singular": "aoxơ",
|
211 |
+
"plural": "aoxơ",
|
212 |
+
},
|
213 |
+
"mph": {
|
214 |
+
"singular": "dặm trên giờ",
|
215 |
+
"plural": "dặm trên giờ"
|
216 |
+
},
|
217 |
+
"lb": {
|
218 |
+
"singular": "pao",
|
219 |
+
"plural": "pao"
|
220 |
+
},
|
221 |
+
"lbs": {
|
222 |
+
"singular": "pao",
|
223 |
+
"plural": "pao"
|
224 |
+
},
|
225 |
+
"kt": {
|
226 |
+
"singular": "nút",
|
227 |
+
"plural": "nút"
|
228 |
+
},
|
229 |
+
"dB": {
|
230 |
+
"singular": "đề-xi-ben",
|
231 |
+
"plural": "đề-xi-ben"
|
232 |
+
},
|
233 |
+
"AU": {
|
234 |
+
"singular": "đơn vị thiên văn",
|
235 |
+
"plural": "đơn vị thiên văn"
|
236 |
+
},
|
237 |
+
"st": {
|
238 |
+
"singular": "stone",
|
239 |
+
"plural": "stone"
|
240 |
+
},
|
241 |
+
"yd": {
|
242 |
+
"singular": "yard",
|
243 |
+
"plural": "yard"
|
244 |
+
},
|
245 |
+
"yr": {
|
246 |
+
"singular": "năm",
|
247 |
+
"plural": "năm"
|
248 |
+
},
|
249 |
+
"yrs": {
|
250 |
+
"singular": "năm",
|
251 |
+
"plural": "năm"
|
252 |
+
},
|
253 |
+
"eV": {
|
254 |
+
"singular": "electron vôn",
|
255 |
+
"plural": "electron vôn"
|
256 |
+
},
|
257 |
+
"/": {
|
258 |
+
"singular": "trên",
|
259 |
+
"plural": "trên"
|
260 |
+
},
|
261 |
+
"sq": {
|
262 |
+
"singular": "vuông",
|
263 |
+
"plural": "vuông"
|
264 |
+
},
|
265 |
+
"2": {
|
266 |
+
"singular": "vuông",
|
267 |
+
"plural": "vuông"
|
268 |
+
},
|
269 |
+
"²": {
|
270 |
+
"singular": "vuông",
|
271 |
+
"plural": "vuông"
|
272 |
+
},
|
273 |
+
"3": {
|
274 |
+
"singular": "khối",
|
275 |
+
"plural": "khối"
|
276 |
+
},
|
277 |
+
"³": {
|
278 |
+
"singular": "khối",
|
279 |
+
"plural": "khối"
|
280 |
+
},
|
281 |
+
"h": {
|
282 |
+
"singular": "giờ",
|
283 |
+
"plural": "giờ"
|
284 |
+
},
|
285 |
+
"hr": {
|
286 |
+
"singular": "giờ",
|
287 |
+
"plural": "giờ"
|
288 |
+
},
|
289 |
+
"hrs": {
|
290 |
+
"singular": "giờ",
|
291 |
+
"plural": "giờ"
|
292 |
+
},
|
293 |
+
"ch": {
|
294 |
+
"singular": "chain",
|
295 |
+
"plural": "chain"
|
296 |
+
},
|
297 |
+
"KiB": {
|
298 |
+
"singular": "kibibyte",
|
299 |
+
"plural": "kibibyte"
|
300 |
+
},
|
301 |
+
"MiB": {
|
302 |
+
"singular": "mebibyte",
|
303 |
+
"plural": "mebibyte"
|
304 |
+
},
|
305 |
+
"GiB": {
|
306 |
+
"singular": "gibibyte",
|
307 |
+
"plural": "gibibyte"
|
308 |
+
},
|
309 |
+
"pH": {
|
310 |
+
"singular": "pH",
|
311 |
+
"plural": "pH"
|
312 |
+
},
|
313 |
+
"kph": {
|
314 |
+
"singular": "kilômét trên giờ",
|
315 |
+
"plural": "kilômét trên giờ"
|
316 |
+
},
|
317 |
+
"Da": {
|
318 |
+
"singular": "đalton",
|
319 |
+
"plural": "đalton"
|
320 |
+
},
|
321 |
+
"cwt": {
|
322 |
+
"singular": "hundredweight",
|
323 |
+
"plural": "hundredweight"
|
324 |
+
},
|
325 |
+
"Sv": {
|
326 |
+
"singular": "sievert",
|
327 |
+
"plural": "sievert",
|
328 |
+
},
|
329 |
+
"C": {
|
330 |
+
"singular": "độ xen-xi-út",
|
331 |
+
"plural": "độ xen-xi-út"
|
332 |
+
},
|
333 |
+
"degrees": {
|
334 |
+
"singular": "độ",
|
335 |
+
"plural": "độ"
|
336 |
+
},
|
337 |
+
"degree": {
|
338 |
+
"singular": "độ",
|
339 |
+
"plural": "độ"
|
340 |
+
},
|
341 |
+
"atm": {
|
342 |
+
"singular": "át-mốt-phê",
|
343 |
+
"plural": "át-mốt-phê"
|
344 |
+
},
|
345 |
+
"min": {
|
346 |
+
"singular": "phút",
|
347 |
+
"plural": "phút"
|
348 |
+
},
|
349 |
+
"cd": {
|
350 |
+
"singular": "can-đê-la",
|
351 |
+
"plural": "can-đê-la"
|
352 |
+
},
|
353 |
+
"ly": {
|
354 |
+
"singular": "năm ánh sáng",
|
355 |
+
"plural": "năm ánh sáng"
|
356 |
+
},
|
357 |
+
"kts": {
|
358 |
+
"singular": "nút",
|
359 |
+
"plural": "nút"
|
360 |
+
},
|
361 |
+
"mol": {
|
362 |
+
"singular": "mol",
|
363 |
+
"plural": "mol"
|
364 |
+
},
|
365 |
+
"Nm": {
|
366 |
+
"singular": "niutơn mét",
|
367 |
+
"plural": "niutơn mét"
|
368 |
+
},
|
369 |
+
"Ω": {
|
370 |
+
"singular": "ôm",
|
371 |
+
"plural": "ôm"
|
372 |
+
},
|
373 |
+
"bbl": {
|
374 |
+
"singular": "thùng",
|
375 |
+
"plural": "thùng"
|
376 |
+
},
|
377 |
+
"gal": {
|
378 |
+
"singular": "gallon",
|
379 |
+
"plural": "gallon"
|
380 |
+
},
|
381 |
+
"cal": {
|
382 |
+
"singular": "cỡ nòng",
|
383 |
+
"plural": "cỡ nòng"
|
384 |
+
}
|
385 |
+
}
|
386 |
+
|
387 |
+
# Ghi đè và thêm giá trị từ custom_dict vào prefixed_dict
|
388 |
+
self.prefixed_dict = {**self.prefixed_dict, **self.custom_dict}
|
389 |
+
|
390 |
+
# Phiên bản viết thường của self.prefixed_dict
|
391 |
+
self.lower_prefixed_dict = {key.lower(): self.prefixed_dict[key] for key in self.prefixed_dict}
|
392 |
+
|
393 |
+
# Hậu tố đặc biệt mà tổng hậu tố nên được tách ra
|
394 |
+
self.special_suffixes = re.compile(r"(\/|trên(?!trăm)|vuông|2|²|3|³)")
|
395 |
+
|
396 |
+
# Chuyển đổi Decimal và Fraction
|
397 |
+
self.decimal = Decimal()
|
398 |
+
self.fraction = Fraction()
|
399 |
+
|
400 |
+
|
401 |
+
def convert(self, token: str) -> str:
|
402 |
+
# 1 Lọc bỏ dấu phẩy
|
403 |
+
token = self.filter_regex.sub("", token)
|
404 |
+
|
405 |
+
result_list = []
|
406 |
+
|
407 |
+
# Số nhiều mặc định là false, vì "/s" nên là "trên giây"
|
408 |
+
plural = False
|
409 |
+
|
410 |
+
# 2 Thử khớp với một phân số
|
411 |
+
match = self.fraction_regex.match(token)
|
412 |
+
if match:
|
413 |
+
# 2.1 Nếu tồn tại, chuyển đổi thành văn bản sử dụng bộ chuyển đổi Fraction
|
414 |
+
result_list.append(self.fraction.convert(match.group(0)))
|
415 |
+
# Chuyển token thành phần còn lại. Vì sử dụng match thay vì search,
|
416 |
+
# phần đầu của dòng tiếp theo có thể không cần thiết
|
417 |
+
token = token[:match.span()[0]] + token[match.span()[1]:]
|
418 |
+
|
419 |
+
# Lọc bỏ khoảng trắng
|
420 |
+
token = self.filter_space_regex.sub("", token)
|
421 |
+
|
422 |
+
# Nếu có một số trước phân số, ví dụ "8 1/2" hoặc "8 ½", thì chúng ta thêm "của một", và giữ nguyên số ít
|
423 |
+
# Ngược lại, chúng ta chuyển sang số nhiều
|
424 |
+
if self.of_a_regex.match(match.group(0)):
|
425 |
+
plural = True
|
426 |
+
else:
|
427 |
+
result_list.append("của một")
|
428 |
+
|
429 |
+
else:
|
430 |
+
# 3 Thử khớp với "x,y" hoặc "x"
|
431 |
+
match = self.value_regex.match(token)
|
432 |
+
if match:
|
433 |
+
# 3.1 Chuyển không có khoảng trắng cho bộ chuyển đổi decimal
|
434 |
+
result_list.append(self.decimal.convert(self.filter_space_regex.sub("", match.group(1))))
|
435 |
+
token = token[:match.span()[0]] + token[match.span()[1]:]
|
436 |
+
# Số nhiều là False khi giá trị tuyệt đối của số thập phân là 1, và giá trị thập phân không có dạng "1,x"
|
437 |
+
# Ngược lại là True
|
438 |
+
if abs(float(self.letter_filter_regex.sub("", match.group(1)))) != 1 or "," in match.group(1):
|
439 |
+
plural = True
|
440 |
+
|
441 |
+
# Biến chỉ ra liệu từ "trên" vừa được sử dụng
|
442 |
+
# Điều này được sử dụng để phát hiện số nhiều
|
443 |
+
per = False
|
444 |
+
# 4 Lặp qua phần còn lại của token
|
445 |
+
for split_token in token.split(" "):
|
446 |
+
for i, token in enumerate(self.split_token(split_token)):
|
447 |
+
# Thêm tên thích hợp của hậu tố nếu tồn tại
|
448 |
+
# Thử không phân biệt chữ hoa chữ thường nếu lần trước thất bại
|
449 |
+
if token in self.prefixed_dict:
|
450 |
+
result_list.append(self.prefixed_dict[token]["plural" if plural and not per else "singular"])
|
451 |
+
elif token.lower() in self.lower_prefixed_dict:
|
452 |
+
result_list.append(
|
453 |
+
self.lower_prefixed_dict[token.lower()]["plural" if plural and not per else "singular"])
|
454 |
+
else:
|
455 |
+
result_list.append(token)
|
456 |
+
|
457 |
+
# Nếu kết quả trước đó là "trên", đặt per thành True để sử dụng "singular" cho từ tiếp theo.
|
458 |
+
# Nhưng chỉ khi "trên" không phải là hậu tố đầu tiên. Ví dụ: "5/km2" là "năm trên kilômét vuông"
|
459 |
+
if result_list[-1] == "trên" and i != 0:
|
460 |
+
per = True
|
461 |
+
|
462 |
+
# Nếu từ cuối cùng không phải là "trên", và không phải "vuông" hoặc "khối", đặt lại per thành False.
|
463 |
+
# Nếu "trên" được sử dụng, tiếp theo là "vuông" hoặc "khối", chúng ta muốn giữ "singular" cho
|
464 |
+
# từ sắp tới
|
465 |
+
elif result_list[-1] not in ("vuông", "khối"):
|
466 |
+
per = False
|
467 |
+
|
468 |
+
result = " ".join(result_list)
|
469 |
+
|
470 |
+
# 5 Xử lý trường hợp đặc biệt: xentimét khối -> xăng-ti-mét khối
|
471 |
+
result = re.sub(r"xentimét khối", "xăng-ti-mét khối", result)
|
472 |
+
|
473 |
+
return result
|
converters/Money.py
ADDED
@@ -0,0 +1,155 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from singleton_decorator import singleton
|
2 |
+
import re, os
|
3 |
+
from .Cardinal import CardinalVietnamese
|
4 |
+
from .Digit import DigitVietnamese
|
5 |
+
|
6 |
+
|
7 |
+
@singleton
|
8 |
+
class Money:
|
9 |
+
def __init__(self):
|
10 |
+
super().__init__()
|
11 |
+
self.decimal_regex = re.compile(r"(.*?)(-?\d*)\.(\d+)(.*)")
|
12 |
+
self.number_regex = re.compile(r"(.*?)(-?\d+)(.*)")
|
13 |
+
self.filter_regex = re.compile(r"[, ]")
|
14 |
+
|
15 |
+
self.currencies = {
|
16 |
+
"đ": {
|
17 |
+
"number": {
|
18 |
+
"singular": "đồng",
|
19 |
+
"plural": "đồng"
|
20 |
+
},
|
21 |
+
"decimal": {
|
22 |
+
"singular": "xu",
|
23 |
+
"plural": "xu"
|
24 |
+
}
|
25 |
+
},
|
26 |
+
"vnd": {
|
27 |
+
"number": {
|
28 |
+
"singular": "đồng Việt Nam",
|
29 |
+
"plural": "đồng Việt Nam"
|
30 |
+
},
|
31 |
+
"decimal": {
|
32 |
+
"singular": "xu",
|
33 |
+
"plural": "xu"
|
34 |
+
}
|
35 |
+
}
|
36 |
+
}
|
37 |
+
|
38 |
+
self.suffixes = [
|
39 |
+
"nghìn",
|
40 |
+
"triệu",
|
41 |
+
"tỷ",
|
42 |
+
"nghìn tỷ",
|
43 |
+
"triệu tỷ",
|
44 |
+
"tỷ tỷ"
|
45 |
+
]
|
46 |
+
|
47 |
+
self.abbr_suffixes = {
|
48 |
+
"k": "nghìn",
|
49 |
+
"tr": "triệu",
|
50 |
+
"t": "tỷ"
|
51 |
+
}
|
52 |
+
|
53 |
+
self.suffix_regex = re.compile(
|
54 |
+
f"({'|'.join(sorted(self.suffixes + list(self.abbr_suffixes.keys()), key=len, reverse=True))})(.*)",
|
55 |
+
flags=re.I)
|
56 |
+
|
57 |
+
self.currency_regex = re.compile(r"(.*?)(đồng|vnd|đ)(.*?)", flags=re.I)
|
58 |
+
|
59 |
+
self.cardinal = CardinalVietnamese() # Giả sử đã có lớp Cardinal cho tiếng Việt
|
60 |
+
self.digit = DigitVietnamese() # Giả sử đã có lớp Digit cho tiếng Việt
|
61 |
+
|
62 |
+
def convert(self, token: str) -> str:
|
63 |
+
token = self.filter_regex.sub("", token)
|
64 |
+
|
65 |
+
before = ""
|
66 |
+
after = ""
|
67 |
+
currency = None
|
68 |
+
number = ""
|
69 |
+
decimal = ""
|
70 |
+
scale = ""
|
71 |
+
|
72 |
+
match = self.decimal_regex.search(token[::-1])
|
73 |
+
if match:
|
74 |
+
before = match.group(4)[::-1]
|
75 |
+
number = match.group(3)[::-1]
|
76 |
+
decimal = match.group(2)[::-1]
|
77 |
+
after = match.group(1)[::-1]
|
78 |
+
else:
|
79 |
+
match = self.number_regex.search(token)
|
80 |
+
if match:
|
81 |
+
before = match.group(1)
|
82 |
+
number = match.group(2)
|
83 |
+
after = match.group(3)
|
84 |
+
|
85 |
+
if before:
|
86 |
+
before = before.lower()
|
87 |
+
if before in self.currencies:
|
88 |
+
currency = self.currencies[before]
|
89 |
+
elif before[-1] in self.currencies:
|
90 |
+
currency = self.currencies[before[-1]]
|
91 |
+
|
92 |
+
if after:
|
93 |
+
match = self.suffix_regex.match(after)
|
94 |
+
if match:
|
95 |
+
scale = match.group(1).lower()
|
96 |
+
scale = self.abbr_suffixes[scale] if scale in self.abbr_suffixes else scale
|
97 |
+
after = match.group(2)
|
98 |
+
|
99 |
+
if after.lower() in self.currencies:
|
100 |
+
currency = self.currencies[after.lower()]
|
101 |
+
after = ""
|
102 |
+
|
103 |
+
decimal_support = currency and "number" in currency
|
104 |
+
|
105 |
+
result_list = []
|
106 |
+
if decimal_support and not scale:
|
107 |
+
if number and (number != "0" or not decimal):
|
108 |
+
result_list.append(self.cardinal.convert(number))
|
109 |
+
result_list.append(currency["number"]["singular"])
|
110 |
+
if decimal and decimal != "0" * len(decimal):
|
111 |
+
result_list.append("và")
|
112 |
+
if decimal and decimal != "0" * len(decimal):
|
113 |
+
decimal = f"{decimal:0<2}"
|
114 |
+
result_list.append(self.cardinal.convert(decimal))
|
115 |
+
result_list.append(currency["decimal"]["singular"])
|
116 |
+
else:
|
117 |
+
if number:
|
118 |
+
result_list.append(self.cardinal.convert(number))
|
119 |
+
if decimal and decimal != "0" * len(decimal):
|
120 |
+
result_list.append("phẩy")
|
121 |
+
result_list.append(self.digit.convert(decimal))
|
122 |
+
if scale:
|
123 |
+
result_list.append(scale)
|
124 |
+
if currency:
|
125 |
+
if decimal_support:
|
126 |
+
currency = currency["number"]
|
127 |
+
result_list.append(currency["singular"])
|
128 |
+
|
129 |
+
if after:
|
130 |
+
result_list.append(after.lower())
|
131 |
+
|
132 |
+
result = " ".join(result_list)
|
133 |
+
return result
|
134 |
+
|
135 |
+
|
136 |
+
if __name__ == "__main__":
|
137 |
+
money = Money()
|
138 |
+
|
139 |
+
# Ví dụ 1: Số tiền đơn giản
|
140 |
+
print(money.convert("15000đ")) # Kết quả mong đợi: mười lăm nghìn đồng
|
141 |
+
|
142 |
+
# Ví dụ 2: Số tiền có phần thập phân
|
143 |
+
print(money.convert("1500.50đ")) # Kết quả mong đợi: một nghìn năm trăm đồng và năm mươi xu
|
144 |
+
|
145 |
+
# Ví dụ 3: Số tiền lớn
|
146 |
+
print(money.convert("1000000000đ")) # Kết quả mong đợi: một tỷ đồng
|
147 |
+
|
148 |
+
# Ví dụ 4: Sử dụng VND
|
149 |
+
print(money.convert("5000000VND")) # Kết quả mong đợi: năm triệu đồng Việt Nam
|
150 |
+
|
151 |
+
# Ví dụ 5: Sử dụng hậu tố
|
152 |
+
print(money.convert("2tr đồng")) # Kết quả mong đợi: hai triệu đồng
|
153 |
+
|
154 |
+
# Ví dụ 6: Số tiền rất lớn
|
155 |
+
print(money.convert("1000000000000đ")) # Kết quả mong đợi: một nghìn tỷ đồng
|
converters/Ordinal.py
ADDED
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from singleton_decorator import singleton
|
2 |
+
import re
|
3 |
+
from Roman import RomanVietnamese
|
4 |
+
from Cardinal import CardinalVietnamese
|
5 |
+
|
6 |
+
|
7 |
+
@singleton
|
8 |
+
class OrdinalVietnamese:
|
9 |
+
"""
|
10 |
+
Các bước:
|
11 |
+
- 1 Lọc bỏ dấu phẩy và khoảng trắng
|
12 |
+
- 2 Kiểm tra số La Mã và chuyển đổi thành chuỗi số nguyên nếu có
|
13 |
+
- 3 Nếu là số La Mã, đặt tiền tố là "thứ"
|
14 |
+
- 4 Nếu không, kiểm tra xem có phải là số thứ tự tiếng Việt không (ví dụ: "thứ nhất", "thứ 2", "thứ ba")
|
15 |
+
- 5 Chuyển đổi chuỗi số còn lại thành Cardinal, và thêm "thứ" vào trước
|
16 |
+
- 6 Áp dụng các quy tắc đặc biệt cho số thứ tự tiếng Việt
|
17 |
+
"""
|
18 |
+
|
19 |
+
def __init__(self):
|
20 |
+
super().__init__()
|
21 |
+
self.filter_regex = re.compile(r"[, ]")
|
22 |
+
self.vietnamese_ordinal_regex = re.compile(r"(?i)(thứ\s*)?(\d+|nhất|nhì|hai|ba|tư|năm|sáu|bảy|tám|chín|mười)")
|
23 |
+
self.roman = RomanVietnamese()
|
24 |
+
self.cardinal = CardinalVietnamese()
|
25 |
+
|
26 |
+
self.special_cases = {
|
27 |
+
"nhất": "thứ nhất",
|
28 |
+
"nhì": "thứ nhì",
|
29 |
+
"hai": "thứ hai",
|
30 |
+
"ba": "thứ ba",
|
31 |
+
"tư": "thứ tư",
|
32 |
+
"năm": "thứ năm",
|
33 |
+
"sáu": "thứ sáu",
|
34 |
+
"bảy": "thứ bảy",
|
35 |
+
"tám": "thứ tám",
|
36 |
+
"chín": "thứ chín",
|
37 |
+
"mười": "thứ mười"
|
38 |
+
}
|
39 |
+
|
40 |
+
def convert(self, token: str) -> str:
|
41 |
+
token = self.filter_regex.sub("", token)
|
42 |
+
|
43 |
+
if self.roman.check_if_roman(token):
|
44 |
+
number, _ = self.roman.convert(token)
|
45 |
+
|
46 |
+
return f"thứ {number}"
|
47 |
+
|
48 |
+
match = self.vietnamese_ordinal_regex.fullmatch(token)
|
49 |
+
if match:
|
50 |
+
prefix = match.group(1) or ""
|
51 |
+
number = match.group(2)
|
52 |
+
|
53 |
+
if number.lower() in self.special_cases:
|
54 |
+
return self.special_cases[number.lower()]
|
55 |
+
|
56 |
+
if number.isdigit():
|
57 |
+
cardinal = self.cardinal.convert(number)
|
58 |
+
return f"thứ {cardinal}"
|
59 |
+
|
60 |
+
return f"{prefix}{number}"
|
61 |
+
|
62 |
+
return f"thứ {self.cardinal.convert(token)}"
|
63 |
+
|
64 |
+
|
65 |
+
if __name__ == "__main__":
|
66 |
+
ordinal_converter = OrdinalVietnamese()
|
67 |
+
|
68 |
+
examples = ["nhất", "thứ nhì", "thứ 3", "thứ tư", "thứ năm", "thứ 10", "21", "100", "1000", "II", "IV"]
|
69 |
+
|
70 |
+
for example in examples:
|
71 |
+
result = ordinal_converter.convert(example)
|
72 |
+
print(f"Số thứ tự: {example}")
|
73 |
+
print(f"Chuyển đổi: {result}")
|
74 |
+
print()
|
converters/Range.py
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from singleton_decorator import singleton
|
2 |
+
import re
|
3 |
+
from .Cardinal import CardinalVietnamese
|
4 |
+
|
5 |
+
|
6 |
+
@singleton
|
7 |
+
class Range:
|
8 |
+
"""
|
9 |
+
Steps:
|
10 |
+
- Check for - splitting numbers
|
11 |
+
|
12 |
+
Note:
|
13 |
+
Punctuation always stays the same
|
14 |
+
"""
|
15 |
+
|
16 |
+
def __init__(self):
|
17 |
+
super().__init__()
|
18 |
+
self.cardinal = CardinalVietnamese()
|
19 |
+
|
20 |
+
def convert(self, token: str) -> str:
|
21 |
+
numbers = re.split('-', token)
|
22 |
+
if len(numbers) == 1:
|
23 |
+
token = self.cardinal.convert(numbers[0])
|
24 |
+
elif len(numbers) == 2:
|
25 |
+
|
26 |
+
token = self.cardinal.convert(numbers[0])
|
27 |
+
token += ' đến '
|
28 |
+
token += self.cardinal.convert(numbers[1])
|
29 |
+
|
30 |
+
else:
|
31 |
+
token = ''
|
32 |
+
for number in numbers:
|
33 |
+
token += self.cardinal.convert(number)
|
34 |
+
token += ' '
|
35 |
+
|
36 |
+
return token
|
converters/Roman.py
ADDED
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from singleton_decorator import singleton
|
2 |
+
import re
|
3 |
+
|
4 |
+
|
5 |
+
@singleton
|
6 |
+
class RomanVietnamese:
|
7 |
+
"""
|
8 |
+
Các bước:
|
9 |
+
- 1 Lấy phần lớn nhất
|
10 |
+
- 2 Kiểm tra hậu tố 's'
|
11 |
+
- 3 Áp dụng lọc nghiêm ngặt
|
12 |
+
- 4 Tính tổng giá trị của chữ số La Mã bằng số nguyên
|
13 |
+
- 5 Trả về biểu diễn chuỗi của tổng, cùng với hậu tố
|
14 |
+
Trường hợp đặc biệt:
|
15 |
+
II I -> hai
|
16 |
+
IIs -> hai's
|
17 |
+
II. -> hai
|
18 |
+
"""
|
19 |
+
|
20 |
+
def __init__(self):
|
21 |
+
super().__init__()
|
22 |
+
# Regex để lọc bỏ các ký tự không phải chữ số La Mã
|
23 |
+
self.roman_filter_strict_regex = re.compile("[^IVXLCDM]")
|
24 |
+
# Regex để phát hiện chữ số La Mã
|
25 |
+
self.roman_filter_regex = re.compile(r"[.IVXLCDM]+(th|nd|st|rd|'s|s)?")
|
26 |
+
|
27 |
+
# Từ điển giá trị chữ số La Mã
|
28 |
+
self.roman_numerals = {
|
29 |
+
"I": 1,
|
30 |
+
"V": 5,
|
31 |
+
"X": 10,
|
32 |
+
"L": 50,
|
33 |
+
"C": 100,
|
34 |
+
"D": 500,
|
35 |
+
"M": 1000
|
36 |
+
}
|
37 |
+
|
38 |
+
|
39 |
+
|
40 |
+
def convert(self, token: str) -> (str, str):
|
41 |
+
# 1 Tách token thành các phần và làm việc với phần lớn nhất, trong trường hợp đầu vào là "I II"
|
42 |
+
token = max(token.split(" "), key=len)
|
43 |
+
# 2 Kiểm tra xem có cần sử dụng hậu tố "'s" không
|
44 |
+
suffix = ""
|
45 |
+
if token[-1:] == "s":
|
46 |
+
suffix = "'s"
|
47 |
+
|
48 |
+
# 3 Áp dụng lọc nghiêm ngặt để loại bỏ ".", "'" và "s"
|
49 |
+
token = self.roman_filter_strict_regex.sub("", token)
|
50 |
+
# 4 Chúng ta lặp qua token theo chiều ngược lại, liên tục cộng hoặc trừ giá trị được biểu diễn
|
51 |
+
# bởi ký tự, dựa trên các token trước đó.
|
52 |
+
total = 0
|
53 |
+
prev = 0
|
54 |
+
for c in reversed(token):
|
55 |
+
cur = self.roman_numerals[c]
|
56 |
+
total += cur if cur >= prev else -cur
|
57 |
+
prev = cur
|
58 |
+
|
59 |
+
return (str(total), suffix)
|
60 |
+
|
61 |
+
|
62 |
+
def check_if_roman(self, token: str) -> bool:
|
63 |
+
# Kiểm tra xem phần lớn nhất của token có được coi là chữ số La Mã hay không
|
64 |
+
return self.roman_filter_regex.fullmatch(max(token.split(" "), key=len)) != None
|
65 |
+
|
66 |
+
|
67 |
+
if __name__ == "__main__":
|
68 |
+
roman_converter = RomanVietnamese()
|
69 |
+
|
70 |
+
# Ví dụ 1
|
71 |
+
example1 = "XIV"
|
72 |
+
result1, suffix1 = roman_converter.convert(example1)
|
73 |
+
print(f"Chữ số La Mã: {example1}")
|
74 |
+
print(f"Chuyển đổi: {result1}{suffix1}")
|
75 |
+
print()
|
76 |
+
|
77 |
+
# Ví dụ 2
|
78 |
+
example2 = "MCMLIV"
|
79 |
+
result2, suffix2 = roman_converter.convert(example2)
|
80 |
+
print(f"Chữ số La Mã: {example2}")
|
81 |
+
print(f"Chuyển đổi: {result2}{suffix2}")
|
82 |
+
print()
|
83 |
+
|
84 |
+
# Ví dụ 3
|
85 |
+
example3 = "IIs"
|
86 |
+
result3, suffix3 = roman_converter.convert(example3)
|
87 |
+
print(f"Chữ số La Mã: {example3}")
|
88 |
+
print(f"Chuyển đổi: {result3}{suffix3}")
|
converters/Telephone.py
ADDED
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from singleton_decorator import singleton
|
2 |
+
import re
|
3 |
+
|
4 |
+
|
5 |
+
@singleton
|
6 |
+
class TelephoneVietnamese:
|
7 |
+
"""
|
8 |
+
Các bước:
|
9 |
+
- 1 Chuyển đổi thành chữ thường và thay thế dấu ngoặc đơn bằng dấu gạch ngang
|
10 |
+
- 2 Chuyển đổi từng ký tự trong token
|
11 |
+
- 3 Loại bỏ nhiều "ngat" liên tiếp. Đồng thời loại bỏ "ngat" ở đầu.
|
12 |
+
- 4 Thay thế các "khong" liên tiếp bằng "tram" hoặc "nghin" khi thích hợp
|
13 |
+
Lưu ý:
|
14 |
+
Số điện thoại chứa 0-9, "-", a-z, A-Z, " ", "(", ")"
|
15 |
+
"""
|
16 |
+
|
17 |
+
def __init__(self):
|
18 |
+
super().__init__()
|
19 |
+
# Từ điển dịch
|
20 |
+
self.tu_dien_dich = {
|
21 |
+
" ": "ngat",
|
22 |
+
"-": "ngat",
|
23 |
+
"x": "may_nhanh",
|
24 |
+
"0": "không",
|
25 |
+
"1": "môt",
|
26 |
+
"2": "hai",
|
27 |
+
"3": "ba",
|
28 |
+
"4": "bốn",
|
29 |
+
"5": "năm",
|
30 |
+
"6": "sáu",
|
31 |
+
"7": "bảy",
|
32 |
+
"8": "tám",
|
33 |
+
"9": "chín",
|
34 |
+
}
|
35 |
+
# Regex để lọc dấu ngoặc đơn
|
36 |
+
self.regex_loc = re.compile(r"[()]")
|
37 |
+
|
38 |
+
def convert(self, token: str) -> str:
|
39 |
+
# 1 Chuyển đổi thành chữ thường và thay thế dấu ngoặc đơn bằng dấu gạch ngang
|
40 |
+
token = self.regex_loc.sub("-", token.lower())
|
41 |
+
|
42 |
+
# 2 Chuyển đổi danh sách các ký tự
|
43 |
+
danh_sach_ket_qua = [self.tu_dien_dich[c] if c in self.tu_dien_dich else c for c in token]
|
44 |
+
|
45 |
+
# 3 Loại bỏ nhiều "ngat" liên tiếp. Đồng thời loại bỏ "ngat" ở đầu.
|
46 |
+
danh_sach_ket_qua = [phan for i, phan in enumerate(danh_sach_ket_qua) if
|
47 |
+
phan != "ngat" or (i - 1 >= 0 and danh_sach_ket_qua[i - 1] != "ngat")]
|
48 |
+
|
49 |
+
# 4 Lặp qua danh_sach_ket_qua và thay thế nhiều "khong" liên tiếp bằng "tram" hoặc "nghin",
|
50 |
+
# nhưng chỉ khi đứng trước là thứ khác ngoài "khong" hoặc "ngat", và đứng sau là "ngat" hoặc kết thúc danh sách.
|
51 |
+
i = 0
|
52 |
+
while i < len(danh_sach_ket_qua):
|
53 |
+
do_lech = 0
|
54 |
+
while i + do_lech < len(danh_sach_ket_qua) and danh_sach_ket_qua[i + do_lech] == "khong":
|
55 |
+
do_lech += 1
|
56 |
+
if (i + do_lech >= len(danh_sach_ket_qua) or danh_sach_ket_qua[i + do_lech] == "ngat") and (
|
57 |
+
i - 1 < 0 or danh_sach_ket_qua[i - 1] not in ("khong", "ngat")) and do_lech in (2, 3):
|
58 |
+
danh_sach_ket_qua[i: do_lech + i] = ["tram"] if do_lech == 2 else ["nghin"]
|
59 |
+
i += 1
|
60 |
+
|
61 |
+
return " ".join(danh_sach_ket_qua)
|
62 |
+
|
63 |
+
|
64 |
+
def main():
|
65 |
+
so_dien_thoai = TelephoneVietnamese()
|
66 |
+
|
67 |
+
# Ví dụ sử dụng
|
68 |
+
cac_vi_du = [
|
69 |
+
"0123-456-789",
|
70 |
+
"(098) 765-4321",
|
71 |
+
"0909 333 222",
|
72 |
+
"1800 1560",
|
73 |
+
"19001560",
|
74 |
+
"0336444027",
|
75 |
+
"+84-912345678",
|
76 |
+
]
|
77 |
+
|
78 |
+
print("Ví dụ chuyển đổi số điện thoại:")
|
79 |
+
for vi_du in cac_vi_du:
|
80 |
+
ket_qua = so_dien_thoai.convert(vi_du)
|
81 |
+
print(f"Gốc: {vi_du}")
|
82 |
+
print(f"Kết quả: {ket_qua}")
|
83 |
+
print()
|
84 |
+
|
85 |
+
|
86 |
+
if __name__ == "__main__":
|
87 |
+
# main()
|
88 |
+
x ="027321"
|
89 |
+
if x.startswith(("19", "18", "0")):
|
90 |
+
print(1)
|
converters/Time.py
ADDED
@@ -0,0 +1,100 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from singleton_decorator import singleton
|
2 |
+
import re
|
3 |
+
from .Cardinal import CardinalVietnamese
|
4 |
+
|
5 |
+
|
6 |
+
@singleton
|
7 |
+
class Time:
|
8 |
+
def __init__(self):
|
9 |
+
super().__init__()
|
10 |
+
|
11 |
+
self.filter_regex = re.compile(r"[. ]")
|
12 |
+
self.time_regex = re.compile(r"^(?P<hour>\d{1,2}) *((?::|.) *(?P<minute>\d{1,2}))? *(?P<suffix>[a-zA-Z\. ]*)$",
|
13 |
+
flags=re.I)
|
14 |
+
self.full_time_regex = re.compile(
|
15 |
+
r"^(?:(?P<hour>\d{1,2}) *:)? *(?P<minute>\d{1,2})(?: *: *(?P<seconds>\d{1,2})(?: *. *(?P<milliseconds>\d{1,3}))?)? *(?P<suffix>[a-zA-Z\. ]*)$",
|
16 |
+
flags=re.I)
|
17 |
+
self.ampm_time_regex = re.compile(r"^(?P<suffix>[a-zA-Z\. ]*)(?P<hour>\d{1,2})", flags=re.I)
|
18 |
+
|
19 |
+
self.cardinal = CardinalVietnamese()
|
20 |
+
|
21 |
+
def convert(self, token: str) -> str:
|
22 |
+
token = token.strip()
|
23 |
+
result_list = []
|
24 |
+
|
25 |
+
match = self.time_regex.match(token)
|
26 |
+
if match:
|
27 |
+
hour, minute, suffix = match.group("hour"), match.group("minute"), match.group("suffix")
|
28 |
+
|
29 |
+
result_list.append(self.cardinal.convert(hour))
|
30 |
+
result_list.append("giờ")
|
31 |
+
|
32 |
+
if minute and minute != "00":
|
33 |
+
result_list.append(self.cardinal.convert(minute))
|
34 |
+
result_list.append("phút")
|
35 |
+
|
36 |
+
if suffix:
|
37 |
+
suffix = self.filter_regex.sub("", suffix).lower()
|
38 |
+
if suffix == "sa":
|
39 |
+
result_list.append("sáng")
|
40 |
+
elif suffix == "ch":
|
41 |
+
result_list.append("chiều")
|
42 |
+
|
43 |
+
return " ".join(result_list)
|
44 |
+
|
45 |
+
match = self.full_time_regex.match(token)
|
46 |
+
if match:
|
47 |
+
hour, minute, seconds, milliseconds, suffix = match.group("hour"), match.group("minute"), match.group(
|
48 |
+
"seconds"), match.group("milliseconds"), match.group("suffix")
|
49 |
+
|
50 |
+
if hour:
|
51 |
+
result_list.append(self.cardinal.convert(hour))
|
52 |
+
result_list.append("giờ")
|
53 |
+
if minute:
|
54 |
+
result_list.append(self.cardinal.convert(minute))
|
55 |
+
result_list.append("phút")
|
56 |
+
if seconds:
|
57 |
+
result_list.append(self.cardinal.convert(seconds))
|
58 |
+
result_list.append("giây")
|
59 |
+
if milliseconds:
|
60 |
+
result_list.append(self.cardinal.convert(milliseconds))
|
61 |
+
result_list.append("phần nghìn giây")
|
62 |
+
|
63 |
+
if suffix:
|
64 |
+
suffix = self.filter_regex.sub("", suffix).lower()
|
65 |
+
if suffix == "sa":
|
66 |
+
result_list.append("sáng")
|
67 |
+
elif suffix == "ch":
|
68 |
+
result_list.append("chiều")
|
69 |
+
|
70 |
+
return " ".join(result_list)
|
71 |
+
|
72 |
+
match = self.ampm_time_regex.match(token)
|
73 |
+
if match:
|
74 |
+
hour, suffix = match.group("hour"), match.group("suffix")
|
75 |
+
|
76 |
+
result_list.append(self.cardinal.convert(hour))
|
77 |
+
result_list.append("giờ")
|
78 |
+
|
79 |
+
suffix = self.filter_regex.sub("", suffix).lower()
|
80 |
+
if suffix == "sa":
|
81 |
+
result_list.append("sáng")
|
82 |
+
elif suffix == "ch":
|
83 |
+
result_list.append("chiều")
|
84 |
+
|
85 |
+
return " ".join(result_list)
|
86 |
+
|
87 |
+
return token
|
88 |
+
|
89 |
+
|
90 |
+
if __name__ == "__main__":
|
91 |
+
time = Time()
|
92 |
+
|
93 |
+
print(time.convert("7:30")) # Kết quả mong đợi: bảy giờ ba mươi phút
|
94 |
+
print(time.convert("8:00 SA")) # Kết quả mong đợi: tám giờ sáng
|
95 |
+
print(time.convert("3:00 CH")) # Kết quả mong đợi: ba giờ chiều
|
96 |
+
print(time.convert(
|
97 |
+
"11:59:59.999")) # Kết quả mong đợi: mười một giờ năm mươi chín phút năm mươi chín giây chín trăm chín mươi chín phần nghìn giây
|
98 |
+
print(time.convert("SA7")) # Kết quả mong đợi: bảy giờ sáng
|
99 |
+
print(time.convert("CH5")) # Kết quả mong đợi: năm giờ chiều
|
100 |
+
print(time.convert("9:30:21")) # Kết quả mong đợi: năm giờ chiều
|
converters/__init__.py
ADDED
File without changes
|
converters/__pycache__/Cardinal.cpython-310.pyc
ADDED
Binary file (5.14 kB). View file
|
|
converters/__pycache__/Date.cpython-310.pyc
ADDED
Binary file (2.92 kB). View file
|
|
converters/__pycache__/Decimal.cpython-310.pyc
ADDED
Binary file (1.99 kB). View file
|
|
converters/__pycache__/Digit.cpython-310.pyc
ADDED
Binary file (1.97 kB). View file
|
|
converters/__pycache__/Fraction.cpython-310.pyc
ADDED
Binary file (2.77 kB). View file
|
|
converters/__pycache__/Meansure.cpython-310.pyc
ADDED
Binary file (6.16 kB). View file
|
|
converters/__pycache__/Money.cpython-310.pyc
ADDED
Binary file (2.86 kB). View file
|
|
converters/__pycache__/Range.cpython-310.pyc
ADDED
Binary file (1.12 kB). View file
|
|
converters/__pycache__/Roman.cpython-310.pyc
ADDED
Binary file (2.22 kB). View file
|
|
converters/__pycache__/Telephone.cpython-310.pyc
ADDED
Binary file (2.8 kB). View file
|
|
converters/__pycache__/Time.cpython-310.pyc
ADDED
Binary file (2.4 kB). View file
|
|
converters/__pycache__/__init__.cpython-310.pyc
ADDED
Binary file (141 Bytes). View file
|
|
download.sh
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env bash
|
2 |
+
|
3 |
+
if [ ! -f uvr5_weights/2_HP-UVR.pth ]; then
|
4 |
+
echo "Download the model weights"
|
5 |
+
wget -q -O uvr5_weights/2_HP-UVR.pth 2_HP-UVR.pth https://huggingface.co/fastrolling/uvr/resolve/main/Main_Models/2_HP-UVR.pth
|
6 |
+
fi
|
7 |
+
|
8 |
+
echo "The model weights have been downloaded"
|
train_list.txt
ADDED
The diff for this file is too large to render.
See raw diff
|
|
val_list.txt
ADDED
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
dataset/Xanh24h/speaker_0/3000.wav|tʃˈuə2ŋ tʃˈaː6j tʃˌɔ ɣˈaː2 kˈi2 lˈən bˈaːw ɣˈo2m kˌaːɜc xˈu vˈy6c sˈaw . |0
|
2 |
+
dataset/Xanh24h/speaker_0/3001.wav|ɗˈo2ŋ tˈəː2j , tʃˈuɜŋ kˈu5ŋ lˌaː2 tˈy6c fˈə4m xˌoŋ bˌi6 lˈə5n kˌaːɜc t0ˈaː6p tʃˈəɜt0 ɲˌy kˈim lwˈaː6j , twˈi4 t0ˈiɲ , kˌaːɜc vˈə6t0 kˈyɜŋ xˈaːɜc . |0
|
3 |
+
dataset/Xanh24h/speaker_0/3002.wav|ɗˈo2ŋ tˈəː2j , mˈuə sˈaɜm tʃˈy6c t0wˈiɛɜn kˌɔ2n lwˈaː6j bˈɔ4 ɲˈu kˈə2w zˈi tʃwˈiɛ4n ɗˌeɜn kˈyə4 hˈaː2ŋ vˈə6t0 lˈiɜ , zˈuɜp t0ˈiɛɜt0 kˈiɛ6m tˈəː2j zˈaːn vˌaː2 t0ˈiɛ2n bˈaː6c . |0
|
4 |
+
dataset/Xanh24h/speaker_0/3003.wav|kˈɔɜ hˈaːj mˈo hˈi2ɲ vˈyə2n ˈyəm ɗˌyə6c ɲˈiɛ2w ŋˈyə2j lˈyə6 tʃˈɔ6n xˌi xˈəː4j ŋˈiɛ6p lˌaː2 vˈyə2n ˈyəm kˈoɜ ɗˈi6ɲ vˌaː2 vˈyə2n ˈyəm t0ˈaː6m tˈəː2j . |0
|
5 |
+
dataset/Xanh24h/speaker_0/3004.wav|hˈɔ6k vˈiɛn kˈɔɜ tˈe4 hˈɔ6k mˌɔ6j lˌuɜc mˌɔ6j nˈəːj , xˌoŋ kˈə2n fˌaː4j ɗˌeɜn lˈəːɜp hˈɔ6k tʃˈy6c t0ˈiɛɜp . |0
|
6 |
+
dataset/Xanh24h/speaker_0/3005.wav|xˌi lˈyə6 tʃˈɔ6n mˈa6t0 hˈaː2ŋ kwˈaː4 bˈiɛɜw t0ˈeɜt0 , kˈə2n lˈiw ˈiɜ mˈo6t0 sˈoɜ vˈəɜn ɗˈe2 sˈaw . |0
|
7 |
+
dataset/Xanh24h/speaker_0/3006.wav|vˈəɪ6 nˌen , ɲˌy5ŋ ŋˈyə2j kˈɔɜ tˈu ɲˈə6p ˈəː4 mˈyɜc tʃˈuŋ bˈi2ɲ xˈaːɜ kˈu5ŋ sˈa5n sˈaː2ŋ bˈɔ4 zˈaː mˈo6t0 xwˈaː4n t0ˈiɛ2n ɗˌe4 tʃˈi tʃˈaː4 tʃˌɔ kˌaːɜc lwˈaː6j tˈy6c fˈə4m tʃˈyɜc nˈaŋ hˈo5 tʃˈəː6 sˈyɜc xwˈɛ4 tʃˌɔ kˈəː tˈe4 . |0
|
8 |
+
dataset/Xanh24h/speaker_0/3007.wav|tʃˈɔŋ tʃˈyə2ŋ hˈəː6p tʃˈɔ6n bˈe-ɜɲ zˈaːɜn mˈa6n , hˈa5j ɗˈaː4m bˈaː4w tʃwˈə4n bˌi6 nˈyəɜc tʃˈəɜm ŋˈɔn mˈiɛ6ŋ . |0
|
9 |
+
dataset/Xanh24h/speaker_0/3008.wav|mˈa6c zˌu2 t0ˈen ɣˈɔ6j kˌuə4 nˈɔɜ lˌaː2 bˈe-ɜɲ ɲˈaː5n , ɲˌyŋ tˈy6c t0ˈeɜ xˌoŋ sˈy4 zˈu6ŋ ɲˈaː5n t0ˈyəj lˈaː2m ŋwˈiɛn lˈiɛ6w tʃˈiɜɲ . |0
|
10 |
+
dataset/Xanh24h/speaker_0/3009.wav|zˈyəɜj zˈoɜŋ kˈə2n kˈɔɜ tˈən hˈi2ɲ kˈən ɗˈoɜj , lˈo2ŋ mˈyə6t0 , mˈaɜt0 sˈaːɜŋ , mˈu5j sˈe-6c , xˌoŋ bˌi6 tˈyəŋ t0ˈiɜc . |0
|
11 |
+
dataset/Xanh24h/speaker_0/3010.wav|bˈe-ɜɲ ŋˈɔ6t0 sˈyɜc xwˈɛ4 vˌaː2 tʃˈeɜ bˈiɛɜn lˈe-2ɲ mˈe-6ɲ . |0
|
12 |
+
dataset/Xanh24h/speaker_0/3011.wav|kˈiɲ zwˈe-ɲ kwˈə2n ˈaːɜw sˈɛkənd hˈand ɗˌaːŋ lˌaː2 mˈo6t0 sˈu hˈyəɜŋ ɗˌyə6c ɲˈiɛ2w ŋˈyə2j lˈyə6 tʃˈɔ6n bˌəː4j vˈoɜn ɗˈə2w t0ˈy bˈaːn ɗˈə2w xˌoŋ kwˈaːɜ lˈəːɜn , mˈa6t0 hˈaː2ŋ ɗˈaː zˈaː6ŋ vˌaː2 kˈɔɜ ɲˈiɛ2w t0ˈiɛ2m nˈaŋ fˈaːɜt0 tʃˈiɛ4n . |0
|
13 |
+
dataset/Xanh24h/speaker_0/3012.wav|ˈəː4 nˈyəɜc t0ˈaː tˌi2 kˈɔɜ zˈəɜt0 ɲˈiɛ2w zˈoɜŋ ɣˈaː2 ɲˌyŋ ɗˌe4 tʃˈan nˈuəj ɣˈaː2 tˈaː4 vˈyə2n tˈɛw hˈyəɜŋ ˈaːn t0wˈaː2n sˈiɲ hˈɔ6k bˈa2ŋ vˈiɛ6c nˈuəj ɣˈaː2 tˈaː4w zˈyə6c tˌi2 tʃˈuɜŋ t0ˈaː lˈyə6 tʃˈɔ6n zˈoɜŋ ɣˈaː2 zˈi . |0
|
14 |
+
dataset/Xanh24h/speaker_0/3013.wav|ŋwˈaː2j tʃˈi fˈiɜ twˈe mˈa6t0 bˈa2ŋ vˌaː2 ɲˈə6p hˈaː2ŋ , bˈaː6n kˈu5ŋ kˈə2n ɗˈə2w t0ˈy vˈaː2w kˌaːɜc tˈiɛɜt0 bˌi6 kˈə2n tˈiɛɜt0 , ɗˌe4 kˈiɲ zwˈe-ɲ kwˈə2n ˈaːɜw tʃˈɛ4 ˈɛm . |0
|
15 |
+
dataset/Xanh24h/speaker_0/3014.wav|ˈoŋ ɗwˈaː2n tˈe-ɲ t0ˈu2ŋ , ˈəɜp mˈyə2j t0ˈaːɜm , sˈaː5 fˈɔŋ tˈe-6ɲ ˈaː , tˈi6 sˈaː5 zˈaːɜ zˈaːj tʃˈiə sˈɛ4 , vˈaː2w xwˌaː4ŋ nˈam hˈaːj ŋˈi2n mˈyə2j bˈaː ɗˌeɜn nˈam hˈaːj ŋˈi2n mˈyə2j lˈam , tʃˈyə2ŋ ɗˈaː6j hˈɔ6k kˈə2n tˈəː kˈɔɜ hˈo5 tʃˈəː6 tˈy6c hˈiɛ6n zˈy6 ˈaːɜn mˈo hˈi2ɲ lˈuəɜ t0ˈom tʃˈɔŋ ˈo ɗˈe2 bˈaː2w xˈɛɜp kˈiɜn ˈəː4 tˈi6 sˈaː5 zˈaːɜ zˈaːj . |0
|
16 |
+
dataset/Xanh24h/speaker_0/3015.wav|ɗˌe4 tˈy6c hˈiɛ6n ɗˈiɛ2w nˈa2j , tʃˈu4 kˈyə4 hˈaː2ŋ nˌen ɗˈə2w t0ˈy vˈaː2w vˈiɛ6c t0ˈaː6w zˈaː mˈo6t0 tʃˈaːŋ wˈɛb tʃˌɔ kˈyə4 hˈaː2ŋ , tˈiɛɜt0 lˈə6p zˈaːn hˈaː2ŋ tʃˈen kˌaːɜc nˈe2n t0ˈaː4ŋ tˈyəŋ mˈaː6j ɗˈiɛ6n t0ˈy4 ɲˌy ʃˈəʊpiː , lazˈɑːdə , tˈɪk tˈɒk hwˌa6c sˈy4 zˈu6ŋ kˌaːɜc mˈaː6ŋ sˈaː5 hˈo6j ɲˌy fˈeɪsbʊk , ˈɪnstɐɡɹˌam , zˈɑːləʊ . |0
|
17 |
+
dataset/Xanh24h/speaker_0/3016.wav|kˈɔɜ tˈe4 bˈaː6n kˈə2n bˈaɜt0 tʃˈa6j lˈen ɲˈaː2 kˈiɜɲ zˈuɜp ŋˈan kˈɔ4 zˈaː6j tʃˌɔ kˈəɪ tʃˈo2ŋ tʃˈɔŋ ɲˈaː2 kˈiɜɲ hwˌa6c ɲˈaː2 zˈyə lˈyəɜj kˌuə4 bˈaː6n . |0
|
18 |
+
dataset/Xanh24h/speaker_0/3018.wav|nˈam hˈaːj ŋˈi2n mˈyə2j bˈoɜn , ˈe-ɲ kwˈiɛɜt0 t0ˈəm tˈɛw ɗˈuə4j nˈiɛ2m ɗˈaːm mˈe kˈiɲ zwˈe-ɲ tˈe4 tˈaːw . |0
|
19 |
+
dataset/Xanh24h/speaker_0/3019.wav|vˈəɪ6 kˌaːɜc bˈaː6n kˈɔɜ tˈaɜc mˈaɜc , tˈy6c sˈy6 ɲˈaː2 lˈe-5ɲ ɗˈaː6w hˈɔ6 kˈim zˈa2w kˈɔɜ t0ˌəːɜj mˈyɜc nˈaː2w ? |0
|
20 |
+
dataset/Xanh24h/speaker_0/3020.wav|tʃwˈiɛn zˈaː tʃˈuɜŋ t0ˈoj sˌɛ5 lˈiɛn hˈe6 vˌaː2 zˈaː4j ɗˈaːɜp tˈaɜc mˈaɜc . |0
|
21 |
+
dataset/Xanh24h/speaker_0/3022.wav|ŋwˈaː2j fˈaːɜt0 tʃˈiɛ4n kˈiɲ t0ˈeɜ , zˈaː ɗˈi2ɲ ˈoŋ fˈu2ŋ swˈən hwˈaː6t0 lˈuən ɣˈə2n mˈə5w tʃˈɔŋ vˈiɛ6c tˈy6c hˈiɛ6n tʃˈu4 tʃˈyəŋ kˌuə4 ɗˈaː4ŋ , tʃˈiɜɲ sˈe-ɜc , fˈaːɜp lwˈə6t0 kˌuə4 ɲˈaː2 nˈyəɜc vˌaː2 kˌaːɜc kwˈi ɗˈi6ɲ kˌuə4 ɗˈiə6 fˈyəŋ . |0
|
22 |
+
dataset/Xanh24h/speaker_0/3023.wav|t0wˈi ɲˈiɛn , tʃˈɔŋ tˈi6 tʃˈyə2ŋ kˈe-6ɲ tʃˈe-ɲ , ɗˌe4 tˈe-2ɲ kˈoŋ bˈaː6n kˈə2n fˌaː4j kˈɔɜ bˈiɜ kwˈiɛɜt0 kˈiɲ zwˈe-ɲ zˈiɛŋ kˌuə4 mˈi2ɲ . |0
|
23 |
+
dataset/Xanh24h/speaker_0/3024.wav|kˈo6ŋ vˌəːɜj t0ˈy zwˈi tʃˈiɛɜn lˈyə6c vˌaː2 sˈy6 tʃˈaː4j ŋˈiɛ6m , ˈe-ɲ ɗˌaː5 tˈe-2ɲ kˈoŋ vˌaː2 ɗˌyə6c ɲˈiɛ2w xˈe-ɜc hˈaː2ŋ ɗˈɔɜn ɲˈə6n , vˌaː2 kˈɔɜ ɗˌyə6c tˈe-2ɲ kˈoŋ sˈyɜŋ ɗˈaːɜŋ . |0
|
24 |
+
dataset/Xanh24h/speaker_0/3025.wav|tˈyɜ hˈaːj , ɲˈu kˈə2w t0ˈiɛw tˈu6 sˈiɛn kwˈɛ nˈyəɜŋ zˈəɜt0 lˈəːɜn , ɗˈa6c bˈiɛ6t0 lˌaː2 tʃˈɔŋ kˌaːɜc xˈu vˈy6c ɗˈoŋ zˈən kˈy ɣˈə2n tʃˈyə2ŋ hˈɔ6k , xˈu kˈoŋ ŋˈiɛ6p . |0
|
25 |
+
dataset/Xanh24h/speaker_0/3026.wav|bˈaː6n kˈɔɜ tˈe4 bˈaːɜn hwˈaː xˈo tʃˈy6c t0ˈiɛɜp tʃˌɔ xˈe-ɜc hˈaː2ŋ hwˌa6c bˈaːɜn bˈuən tʃˌɔ kˌaːɜc ɲˈaː2 bˈaːɜn lˈɛ4 . |0
|
26 |
+
dataset/Xanh24h/speaker_0/3027.wav|kˈɔɜ ɲˌy vˈəɪ6 tˌi2 tʃˈɛw tʃˈɛw mˈəːɜj hˈaː6n tʃˈeɜ ɗˌyə6c bˈe6ɲ t0ˈə6t0 vˌaː2 sˈiɲ tʃˈyə4ŋ xwˈɛ4 mˈe-6ɲ . |0
|
27 |
+
dataset/Xanh24h/speaker_0/3028.wav|nˌeɜw lˌaː2 zˈɔ2ŋ kˈaːɜ ɲˈə6p xˈə4w tˌi2 zˈaːɜ tˈe-2ɲ zˈəɜt0 ɗˈaɜt0 ɗˈɔ4 , xˌoŋ fˌaː4j ˈaːj kˈu5ŋ kˈɔɜ ɗˈiɛ2w kˈiɛ6n mˈuə . |0
|
28 |
+
dataset/Xanh24h/speaker_0/3029.wav|lˈaɜp ɗˈa6t0 hˈe6 tˈoɜŋ ɗˈiɛ6n , nˈyəɜc , tˈoɜŋ zˈɔɜ , twˈaːɜt0 nˈyəɜc . |0
|
29 |
+
dataset/Xanh24h/speaker_0/3032.wav|bˈaː6n kˈɔɜ tˈe4 kˈiɲ zwˈe-ɲ kˌaːɜc sˈaː4n fˈə4m mˈəːɜj zˈaː mˈaɜt0 , sˈaː4n fˈə4m lˈiɛn kwˈaːn ɗˌeɜn kˌaːɜc zˈy6 kˈiɛ6n nˈo4j bˈə6t0 . |0
|
30 |
+
dataset/Xanh24h/speaker_0/3033.wav|vˌəːɜj lˈyə6ŋ ŋˈyə2j zˈu2ŋ ɗˈoŋ ɗˈaː4w vˌaː2 ɗˈaː zˈaː6ŋ , tˈɪk tˈɒk lˌaː2 mˈo6t0 kˈeɲ t0ˈiɛɜp tˈi6 hˈiɛ6w kwˈaː4 ɗˌe4 t0ˈiɛɜp kˈə6n vˌəːɜj ɗˈoŋ ɗˈaː4w xˈe-ɜc hˈaː2ŋ t0ˈiɛ2m nˈaŋ . |0
|
31 |
+
dataset/Xanh24h/speaker_0/3035.wav|ɗˈaː2w tˈeɜ kˈɔɜ tˈe4 kˈɔɜ zˈaːɜŋ tʃˈy6c , zˈaːɜŋ hwˈe-2ɲ , zˈaːɜŋ t0ˈaːm ɗˈaː , zˈaːɜŋ hˈaː6c mˈaːj . |0
|
32 |
+
dataset/Xanh24h/speaker_0/3036.wav|tˈɛw t0ˈəː2 ʒˈɔŋˈaːn ˈɪnbəʊ kˌuə4 hˈaː2n kˈuəɜc tʃˌɔ bˈiɛɜt0 , tʃˈɔŋ sˈoɜ kˌaːɜc mˈa6t0 hˈaː2ŋ sˈaː sˈi4 mˌaː2 tʃˈiɛ2w t0ˈiɛn mˈuə , tˈiɛɜt0 bˌi6 ɗˈiɛ6n t0ˈy4 ɗˈyɜŋ ɗˈə2w vˌaː2 tʃˈiɛɜm t0ˌəːɜj bˈoɜn mˈyəj tʃˈiɜn fˈə2n tʃˈam lˈyə6ŋ t0ˈiɛw tˈu6 hˈaː2ŋ sˈaː sˈi4 kˌuə4 kˈim ʒˈɔŋˈun . |0
|
33 |
+
dataset/Xanh24h/speaker_0/3037.wav|tˈəː2j zˈaːn sˈaw ɗˈɔɜ , ˈe-ɲ lˈaː2m tˈem ɲˈiɛ2w ŋˈe2 xˈaːɜc ɲˈaw ɲˌy fˈu6c vˈu6 , lˈe5 t0ˈən , bˈoɜk vˈaːɜc ɗˌe4 lˈəɪɜ vˈoɜn mˈuə tˈem hˈaː2ŋ . |0
|
34 |
+
dataset/Xanh24h/speaker_0/3038.wav|tˈəː2j ɗˈiɛ4m tˈu hwˈe-6c lˌaː2 xˌi tʃˈaː6c tˈyɜ hˈaːj vˈyə2 ɲˈuɜ lˈen . |0
|
35 |
+
dataset/Xanh24h/speaker_0/3039.wav|bˈaː6n kˈɔɜ tˈe4 bˈaːɜn ɗˈo2 tʃˈaːŋ tʃˈiɜ t0ˈeɜt0 ŋwˈiɛn ɗˈaːɜn hˈandmeɪd tʃˈy6c t0ˈiɛɜp tʃˌɔ ŋˈyə2j t0ˈiɛw zˈu2ŋ hwˌa6c tˈoŋ kwˈaː kˌaːɜc ɲˈaː2 bˈaːɜn lˈɛ4 . |0
|
36 |
+
dataset/Xanh24h/speaker_0/3041.wav|tʃˈɔŋ t0ˈi2ɲ hˈi2ɲ tˈi6 tʃˈyə2ŋ hˈiɛ6n nˈaj , tˈy6c fˈə4m vˌə5n lˌaː2 ŋˈe-2ɲ hˈaː2ŋ ɗˈyɜŋ ɗˈə2w vˈe2 lˈəː6j ɲwˈə6n tʃˌɔ ɲˌy5ŋ ɲˈaː2 ɗˈə2w t0ˈy tˈoŋ tˈaːɜj . |0
|
37 |
+
dataset/Xanh24h/speaker_0/3042.wav|tˈyɜc ˈan kˌuə4 tʃˈuɜŋ bˈaːw ɣˈo2m tˈɔɜk vˌaː2 kˌaːɜc lwˈaː6j zˈaw . |0
|
38 |
+
dataset/Xanh24h/speaker_0/3043.wav|lwˈaː2j tʃˈɛw tʃˈɛw kˈɔɜ kˈiɜc tˈyəɜc xˈaːɜ xˈiɛm t0ˈoɜn , vˌəːɜj tʃˈiɛ2w zˈaː2j tˈən ɗˌeɜn ɗˈə2w lˌaː2 bˈoɜn mˈyəj hˈaːj ɗˌeɜn bˈoɜn mˈyəj t0ˈaːɜm sˈaŋt0ˈimˈɛɜt0 , tʃˈɔŋ xˌi tʃˈɔ6ŋ lˈyə6ŋ kˌuə4 tʃˈuɜŋ tʃˈi4 t0ˌy2 xˌoŋ fˈəɪ4 bˈa4j ɗˌeɜn hˈaːj kˈilˈoɣˈaːm , lˈoŋ tʃˈen tˈən kˈɔɜ mˈa2w nˈə1w vˌaː2 zˈyəɜj bˈu6ŋ kˈɔɜ mˈa2w tʃˈaɜŋ hwˌa6c vˈaː2ŋ ɲˈaː6t0 . |0
|
39 |
+
dataset/Xanh24h/speaker_0/3044.wav|kˌaːɜc bˈyəɜc tˈy6c hˈiɛ6n mˈo6t0 . ŋˈiɛn kˈiɜw tˈi6 tʃˈyə2ŋ . |0
|
40 |
+
dataset/Xanh24h/speaker_0/3046.wav|ɗˈiɛ2w kwˈaːn tʃˈɔ6ŋ lˌaː2 fˌaː4j tʃˈɔ6n kˈaːɜ kˈɔɜ tˈi6t0 sˈan tʃˈaɜc . |0
|
41 |
+
dataset/Xanh24h/speaker_0/3047.wav|kˌaːɜc mˈa6t0 hˈaː2ŋ ˈəː4 ɗˈəɪ ɗˌyə6c tʃˈɔ6n lˈɔ6k bˌəː4j ɲˌy5ŋ ŋˈyə2j kˈiɲ zwˈe-ɲ kˈɔɜ kˈiɲ ŋˈiɛ6m , ɗˈaː4m bˈaː4w tʃˈəɜt0 lˈyə6ŋ vˌaː2 ɗˈaː zˈaː6ŋ vˈe2 mˈə5w mˈaː5 . |0
|
42 |
+
dataset/Xanh24h/speaker_0/3048.wav|t0ˈiɛɜp tˈi6 kwˈaː ˈiːmeɪl lˌaː2 mˈo6t0 kˈe-ɜc hˈiɛ6w kwˈaː4 ɗˌe4 t0ˈiɛɜp kˈə6n xˈe-ɜc hˈaː2ŋ ɗˌaː5 ɗˈaŋ kˈiɜ ɲˈə6n tˈoŋ t0ˈin t0ˌy2 bˈaː6n . |0
|
43 |
+
dataset/Xanh24h/speaker_0/3049.wav|zˈaː ɗˈi2ɲ ˈe-ɲ ɗˌaː5 tˈiɛɜt0 lˈə6p mˈo6t0 tʃˈaːŋ tʃˈaː6j nˈuəj tˈɔ4 tˈyəŋ fˈə4m . |0
|
44 |
+
dataset/Xanh24h/speaker_0/3050.wav|vˈe2 sˈaw , mˈɔɜn ˈan ɗˌyə6c mˌɔ6j ŋˈyə2j xˈɛn ŋˈɔn , ɲˈiɛ2w ŋˈyə2j ɲˈə6n sˈɛɜt0 fˈu2 hˈəː6p vˌəːɜj sˈu hˈyəɜŋ tˈy6c fˈə4m zˈyə5 mˈuə2 zˈi6c . |0
|
45 |
+
dataset/Xanh24h/speaker_0/3051.wav|tˈiɛɜt0 kˈeɜ vˌaː2 tʃˈaːŋ tʃˈiɜ nˈo6j tˈəɜt0 tʃˌɔ kˈyə4 hˈaː2ŋ . |0
|
46 |
+
dataset/Xanh24h/speaker_0/3052.wav|hˈɔ6 ˈyə tʃˈuə6ŋ ɲˌy5ŋ lwˈaː6j hˈaː4j sˈaː4n nˈaː2w vˌaː2 mˈyɜc zˈaːɜ mˌaː2 hˈɔ6 sˈa5n lˈɔ2ŋ tʃˈi tʃˈaː4 lˌaː2 bˈaːw ɲˈiɛw . |0
|
47 |
+
dataset/Xanh24h/speaker_0/3053.wav|tˈi6t0 ɣˈaː2 kˈi2 lˈən kˈɔɜ zˈaːɜ tʃˈi6 kˈaːw , ɗˌyə6c ɲˈiɛ2w ŋˈyə2j ˈyə tʃˈuə6ŋ . |0
|
48 |
+
dataset/Xanh24h/speaker_0/3054.wav|vˌaː2 vˈu6 tˈu t0ˌy2 tˈaːɜŋ bˈa4j ɗˌeɜn tˈaːɜŋ tʃˈiɜn . |0
|
49 |
+
dataset/Xanh24h/speaker_0/3055.wav|ɗˈəɪ kˈu5ŋ lˌaː2 t0ˈiɛ2n ɗˈe2 tˈuɜc ɗˈəɪ4 ɲˈiɛ2w ŋˈyə2j tʃˈɔ6n , ɲˌy lˌaː2 mˈo6t0 kˈoŋ vˈiɛ6c bˈaːɜn hˈaː2ŋ ŋˈaɜn hˈaː6n ɗˌe4 kˈiɛɜm tˈem tˈu ɲˈə6p . |0
|
50 |
+
dataset/Xanh24h/speaker_0/3056.wav|bˈaː6n nˌen lˈyə6 tʃˈɔ6n ŋwˈiɛn lˈiɛ6w tʃˈəɜt0 lˈyə6ŋ , ɗˈaː zˈaː6ŋ , ɗˈaː4m bˈaː4w vˈe6 sˈiɲ ˈaːn t0wˈaː2n tˈy6c fˈə4m ɗˌe4 t0ˈaː6w zˈaː ɲˌy5ŋ sˈaː4n fˈə4m tˈəːm ŋˈɔn , hˈəɜp zˈə5n . |0
|
51 |
+
dataset/Xanh24h/speaker_0/3057.wav|bˈen kˈe-6ɲ ɗˈɔɜ , kˌɔ2n t0ˈi2m hˈiɛ4w tˈem ɲˌy5ŋ kˈiɛɜn tˈyɜc vˌaː2 kˈi5 twˈə6t0 tʃˈan nˈuəj tʃˈen sˈe-ɜc bˈaːɜw tˌevˈe . |0
|
52 |
+
dataset/Xanh24h/speaker_0/3058.wav|ɗˌeɜn vˌəːɜj vˈɪdɪəʊ nˈa2j , t0ˈaː2j tʃˈiɜɲ kˈiɲ zwˈe-ɲ sˈin ɣˈy4j ɗˌeɜn kˌaːɜc bˈaː6n kˈə1w tʃwˈiɛ6n vˈe2 tʃˈaː2ŋ tʃˈaːj bˈɔ4 ŋˈe2 kˈi5 sˈy kˈiɛɜm t0ˈiɛ2n t0ˈi4 mˌo5j nˈam ɲˈəː2 ŋˈe2 nˈuəj kˈaːɜ kˈe-4ɲ ˈiɜt0 vˈoɜn . |0
|
53 |
+
dataset/Xanh24h/speaker_0/3059.wav|ɲˈiɛ2w xˌi sˈyɜc xwˈɛ4 kˈiɛ6t0 kwˈe6 t0ˌəːɜj mˈyɜc ˈe-ɲ fˌaː4j ɲˈə6p vˈiɛ6n , ɗˈiɛ2w tʃˈi6 zˈɔ swˈi ɲˈyə6c , lˈaːw lˈy6c ɗˌeɜn ɗˈo6 tˈo4 hwˈiɛɜt0 . |0
|
54 |
+
dataset/Xanh24h/speaker_0/3060.wav|kˈiɲ zwˈe-ɲ ɗˈo2ŋ hˈo2 kˈu5 lˌaː2 mˈo hˈi2ɲ kˈiɲ zwˈe-ɲ ɗˌyə6c ɲˈiɛ2w ŋˈyə2j kwˈaːn t0ˈəm , ɗˈa6c bˈiɛ6t0 lˌaː2 ɲˌy5ŋ ŋˈyə2j ˈiɛw tˈiɜc sˈiw t0ˈə2m ɗˈo2ŋ hˈo2 . |0
|
55 |
+
dataset/Xanh24h/speaker_0/3061.wav|lˈyə6ŋ t0ˈom nˈuəj tʃˈɔŋ zˈuə6ŋ lˈuəɜ kˈə2n ɗˌyə6c kˈiɛ4m swˈaːɜt0 tʃˈa6t0 tʃˈɛ5 ɗˌe4 tʃˈe-ɜɲ t0ˈi2ɲ tʃˈaː6ŋ t0ˈom ˈan lˈuəɜ . |0
|
56 |
+
dataset/Xanh24h/speaker_0/3062.wav|zˈɔ ɗˈɔɜ , lˈəː6j ɲwˈə6n t0ˌy2 kˈiɲ zwˈe-ɲ kˈə1w ɗˈoɜj , tʃˈe-ɲ ˈe-4ɲ t0ˈeɜt0 tˈyə2ŋ zˈəɜt0 kˈaːw . |0
|
57 |
+
dataset/Xanh24h/speaker_0/3063.wav|zˈi6c vˈu6 nˈa2j ɗˈaːɜp ˈyɜŋ ɲˈu kˈə2w kˌuə4 kˌaːɜc zwˈe-ɲ ŋˈiɛ6p , hˈo6 kˈiɲ zwˈe-ɲ mˈuəɜn bˈa2j mˈəm kˈuɜŋ tˈə2n t0ˈaː2j tʃˈuɜ ɗˈaːɜw , tʃwˈə4n , fˈɔŋ t0ˈu6c mˌaː2 xˌoŋ kˈə2n t0ˈoɜn tˈəː2j zˈaːn , kˈoŋ sˈyɜc . |0
|
58 |
+
dataset/Xanh24h/speaker_0/3064.wav|ŋˈiɛn kˈiɜw tˈi6 tʃˈyə2ŋ ɗˌe4 t0ˈi2m hˈiɛ4w sˈu hˈyəɜŋ vˌaː2 ɲˈu kˈə2w kˌuə4 fˈu6 hwˈiɲ hˈiɛ6n nˈaj . |0
|
59 |
+
dataset/Xanh24h/speaker_0/3065.wav|vˌaː2 mˈo6t0 sˈoɜ xˌoŋ ŋˈaː6j tʃˈi t0ˈiɛ2n ɗˌe4 kˈɔɜ ɗˌyə6c ɗˈa6c kwˈiɛ2n nˈa2j . |0
|
60 |
+
dataset/Xanh24h/speaker_0/3066.wav|mˈo hˈi2ɲ nˈuəj ɣˈaː2 tˈaː4w zˈyə6c ɗˈaɜt0 ɣˈəɜp bˈaː ɣˈaː2 tˈyə2ŋ vˌə5n tʃˈaɜj hˈaː2ŋ . |0
|
61 |
+
dataset/Xanh24h/speaker_0/3067.wav|ɗˈoɜj vˌəːɜj ɲˌy5ŋ ŋˈyə2j tʃˈɔ6n mˈuə fˈu6 kˈiɛ6n tʃˌɔ tʃˈiɛɜc ɗˈiɛ6n twˈaː6j ˈiɛw kwˈiɜ kˌuə4 mˈi2ɲ , hˈɔ6 tˈyə2ŋ kˈɔɜ xˈaː4 nˈaŋ , lˈyə6 tʃˈɔ6n sˈaː4n fˈə4m tˈɛw kˌaːɜc t0ˈiɛw tʃˈiɜ nˈa2j . |0
|
62 |
+
dataset/Xanh24h/speaker_0/3068.wav|ɗˈiɛ2w nˈa2j sˌɛ5 t0ˈaː6w zˈaː mˈo6t0 ŋˈuə2n tˈu ɲˈə6p ˈo4n ɗˈi6ɲ vˌaː2 ɗˈaː zˈaː6ŋ tʃˌɔ kˈyə4 hˈaː2ŋ . |0
|
63 |
+
dataset/Xanh24h/speaker_0/3069.wav|nˈam hˈaːj ŋˈi2n xˌoŋ tʃˈam mˈyə2j t0ˈaːɜm , ˈe-ɲ kwˈiɛɜt0 ɗˈi6ɲ vˈaj vˈoɜn ŋˈən hˈaː2ŋ ɗˌe4 ɗˈə2w t0ˈy sˈəɪ zˈy6ŋ hˈe6 tˈoɜŋ bˈe4 nˈuəj t0ˈom hˈu2m bˈoŋ . |0
|
64 |
+
dataset/Xanh24h/speaker_0/3070.wav|xˌi tˈyəŋ hˈiɛ6w ɗˌaː5 kˈɔɜ tʃˈo5 ɗˈyɜŋ tʃˈen tˈi6 tʃˈyə2ŋ , tʃˈu4 ʃˈɒp kˈɔɜ tˈe4 bˈaɜt0 ɗˈə2w fˈaːɜt0 tʃˈiɛ4n tˈem ɗˈaː zˈaː6ŋ sˈaː4n fˈə4m . |0
|
65 |
+
dataset/Xanh24h/speaker_0/3071.wav|xˌi t0ˈyəɜj bˈa2ŋ t0ˈaj , hˈa5j t0ˈyəɜj vˈaː2w vˈu2ŋ zˈe5 ɗˌe4 nˈyəɜc t0ˌy2 t0ˌy2 ŋˈə2m sˈuəɜŋ ɗˈəɜt0 . |0
|
66 |
+
dataset/Xanh24h/speaker_0/3072.wav|kˈɔɜ tˈe4 nˈɔɜj , kˈiɲ zwˈe-ɲ zˈi6c vˈu6 vˈə6n t0ˈaː4j tʃˈɛ4 ˈɛm kˈɔɜ tˈi6 tʃˈyə2ŋ zˈo6ŋ ɲˌyŋ kˈu5ŋ kˈɔɜ zˈəɜt0 ɲˈiɛ2w kˈe-6ɲ tʃˈe-ɲ . |0
|
67 |
+
dataset/Xanh24h/speaker_0/3074.wav|tˈəː2j zˈaːn t0ˌy2 xˌoŋ ɗˌeɜn bˈaː mˈyəj lˈam ŋˈa2j . |0
|
68 |
+
dataset/Xanh24h/speaker_0/3075.wav|tʃˈuɜŋ zˈuɜp tʃˈɛ4 fˈaːɜt0 tʃˈiɛ4n vˈe2 mˈa6t0 tˈe4 tʃˈəɜt0 , tʃˈiɜ t0wˈe6 , kˈaː4m sˈuɜc vˌaː2 kˈi5 nˈaŋ sˈaː5 hˈo6j , zˈɔ ɗˈɔɜ ɗˈo2 tʃˈəːj tʃˈəː4 tˈe-2ɲ mˈo6t0 mˈa6t0 hˈaː2ŋ tˈiɛɜt0 ˈiɛɜw xˌoŋ tˈe4 tˈiɛɜw tʃˈɔŋ kˈuə6c sˈoɜŋ kˌuə4 tʃˈɛ4 ˈɛm . |0
|
whisperx/SubtitlesProcessor.py
ADDED
@@ -0,0 +1,227 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import math
|
2 |
+
from conjunctions import get_conjunctions, get_comma
|
3 |
+
from typing import TextIO
|
4 |
+
|
5 |
+
def normal_round(n):
|
6 |
+
if n - math.floor(n) < 0.5:
|
7 |
+
return math.floor(n)
|
8 |
+
return math.ceil(n)
|
9 |
+
|
10 |
+
|
11 |
+
def format_timestamp(seconds: float, is_vtt: bool = False):
|
12 |
+
|
13 |
+
assert seconds >= 0, "non-negative timestamp expected"
|
14 |
+
milliseconds = round(seconds * 1000.0)
|
15 |
+
|
16 |
+
hours = milliseconds // 3_600_000
|
17 |
+
milliseconds -= hours * 3_600_000
|
18 |
+
|
19 |
+
minutes = milliseconds // 60_000
|
20 |
+
milliseconds -= minutes * 60_000
|
21 |
+
|
22 |
+
seconds = milliseconds // 1_000
|
23 |
+
milliseconds -= seconds * 1_000
|
24 |
+
|
25 |
+
separator = '.' if is_vtt else ','
|
26 |
+
|
27 |
+
hours_marker = f"{hours:02d}:"
|
28 |
+
return (
|
29 |
+
f"{hours_marker}{minutes:02d}:{seconds:02d}{separator}{milliseconds:03d}"
|
30 |
+
)
|
31 |
+
|
32 |
+
|
33 |
+
|
34 |
+
class SubtitlesProcessor:
|
35 |
+
def __init__(self, segments, lang, max_line_length = 45, min_char_length_splitter = 30, is_vtt = False):
|
36 |
+
self.comma = get_comma(lang)
|
37 |
+
self.conjunctions = set(get_conjunctions(lang))
|
38 |
+
self.segments = segments
|
39 |
+
self.lang = lang
|
40 |
+
self.max_line_length = max_line_length
|
41 |
+
self.min_char_length_splitter = min_char_length_splitter
|
42 |
+
self.is_vtt = is_vtt
|
43 |
+
complex_script_languages = ['th', 'lo', 'my', 'km', 'am', 'ko', 'ja', 'zh', 'ti', 'ta', 'te', 'kn', 'ml', 'hi', 'ne', 'mr', 'ar', 'fa', 'ur', 'ka']
|
44 |
+
if self.lang in complex_script_languages:
|
45 |
+
self.max_line_length = 30
|
46 |
+
self.min_char_length_splitter = 20
|
47 |
+
|
48 |
+
def estimate_timestamp_for_word(self, words, i, next_segment_start_time=None):
|
49 |
+
k = 0.25
|
50 |
+
has_prev_end = i > 0 and 'end' in words[i - 1]
|
51 |
+
has_next_start = i < len(words) - 1 and 'start' in words[i + 1]
|
52 |
+
|
53 |
+
if has_prev_end:
|
54 |
+
words[i]['start'] = words[i - 1]['end']
|
55 |
+
if has_next_start:
|
56 |
+
words[i]['end'] = words[i + 1]['start']
|
57 |
+
else:
|
58 |
+
if next_segment_start_time:
|
59 |
+
words[i]['end'] = next_segment_start_time if next_segment_start_time - words[i - 1]['end'] <= 1 else next_segment_start_time - 0.5
|
60 |
+
else:
|
61 |
+
words[i]['end'] = words[i]['start'] + len(words[i]['word']) * k
|
62 |
+
|
63 |
+
elif has_next_start:
|
64 |
+
words[i]['start'] = words[i + 1]['start'] - len(words[i]['word']) * k
|
65 |
+
words[i]['end'] = words[i + 1]['start']
|
66 |
+
|
67 |
+
else:
|
68 |
+
if next_segment_start_time:
|
69 |
+
words[i]['start'] = next_segment_start_time - 1
|
70 |
+
words[i]['end'] = next_segment_start_time - 0.5
|
71 |
+
else:
|
72 |
+
words[i]['start'] = 0
|
73 |
+
words[i]['end'] = 0
|
74 |
+
|
75 |
+
|
76 |
+
|
77 |
+
def process_segments(self, advanced_splitting=True):
|
78 |
+
subtitles = []
|
79 |
+
for i, segment in enumerate(self.segments):
|
80 |
+
next_segment_start_time = self.segments[i + 1]['start'] if i + 1 < len(self.segments) else None
|
81 |
+
|
82 |
+
if advanced_splitting:
|
83 |
+
|
84 |
+
split_points = self.determine_advanced_split_points(segment, next_segment_start_time)
|
85 |
+
subtitles.extend(self.generate_subtitles_from_split_points(segment, split_points, next_segment_start_time))
|
86 |
+
else:
|
87 |
+
words = segment['words']
|
88 |
+
for i, word in enumerate(words):
|
89 |
+
if 'start' not in word or 'end' not in word:
|
90 |
+
self.estimate_timestamp_for_word(words, i, next_segment_start_time)
|
91 |
+
|
92 |
+
subtitles.append({
|
93 |
+
'start': segment['start'],
|
94 |
+
'end': segment['end'],
|
95 |
+
'text': segment['text']
|
96 |
+
})
|
97 |
+
|
98 |
+
return subtitles
|
99 |
+
|
100 |
+
def determine_advanced_split_points(self, segment, next_segment_start_time=None):
|
101 |
+
split_points = []
|
102 |
+
last_split_point = 0
|
103 |
+
char_count = 0
|
104 |
+
|
105 |
+
words = segment.get('words', segment['text'].split())
|
106 |
+
add_space = 0 if self.lang in ['zh', 'ja'] else 1
|
107 |
+
|
108 |
+
total_char_count = sum(len(word['word']) if isinstance(word, dict) else len(word) + add_space for word in words)
|
109 |
+
char_count_after = total_char_count
|
110 |
+
|
111 |
+
for i, word in enumerate(words):
|
112 |
+
word_text = word['word'] if isinstance(word, dict) else word
|
113 |
+
word_length = len(word_text) + add_space
|
114 |
+
char_count += word_length
|
115 |
+
char_count_after -= word_length
|
116 |
+
|
117 |
+
char_count_before = char_count - word_length
|
118 |
+
|
119 |
+
if isinstance(word, dict) and ('start' not in word or 'end' not in word):
|
120 |
+
self.estimate_timestamp_for_word(words, i, next_segment_start_time)
|
121 |
+
|
122 |
+
if char_count >= self.max_line_length:
|
123 |
+
midpoint = normal_round((last_split_point + i) / 2)
|
124 |
+
if char_count_before >= self.min_char_length_splitter:
|
125 |
+
split_points.append(midpoint)
|
126 |
+
last_split_point = midpoint + 1
|
127 |
+
char_count = sum(len(words[j]['word']) if isinstance(words[j], dict) else len(words[j]) + add_space for j in range(last_split_point, i + 1))
|
128 |
+
|
129 |
+
elif word_text.endswith(self.comma) and char_count_before >= self.min_char_length_splitter and char_count_after >= self.min_char_length_splitter:
|
130 |
+
split_points.append(i)
|
131 |
+
last_split_point = i + 1
|
132 |
+
char_count = 0
|
133 |
+
|
134 |
+
elif word_text.lower() in self.conjunctions and char_count_before >= self.min_char_length_splitter and char_count_after >= self.min_char_length_splitter:
|
135 |
+
split_points.append(i - 1)
|
136 |
+
last_split_point = i
|
137 |
+
char_count = word_length
|
138 |
+
|
139 |
+
return split_points
|
140 |
+
|
141 |
+
|
142 |
+
def generate_subtitles_from_split_points(self, segment, split_points, next_start_time=None):
|
143 |
+
subtitles = []
|
144 |
+
|
145 |
+
words = segment.get('words', segment['text'].split())
|
146 |
+
total_word_count = len(words)
|
147 |
+
total_time = segment['end'] - segment['start']
|
148 |
+
elapsed_time = segment['start']
|
149 |
+
prefix = ' ' if self.lang not in ['zh', 'ja'] else ''
|
150 |
+
start_idx = 0
|
151 |
+
for split_point in split_points:
|
152 |
+
|
153 |
+
fragment_words = words[start_idx:split_point + 1]
|
154 |
+
current_word_count = len(fragment_words)
|
155 |
+
|
156 |
+
|
157 |
+
if isinstance(fragment_words[0], dict):
|
158 |
+
start_time = fragment_words[0]['start']
|
159 |
+
end_time = fragment_words[-1]['end']
|
160 |
+
next_start_time_for_word = words[split_point + 1]['start'] if split_point + 1 < len(words) else None
|
161 |
+
if next_start_time_for_word and (next_start_time_for_word - end_time) <= 0.8:
|
162 |
+
end_time = next_start_time_for_word
|
163 |
+
else:
|
164 |
+
fragment = prefix.join(fragment_words).strip()
|
165 |
+
current_duration = (current_word_count / total_word_count) * total_time
|
166 |
+
start_time = elapsed_time
|
167 |
+
end_time = elapsed_time + current_duration
|
168 |
+
elapsed_time += current_duration
|
169 |
+
|
170 |
+
|
171 |
+
subtitles.append({
|
172 |
+
'start': start_time,
|
173 |
+
'end': end_time,
|
174 |
+
'text': fragment if not isinstance(fragment_words[0], dict) else prefix.join(word['word'] for word in fragment_words)
|
175 |
+
})
|
176 |
+
|
177 |
+
start_idx = split_point + 1
|
178 |
+
|
179 |
+
# Handle the last fragment
|
180 |
+
if start_idx < len(words):
|
181 |
+
fragment_words = words[start_idx:]
|
182 |
+
current_word_count = len(fragment_words)
|
183 |
+
|
184 |
+
if isinstance(fragment_words[0], dict):
|
185 |
+
start_time = fragment_words[0]['start']
|
186 |
+
end_time = fragment_words[-1]['end']
|
187 |
+
else:
|
188 |
+
fragment = prefix.join(fragment_words).strip()
|
189 |
+
current_duration = (current_word_count / total_word_count) * total_time
|
190 |
+
start_time = elapsed_time
|
191 |
+
end_time = elapsed_time + current_duration
|
192 |
+
|
193 |
+
if next_start_time and (next_start_time - end_time) <= 0.8:
|
194 |
+
end_time = next_start_time
|
195 |
+
|
196 |
+
subtitles.append({
|
197 |
+
'start': start_time,
|
198 |
+
'end': end_time if end_time is not None else segment['end'],
|
199 |
+
'text': fragment if not isinstance(fragment_words[0], dict) else prefix.join(word['word'] for word in fragment_words)
|
200 |
+
})
|
201 |
+
|
202 |
+
return subtitles
|
203 |
+
|
204 |
+
|
205 |
+
|
206 |
+
def save(self, filename="subtitles.srt", advanced_splitting=True):
|
207 |
+
|
208 |
+
subtitles = self.process_segments(advanced_splitting)
|
209 |
+
|
210 |
+
def write_subtitle(file, idx, start_time, end_time, text):
|
211 |
+
|
212 |
+
file.write(f"{idx}\n")
|
213 |
+
file.write(f"{start_time} --> {end_time}\n")
|
214 |
+
file.write(text + "\n\n")
|
215 |
+
|
216 |
+
with open(filename, 'w', encoding='utf-8') as file:
|
217 |
+
if self.is_vtt:
|
218 |
+
file.write("WEBVTT\n\n")
|
219 |
+
|
220 |
+
if advanced_splitting:
|
221 |
+
for idx, subtitle in enumerate(subtitles, 1):
|
222 |
+
start_time = format_timestamp(subtitle['start'], self.is_vtt)
|
223 |
+
end_time = format_timestamp(subtitle['end'], self.is_vtt)
|
224 |
+
text = subtitle['text'].strip()
|
225 |
+
write_subtitle(file, idx, start_time, end_time, text)
|
226 |
+
|
227 |
+
return len(subtitles)
|
whisperx/__init__.py
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from .transcribe import load_model
|
2 |
+
from .alignment import load_align_model, align
|
3 |
+
from .audio import load_audio
|
4 |
+
from .diarize import assign_word_speakers, DiarizationPipeline
|
whisperx/__main__.py
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from .transcribe import cli
|
2 |
+
|
3 |
+
|
4 |
+
cli()
|
whisperx/__pycache__/__init__.cpython-310.pyc
ADDED
Binary file (372 Bytes). View file
|
|
whisperx/__pycache__/alignment.cpython-310.pyc
ADDED
Binary file (12.6 kB). View file
|
|
whisperx/__pycache__/asr.cpython-310.pyc
ADDED
Binary file (11.5 kB). View file
|
|
whisperx/__pycache__/audio.cpython-310.pyc
ADDED
Binary file (4.46 kB). View file
|
|
whisperx/__pycache__/diarize.cpython-310.pyc
ADDED
Binary file (2.79 kB). View file
|
|
whisperx/__pycache__/transcribe.cpython-310.pyc
ADDED
Binary file (10.1 kB). View file
|
|
whisperx/__pycache__/types.cpython-310.pyc
ADDED
Binary file (1.89 kB). View file
|
|
whisperx/__pycache__/utils.cpython-310.pyc
ADDED
Binary file (13.2 kB). View file
|
|