diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8b63ece..f97c03b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -9,6 +9,14 @@ on: pull_request: branches: [ master ] +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: write + pull-requests: write + # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: Windows: @@ -20,29 +28,42 @@ jobs: include: - arch: x86 - arch: x64 + - arch: arm64 steps: - - name: "Set Build Type" + - name: "Set Build Type and Presets" id: build_type + shell: pwsh run: | - if ("${{ github.ref }}".StartsWith("refs/tags/")) { - echo "BUILD_TYPE::Release"; - echo "::set-output name=BUILD_TYPE::Release"; - } else { - echo "BUILD_TYPE::Debug"; - echo "::set-output name=BUILD_TYPE::Debug"; + $buildType = if ("${{ github.ref }}".StartsWith("refs/tags/")) { "Release" } else { "Debug" } + echo "BUILD_TYPE=$buildType" >> $env:GITHUB_OUTPUT + + $arch = "${{ matrix.arch }}" + switch ($arch) { + "x64" { + $configurePreset = "windows-msvc-vs17" + $buildDir = [System.IO.Path]::GetFullPath((Join-Path $env:GITHUB_WORKSPACE "..\..\_temp\windows-x64")).Replace('\', '/') + } + "x86" { + $configurePreset = "windows-msvc-vs17-x86" + $buildDir = [System.IO.Path]::GetFullPath((Join-Path $env:GITHUB_WORKSPACE "..\..\_temp\windows-x86")).Replace('\', '/') + } + "arm64" { + $configurePreset = "windows-msvc-vs17-arm64" + $buildDir = [System.IO.Path]::GetFullPath((Join-Path $env:GITHUB_WORKSPACE "..\..\_temp\windows-arm64")).Replace('\', '/') + } + default { + throw "Unsupported matrix.arch: $arch" + } } - if ("${{ matrix.arch }}" -eq "x64") { - echo "Arch:x64"; - echo "::set-output name=BUILD_ARCH::x64"; - } else { - echo "Arch:Win32"; - echo "::set-output name=BUILD_ARCH::Win32"; - } + $buildPreset = if ($buildType -eq "Release") { "${configurePreset}-release" } else { $configurePreset } + echo "CONFIGURE_PRESET=$configurePreset" >> $env:GITHUB_OUTPUT + echo "BUILD_PRESET=$buildPreset" >> $env:GITHUB_OUTPUT + echo "BUILD_DIR=$buildDir" >> $env:GITHUB_OUTPUT # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: true @@ -50,11 +71,11 @@ jobs: # uses: ammaraskar/msvc-problem-matcher@master # Install latest CMake - - uses: lukka/get-cmake@latest + - uses: lukka/get-cmake@v4.2.0 # Restore from cache the previously built ports - name: Restore artifacts, or setup vcpkg - uses: lukka/run-vcpkg@v10 + uses: lukka/run-vcpkg@v11 with: vcpkgDirectory: '${{ github.workspace }}/vcpkg' @@ -66,15 +87,16 @@ jobs: - name: 'Run CMake' uses: lukka/run-cmake@v10 + continue-on-error: false with: - configurePreset: 'windows-msvc-vs17' - buildPreset: ${{ startsWith(github.ref, 'refs/tags/') && 'windows-msvc-vs17-release' || 'windows-msvc-vs17' }} + configurePreset: ${{ steps.build_type.outputs.CONFIGURE_PRESET }} + buildPreset: ${{ steps.build_type.outputs.BUILD_PRESET }} - name: "Check file existence" uses: andstor/file-existence-action@v3 with: allow_failure: true - files: "${{ github.workspace }}/../../_temp/windows/src/${{ steps.build_type.outputs.BUILD_TYPE }}/simple.dll, ${{ github.workspace }}/../../_temp/windows/cppjieba/src/cppjieba/dict/jieba.dict.utf8" + files: "${{ steps.build_type.outputs.BUILD_DIR }}/src/${{ steps.build_type.outputs.BUILD_TYPE }}/simple.dll, ${{ steps.build_type.outputs.BUILD_DIR }}/cppjieba/src/cppjieba/dict/jieba.dict.utf8" # can't build # npm run @@ -102,17 +124,18 @@ jobs: - name: Package if: startsWith(github.ref, 'refs/tags/') run: | + $outputDir = "$env:GITHUB_WORKSPACE\artifacts" + New-Item -ItemType Directory -Path $outputDir -Force mkdir libsimple-windows-${{ matrix.arch }} Copy-Item -Path src/${{ steps.build_type.outputs.BUILD_TYPE }}/simple.dll,cppjieba/src/cppjieba/dict/ -Destination libsimple-windows-${{ matrix.arch }}/ -Recurse - Compress-Archive -Path libsimple-windows-${{ matrix.arch }} -DestinationPath libsimple-windows-${{ matrix.arch }}.zip - working-directory: "${{ github.workspace }}/../../_temp/windows/" + Compress-Archive -Path libsimple-windows-${{ matrix.arch }} -DestinationPath "$outputDir\libsimple-windows-${{ matrix.arch }}.zip" + working-directory: "${{ steps.build_type.outputs.BUILD_DIR }}" - name: Release if: startsWith(github.ref, 'refs/tags/') - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: draft: true - files: "D:/a/_temp/windows/libsimple-windows-${{ matrix.arch }}.zip" - # files: "${{ github.workspace }}/../../_temp/windows/libsimple-windows-${{ matrix.arch }}.zip" + files: "${{ github.workspace }}/artifacts/libsimple-windows-${{ matrix.arch }}.zip" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -121,20 +144,14 @@ jobs: strategy: fail-fast: true matrix: - include: - - os: ubuntu-20.04 - - os: ubuntu-22.04-arm - - os: ubuntu-latest + os: [ubuntu-22.04, ubuntu-24.04-arm, ubuntu-latest] + timeout-minutes: 60 + env: + BUILD_TYPE: ${{ startsWith(github.ref, 'refs/tags/') && 'Release' || 'Debug' }} steps: - - name: "Release Build Type" - if: startsWith(github.ref, 'refs/tags/') - run: echo "BUILD_TYPE=Release" >> $GITHUB_ENV - - name: "Debug Build Type" - if: startsWith(github.ref, 'refs/tags/') != true - run: echo "BUILD_TYPE=Debug" >> $GITHUB_ENV - - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: + fetch-depth: 0 submodules: true - name: Update apt-get @@ -147,7 +164,7 @@ jobs: sudo apt-get install -y gcc g++ - name: Restore artifacts, or setup vcpkg - uses: lukka/run-vcpkg@v10 + uses: lukka/run-vcpkg@v11 with: vcpkgDirectory: '${{ github.workspace }}/vcpkg' @@ -155,11 +172,6 @@ jobs: if: startsWith(github.ref, 'refs/tags/') != true run: sudo apt-get install lcov - - name: Install codecov - if: startsWith(github.ref, 'refs/tags/') != true - shell: bash - run: sudo pip install codecov - - run: | echo "RUNVCPKG_NO_CACHE=1" >> $GITHUB_ENV if: ${{ failure() || cancelled() }} @@ -167,17 +179,10 @@ jobs: - name: 'Run CMake' uses: lukka/run-cmake@v10 - if: startsWith(github.ref, 'refs/tags/') != true - with: - configurePreset: 'ninja-vcpkg-coverage' - buildPreset: 'ninja-vcpkg-coverage' - - - name: 'Run CMake without coverage' - if: startsWith(github.ref, 'refs/tags/') - uses: lukka/run-cmake@v10 + continue-on-error: false with: - configurePreset: 'ninja-vcpkg-release' - buildPreset: 'ninja-vcpkg-release' + configurePreset: ${{ startsWith(github.ref, 'refs/tags/') && 'ninja-vcpkg-release' || 'ninja-vcpkg-coverage' }} + buildPreset: ${{ startsWith(github.ref, 'refs/tags/') && 'ninja-vcpkg-release' || 'ninja-vcpkg-coverage' }} - name: 'Run CTest' if: ${{ startsWith(github.ref, 'refs/tags/') != true && matrix.os == 'ubuntu-latest' }} @@ -196,7 +201,12 @@ jobs: run: lcov --list coverage_filter.info - name: Code coverage - Upload to CodeCov if: ${{ startsWith(github.ref, 'refs/tags/') != true && matrix.os == 'ubuntu-latest' }} - run: bash <(curl -s https://codecov.io/bash) -f coverage_filter.info || echo "Codecov did not collect coverage reports" + uses: codecov/codecov-action@v5 + with: + files: coverage_filter.info + token: ${{ secrets.CODECOV_TOKEN }} + fail_ci_if_error: false + verbose: true - name: "Check file existence" uses: andstor/file-existence-action@v3 @@ -205,7 +215,7 @@ jobs: files: "${{ github.workspace }}/build/src/libsimple.so, ${{ github.workspace }}/build/test/dict/jieba.dict.utf8" # npm run - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v6 if: startsWith(github.ref, 'refs/tags/') != true with: node-version: '20' @@ -218,7 +228,7 @@ jobs: npm run b -- --ext_path="${{ github.workspace }}/build/src/" --dict_path="${{ github.workspace }}/build/test/dict/" # python run - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 if: startsWith(github.ref, 'refs/tags/') != true with: python-version: '3.13' # Version range or exact version of a Python version to use, using SemVer's version range syntax @@ -231,7 +241,7 @@ jobs: - name: "Build Changelog" id: build_changelog if: ${{ startsWith(github.ref, 'refs/tags/') && matrix.os == 'ubuntu-latest' }} - uses: mikepenz/release-changelog-builder-action@v1 + uses: mikepenz/release-changelog-builder-action@v6 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Package @@ -243,7 +253,7 @@ jobs: working-directory: "${{ github.workspace }}/build" - name: Release without changelog if: ${{ startsWith(github.ref, 'refs/tags/') && matrix.os != 'ubuntu-latest' }} - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: draft: true files: ${{ github.workspace }}/build/libsimple-linux-${{ matrix.os }}.zip @@ -251,7 +261,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Release if: ${{ startsWith(github.ref, 'refs/tags/') && matrix.os == 'ubuntu-latest' }} - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: draft: true files: ${{ github.workspace }}/build/libsimple-linux-${{ matrix.os }}.zip @@ -259,18 +269,57 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Build Summary + if: always() + run: | + echo "## Build Summary" >> $GITHUB_STEP_SUMMARY + echo "- Build Type: ${{ env.BUILD_TYPE }}" >> $GITHUB_STEP_SUMMARY + echo "- OS: ${{ runner.os }}" >> $GITHUB_STEP_SUMMARY + MacOS: - runs-on: macos-latest + runs-on: ${{ matrix.os }} needs: Linux + strategy: + fail-fast: true + matrix: + include: + - os: macos-15-intel + arch: x64 + - os: macos-15 + arch: arm64 + env: + BUILD_TYPE: ${{ startsWith(github.ref, 'refs/tags/') && 'Release' || 'Debug' }} steps: - - name: "Release Build Type" - if: startsWith(github.ref, 'refs/tags/') - run: echo "BUILD_TYPE=Release" >> $GITHUB_ENV - - name: "Debug Build Type" - if: startsWith(github.ref, 'refs/tags/') != true - run: echo "BUILD_TYPE=Debug" >> $GITHUB_ENV - - - uses: actions/checkout@v4 + - name: "Set macOS Build Presets" + id: mac_build + shell: bash + run: | + case "${{ matrix.arch }}" in + x64) + configure_preset="macos-ninja-x64" + build_dir="${GITHUB_WORKSPACE}/../../_temp/macos-x64" + ;; + arm64) + configure_preset="macos-ninja-arm64" + build_dir="${GITHUB_WORKSPACE}/../../_temp/macos-arm64" + ;; + *) + echo "Unsupported matrix.arch: ${{ matrix.arch }}" >&2 + exit 1 + ;; + esac + + if [[ "${{ github.ref }}" == refs/tags/* ]]; then + build_preset="${configure_preset}-release" + else + build_preset="${configure_preset}" + fi + + echo "CONFIGURE_PRESET=${configure_preset}" >> "$GITHUB_OUTPUT" + echo "BUILD_PRESET=${build_preset}" >> "$GITHUB_OUTPUT" + echo "BUILD_DIR=${build_dir}" >> "$GITHUB_OUTPUT" + + - uses: actions/checkout@v6 with: submodules: true @@ -280,7 +329,7 @@ jobs: # Restore from cache the previously built ports - name: Restore artifacts, or setup vcpkg - uses: lukka/run-vcpkg@v10 + uses: lukka/run-vcpkg@v11 with: vcpkgDirectory: '${{ github.workspace }}/vcpkg' @@ -293,24 +342,24 @@ jobs: - name: 'Run CMake' uses: lukka/run-cmake@v10 with: - configurePreset: 'macos-ninja' - buildPreset: ${{ startsWith(github.ref, 'refs/tags/') && 'macos-ninja-release' || 'macos-ninja' }} + configurePreset: ${{ steps.mac_build.outputs.CONFIGURE_PRESET }} + buildPreset: ${{ steps.mac_build.outputs.BUILD_PRESET }} - name: 'Run CTest' run: ctest -C ${{ env.BUILD_TYPE }} - working-directory: "${{ github.workspace }}/../../_temp/macos" + working-directory: "${{ steps.mac_build.outputs.BUILD_DIR }}" - name: "Check file existence" uses: andstor/file-existence-action@v3 with: allow_failure: true - files: "${{ github.workspace }}/../../_temp/macos/src/libsimple.dylib, ${{ github.workspace }}/../../_temp/macos/test/dict/jieba.dict.utf8" + files: "${{ steps.mac_build.outputs.BUILD_DIR }}/src/libsimple.dylib, ${{ steps.mac_build.outputs.BUILD_DIR }}/test/dict/jieba.dict.utf8" - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v6 with: node-version: '20' - # - uses: actions/setup-python@v5 + # - uses: actions/setup-python@v6 # with: # python-version: '3.12' # Version range or exact version of a Python version to use, using SemVer's version range syntax # npm run @@ -319,9 +368,9 @@ jobs: run: | brew install python-setuptools npm install - npm run p -- --ext_path="${{ github.workspace }}/../../_temp/macos/src/" --dict_path="${{ github.workspace }}/../../_temp/macos/test/dict/" + npm run p -- --ext_path="${{ steps.mac_build.outputs.BUILD_DIR }}/src/" --dict_path="${{ steps.mac_build.outputs.BUILD_DIR }}/test/dict/" # don't run this as it's toooo slow - # npm run b -- --ext_path="${{ github.workspace }}/../../_temp/macos/src/" --dict_path="${{ github.workspace }}/../../_temp/macos/test/dict/" + # npm run b -- --ext_path="${{ steps.mac_build.outputs.BUILD_DIR }}/src/" --dict_path="${{ steps.mac_build.outputs.BUILD_DIR }}/test/dict/" # python run # - name: run python example @@ -332,20 +381,185 @@ jobs: - name: Package if: startsWith(github.ref, 'refs/tags/') run: | - mkdir libsimple-osx-x64 + mkdir libsimple-osx-${{ matrix.arch }} sudo xattr -r -d com.apple.quarantine src/libsimple.dylib - cp -r src/libsimple.dylib test/dict libsimple-osx-x64/ - zip -r libsimple-osx-x64.zip libsimple-osx-x64 - working-directory: "${{ github.workspace }}/../../_temp/macos" + cp -r src/libsimple.dylib test/dict libsimple-osx-${{ matrix.arch }}/ + zip -r libsimple-osx-${{ matrix.arch }}.zip libsimple-osx-${{ matrix.arch }} + working-directory: "${{ steps.mac_build.outputs.BUILD_DIR }}" - name: Release if: startsWith(github.ref, 'refs/tags/') - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: draft: true - files: "${{ github.workspace }}/../../_temp/macos/libsimple-osx-x64.zip" + files: "${{ steps.mac_build.outputs.BUILD_DIR }}/libsimple-osx-${{ matrix.arch }}.zip" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: build-iOS + if: ${{ success() && matrix.arch == 'arm64' }} run: ./build-ios.sh + + - name: Package iOS + if: ${{ startsWith(github.ref, 'refs/tags/') && matrix.arch == 'arm64' }} + run: | + mkdir libsimple-ios-xcframework + cp -r output/libsimple.xcframework output/dict libsimple-ios-xcframework/ + zip -r libsimple-ios-xcframework.zip libsimple-ios-xcframework + working-directory: "${{ github.workspace }}" + + - name: Release iOS + if: ${{ startsWith(github.ref, 'refs/tags/') && matrix.arch == 'arm64' }} + uses: softprops/action-gh-release@v2 + with: + draft: true + files: "${{ github.workspace }}/libsimple-ios-xcframework.zip" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + Android: + runs-on: ubuntu-latest + needs: Linux + strategy: + fail-fast: true + matrix: + abi: [arm64-v8a, x86_64] + steps: + - uses: actions/checkout@v6 + with: + submodules: true + + - name: Resolve Android NDK + run: | + if [ -z "${ANDROID_NDK_LATEST_HOME:-}" ]; then + echo "ANDROID_NDK_LATEST_HOME is not available on this runner." >&2 + exit 1 + fi + echo "ANDROID_NDK_HOME=${ANDROID_NDK_LATEST_HOME}" >> "$GITHUB_ENV" + + - name: Configure Android + run: | + cmake -S "${{ github.workspace }}" -B "${{ github.workspace }}/build-android-${{ matrix.abi }}" \ + -DCMAKE_TOOLCHAIN_FILE="${ANDROID_NDK_HOME}/build/cmake/android.toolchain.cmake" \ + -DANDROID_ABI="${{ matrix.abi }}" \ + -DANDROID_PLATFORM=android-21 \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/output-android-${{ matrix.abi }}" \ + -DBUILD_TEST_EXAMPLE=OFF \ + -DBUILD_SHELL=OFF + + - name: Build Android + run: cmake --build "${{ github.workspace }}/build-android-${{ matrix.abi }}" --config Release --parallel + + - name: Install Android + run: cmake --install "${{ github.workspace }}/build-android-${{ matrix.abi }}" --config Release + + - name: Verify Android outputs + run: | + test -f "output-android-${{ matrix.abi }}/bin/libsimple.so" + test -f "output-android-${{ matrix.abi }}/bin/dict/jieba.dict.utf8" + + - name: Package Android + if: startsWith(github.ref, 'refs/tags/') + run: | + mkdir "libsimple-android-${{ matrix.abi }}" + cp -r "output-android-${{ matrix.abi }}/bin/libsimple.so" "output-android-${{ matrix.abi }}/bin/dict" "libsimple-android-${{ matrix.abi }}/" + zip -r "libsimple-android-${{ matrix.abi }}.zip" "libsimple-android-${{ matrix.abi }}" + working-directory: "${{ github.workspace }}" + + - name: Release Android + if: startsWith(github.ref, 'refs/tags/') + uses: softprops/action-gh-release@v2 + with: + draft: true + files: "${{ github.workspace }}/libsimple-android-${{ matrix.abi }}.zip" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + OHOS: + runs-on: ubuntu-latest + needs: Linux + env: + OHOS_SDK_URL: ${{ secrets.OHOS_SDK_URL }} + OHOS_SDK_VERSION: v6.0.0.1 + steps: + - uses: actions/checkout@v6 + with: + submodules: true + + - name: Download and extract OHOS SDK + shell: bash + run: | + set -euxo pipefail + sdk_root="${RUNNER_TEMP}/ohos-sdk" + sdk_archive="${RUNNER_TEMP}/ohos-sdk.tar.gz" + mkdir -p "${sdk_root}" + + if [ -n "${OHOS_SDK_URL:-}" ]; then + curl -L --retry 3 --retry-all-errors --max-time 3600 "${OHOS_SDK_URL}" -o "${sdk_archive}" + else + sdk_base_url="https://github.com/openharmony-rs/ohos-sdk/releases/download/${OHOS_SDK_VERSION}" + curl -L --retry 3 --retry-all-errors --max-time 3600 "${sdk_base_url}/ohos-sdk-windows_linux-public.tar.gz.aa" -o "${sdk_archive}.aa" + curl -L --retry 3 --retry-all-errors --max-time 3600 "${sdk_base_url}/ohos-sdk-windows_linux-public.tar.gz.ab" -o "${sdk_archive}.ab" + cat "${sdk_archive}.aa" "${sdk_archive}.ab" > "${sdk_archive}" + fi + + tar -xzf "${sdk_archive}" -C "${sdk_root}" + + toolchain_file="$(find "${sdk_root}" -type f -path '*/native/build/cmake/ohos.toolchain.cmake' | head -n 1 || true)" + if [ -z "${toolchain_file}" ]; then + mapfile -t zips < <(find "${sdk_root}" -type f -name '*.zip') + if [ "${#zips[@]}" -eq 0 ]; then + echo "No SDK zip files found under ${sdk_root}." >&2 + find "${sdk_root}" -maxdepth 4 -print + exit 1 + fi + + # Fast path: native build only needs Linux native/toolchains SDK packages. + mapfile -t preferred_zips < <(printf '%s\n' "${zips[@]}" | grep -E '/linux/(native|toolchains)-linux-x64-.*\.zip$' || true) + if [ "${#preferred_zips[@]}" -gt 0 ]; then + for f in "${preferred_zips[@]}"; do unzip -q -o "${f}" -d "$(dirname "${f}")"; done + else + for f in "${zips[@]}"; do unzip -q -o "${f}" -d "$(dirname "${f}")"; done + fi + + toolchain_file="$(find "${sdk_root}" -type f -path '*/native/build/cmake/ohos.toolchain.cmake' | head -n 1 || true)" + if [ -z "${toolchain_file}" ]; then + for f in "${zips[@]}"; do unzip -q -o "${f}" -d "$(dirname "${f}")"; done + toolchain_file="$(find "${sdk_root}" -type f -path '*/native/build/cmake/ohos.toolchain.cmake' | head -n 1 || true)" + fi + fi + + if [ -z "${toolchain_file}" ]; then + echo "Failed to locate OHOS toolchain after extraction." >&2 + find "${sdk_root}" -maxdepth 5 -print + exit 1 + fi + + ohos_sdk="${toolchain_file%/native/build/cmake/ohos.toolchain.cmake}" + echo "OHOS_SDK=${ohos_sdk}" >> "${GITHUB_ENV}" + + - name: Build OHOS + run: ./build-ohos.sh + + - name: Verify OHOS outputs + run: | + test -f "output/bin/libsimple.so" + test -f "output/bin/dict/jieba.dict.utf8" + + - name: Package OHOS + if: startsWith(github.ref, 'refs/tags/') + run: | + mkdir libsimple-ohos-arm64-v8a + cp -r output/bin/libsimple.so output/bin/dict libsimple-ohos-arm64-v8a/ + zip -r libsimple-ohos-arm64-v8a.zip libsimple-ohos-arm64-v8a + working-directory: "${{ github.workspace }}" + + - name: Release OHOS + if: startsWith(github.ref, 'refs/tags/') + uses: softprops/action-gh-release@v2 + with: + draft: true + files: "${{ github.workspace }}/libsimple-ohos-arm64-v8a.zip" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 785a537..d90bdd7 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,9 @@ compile_commands.json libsimple.* build/ build-ios/ +build-ios-*/ +build-ohos/ +output-ios*/ *.gch bin/ output/ @@ -20,4 +23,4 @@ examples/go/libsimple-osx-x64/ cmake-build-debug/ -.vscode/ \ No newline at end of file +.vscode/ diff --git a/CMakeLists.txt b/CMakeLists.txt index bb6430e..3fdf35f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,8 +34,8 @@ if(BUILD_SQLITE3) set(SQLITE_INSTALL_DIR ${SQLITE_ROOT}) set(SQLITE_INCLUDE_DIR ${SQLITE_INSTALL_DIR}/include) set(SQLITE_LIB_DIR ${SQLITE_INSTALL_DIR}/lib) - if (NOT IOS) - option(SQLITE_ENABLE_FTS5 "enables full text searches version 5" ON) + if (MSVC OR (UNIX AND NOT IOS)) + option(SQLITE_ENABLE_FTS5 "enables full text searches version 5" ON) option(BUILD_SHELL "build SQLite3 shell application" ON) add_subdirectory(contrib/sqlite3) endif() diff --git a/CMakePresets.json b/CMakePresets.json index 4501372..785d612 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -2,7 +2,7 @@ "version": 8, "cmakeMinimumRequired": { "major": 3, - "minor": 21, + "minor": 28, "patch": 0 }, "configurePresets": [ @@ -65,25 +65,66 @@ "CMAKE_INSTALL_PREFIX": { "type": "PATH", "value": "${sourceDir}/../../_temp/macos/install" + }, + "BUILD_SHELL": { + "type": "BOOL", + "value": "OFF" } } }, { - "name": "windows-msvc-vs17", - "displayName": "Visual Studio 17 2022", - "description": "Configure with VS17 and vcpkg toolchain", + "name": "macos-ninja-x64", + "displayName": "macOS Ninja (x64)", + "description": "Configure for macOS x64 build", + "inherits": "macos-ninja", + "binaryDir": "${sourceDir}/../../_temp/macos-x64", + "cacheVariables": { + "CMAKE_OSX_ARCHITECTURES": "x86_64", + "CMAKE_INSTALL_PREFIX": { + "type": "PATH", + "value": "${sourceDir}/../../_temp/macos-x64/install" + } + } + }, + { + "name": "macos-ninja-arm64", + "displayName": "macOS Ninja (arm64)", + "description": "Configure for macOS arm64 build", + "inherits": "macos-ninja", + "binaryDir": "${sourceDir}/../../_temp/macos-arm64", + "cacheVariables": { + "CMAKE_OSX_ARCHITECTURES": "arm64", + "CMAKE_INSTALL_PREFIX": { + "type": "PATH", + "value": "${sourceDir}/../../_temp/macos-arm64/install" + } + } + }, + { + "name": "windows-msvc-vs17-base", + "hidden": true, "generator": "Visual Studio 17 2022", - "architecture": "x64", "toolset": "v143", - "binaryDir": "${sourceDir}/../../_temp/windows", "cacheVariables": { "CMAKE_TOOLCHAIN_FILE": { "type": "FILEPATH", "value": "${sourceDir}/vcpkg/scripts/buildsystems/vcpkg.cmake" }, + "BUILD_SHELL": { + "type": "BOOL", + "value": "OFF" + }, "BUILD_TESTING": { "type": "BOOL", "value": "OFF" + }, + "BUILD_SQLITE3": { + "type": "BOOL", + "value": "ON" + }, + "SQLITE_ENABLE_FTS5": { + "type": "BOOL", + "value": "ON" } }, "condition": { @@ -91,6 +132,39 @@ "lhs": "${hostSystemName}", "rhs": "Windows" } + }, + { + "name": "windows-msvc-vs17", + "displayName": "Visual Studio 17 2022 (x64)", + "description": "Configure with VS17 and vcpkg toolchain (x64)", + "inherits": "windows-msvc-vs17-base", + "architecture": "x64", + "binaryDir": "${sourceDir}/../../_temp/windows-x64", + "cacheVariables": { + "VCPKG_TARGET_TRIPLET": "x64-windows" + } + }, + { + "name": "windows-msvc-vs17-x86", + "displayName": "Visual Studio 17 2022 (Win32)", + "description": "Configure with VS17 and vcpkg toolchain (Win32)", + "inherits": "windows-msvc-vs17-base", + "architecture": "Win32", + "binaryDir": "${sourceDir}/../../_temp/windows-x86", + "cacheVariables": { + "VCPKG_TARGET_TRIPLET": "x86-windows" + } + }, + { + "name": "windows-msvc-vs17-arm64", + "displayName": "Visual Studio 17 2022 (ARM64)", + "description": "Configure with VS17 and vcpkg toolchain (ARM64)", + "inherits": "windows-msvc-vs17-base", + "architecture": "ARM64", + "binaryDir": "${sourceDir}/../../_temp/windows-arm64", + "cacheVariables": { + "VCPKG_TARGET_TRIPLET": "arm64-windows" + } } ], "buildPresets": [ @@ -120,6 +194,32 @@ "description": "Build for macOS (Release)", "configuration": "Release" }, + { + "name": "macos-ninja-x64", + "configurePreset": "macos-ninja-x64", + "displayName": "Build macOS x64", + "description": "Build for macOS x64" + }, + { + "name": "macos-ninja-x64-release", + "configurePreset": "macos-ninja-x64", + "displayName": "Build macOS x64 (Release)", + "description": "Build for macOS x64 (Release)", + "configuration": "Release" + }, + { + "name": "macos-ninja-arm64", + "configurePreset": "macos-ninja-arm64", + "displayName": "Build macOS arm64", + "description": "Build for macOS arm64" + }, + { + "name": "macos-ninja-arm64-release", + "configurePreset": "macos-ninja-arm64", + "displayName": "Build macOS arm64 (Release)", + "description": "Build for macOS arm64 (Release)", + "configuration": "Release" + }, { "name": "windows-msvc-vs17", "configurePreset": "windows-msvc-vs17", @@ -132,6 +232,32 @@ "displayName": "Build Windows VS17 (Release)", "description": "Build with VS17 (Release)", "configuration": "Release" + }, + { + "name": "windows-msvc-vs17-x86", + "configurePreset": "windows-msvc-vs17-x86", + "displayName": "Build Windows VS17 (Win32)", + "description": "Build with VS17 (Win32)" + }, + { + "name": "windows-msvc-vs17-x86-release", + "configurePreset": "windows-msvc-vs17-x86", + "displayName": "Build Windows VS17 (Win32, Release)", + "description": "Build with VS17 (Win32, Release)", + "configuration": "Release" + }, + { + "name": "windows-msvc-vs17-arm64", + "configurePreset": "windows-msvc-vs17-arm64", + "displayName": "Build Windows VS17 (ARM64)", + "description": "Build with VS17 (ARM64)" + }, + { + "name": "windows-msvc-vs17-arm64-release", + "configurePreset": "windows-msvc-vs17-arm64", + "displayName": "Build Windows VS17 (ARM64, Release)", + "description": "Build with VS17 (ARM64, Release)", + "configuration": "Release" } ] } diff --git a/README.md b/README.md index 561cd66..33e6f5b 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ [![Downloads](https://img.shields.io/github/downloads/wangfenjin/simple/total)](https://img.shields.io/github/downloads/wangfenjin/simple/total) -[![build](https://github.com/wangfenjin/simple/workflows/CI/badge.svg)](https://github.com/wangfenjin/simple/actions?query=workflow%3ACI) +[![build](https://github.com/wangfenjin/simple/actions/workflows/main.yml/badge.svg?branch=master)](https://github.com/wangfenjin/simple/actions/workflows/main.yml) [![codecov](https://codecov.io/gh/wangfenjin/simple/branch/master/graph/badge.svg?token=8SHLFZ3RB4)](https://codecov.io/gh/wangfenjin/simple) [![CodeFactor](https://www.codefactor.io/repository/github/wangfenjin/simple/badge)](https://www.codefactor.io/repository/github/wangfenjin/simple) [![License: MIT](https://img.shields.io/badge/Dual_License-MIT_or_GPL_v3_later-blue.svg)](https://github.com/wangfenjin/simple/blob/master/LICENSE) @@ -26,6 +26,7 @@ simple 是一个支持中文和拼音的 [sqlite3 fts5](https://www.sqlite.org/f * C# 例子 https://github.com/dudylan/SqliteCheck/ * Rust 例子 https://github.com/xuxiaocheng0201/libsimple/ * Android Flutter 的例子 https://github.com/SageMik/sqlite3_simple +* React Native op-sqlite 例子 [examples/react-native-op-sqlite.md](./examples/react-native-op-sqlite.md) ### 命令行使用 @@ -63,6 +64,26 @@ sqlite> select simple_highlight(t1, 0, '[', ']') as text from t1 where text matc 5. simple_snippet() 实现截取 match 片段的功能,与 sqlite 自带的 snippet 功能类似,同样是增强连续 match 的词汇分到同一组的逻辑 6. jieba_query() 实现jieba分词的效果,在索引不变的情况下,可以实现更精准的匹配。可以通过 `-DSIMPLE_WITH_JIEBA=OFF ` 关掉结巴分词的功能 [#35](https://github.com/wangfenjin/simple/pull/35) 7. jieba_dict() 指定 dict 的目录,只需要调用一次,需要在调用 jieba_query() 之前指定。 +8. pinyin_dict() 支持指定自定义的 `pinyin.txt` 文件路径。调用成功后会立即切换拼音映射;如果文件格式不正确,会返回错误并保持当前映射不变。 + +### 自定义 pinyin.txt + +默认会使用内置在 so 中的 `contrib/pinyin.txt`。如果希望使用自己的拼音表,可以在查询前调用: + +```sql +select pinyin_dict('/path/to/pinyin.txt'); +``` + +`pinyin.txt` 每行格式与默认文件一致,例如: + +```text +U+3007: líng,yuán,xīng +U+3007: líng,yuán,xīng # 行尾注释也支持(前面需要空格) +``` + +注意: +- 建议在建索引和查询前先调用一次 `pinyin_dict()`。 +- 如果替换了拼音映射,已有索引中的拼音 token 不会自动重建,需要按你的业务策略重建索引。 ## 开发 @@ -83,6 +104,19 @@ make install ./build-ios.sh ``` +支持 ohos 编译: + +从 [官方发布渠道](https://gitcode.com/openharmony/docs/blob/master/en/release-notes/OpenHarmony-v5.0.1-release.md#acquiring-source-code-from-mirrors)下载适用于目标平台的SDK. +``` +tar -zxvf ohos-sdk.tar.gz +cd $OHOS_SDK/Linux +for i in *.zip;do unzip ${i};done +``` +开始编译 +``` +./build-ohos.sh +``` + ### 代码 - `src/entry` 入口文件,注册 sqlite tokenizer 和函数 - `src/simple_tokenizer` 分词器实现 @@ -99,5 +133,4 @@ make install ## Star History -[![Star History Chart](https://api.star-history.com/svg?repos=wangfenjin/simple&type=Date)](https://star-history.com/#wangfenjin/simple&Date) - +[![Star History Chart](https://api.star-history.com/svg?repos=wangfenjin/simple&type=Date)](https://www.star-history.com/#wangfenjin/simple&Date) diff --git a/build-and-run b/build-and-run index f369793..9765a2c 100755 --- a/build-and-run +++ b/build-and-run @@ -76,8 +76,8 @@ simple.example() { run "cd output/bin/" run "cat ${ProjectRoot}/example.sql ${ProjectRoot}/example-jieba.sql | ./sqlite3" run "./simple_cpp_example" - run "cd ${ProjectRoot}" - run "python3 examples/python3/db_connector.py './output/bin/libsimple'" + # run "cd ${ProjectRoot}" + # run "python3 examples/python3/db_connector.py './output/bin/libsimple'" } main() { diff --git a/build-and-run-no-jieba b/build-and-run-no-jieba index 424b8ec..f262468 100755 --- a/build-and-run-no-jieba +++ b/build-and-run-no-jieba @@ -76,8 +76,8 @@ simple.example() { run "cd output-no-jieba/bin/" run "./sqlite3 < ${ProjectRoot}/example.sql" run "./simple_cpp_example" - run "cd ${ProjectRoot}" - run "python3 examples/python3/db_connector.py" + # run "cd ${ProjectRoot}" + # run "python3 examples/python3/db_connector.py" } main() { diff --git a/build-ios-dynamic.sh b/build-ios-dynamic.sh new file mode 100755 index 0000000..af88b98 --- /dev/null +++ b/build-ios-dynamic.sh @@ -0,0 +1,50 @@ +#!/bin/sh + +set -eu + +script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) +toolchain_file="${script_dir}/contrib/ios.toolchain.cmake" +ios_output_root="${script_dir}/output-ios-dynamic" +final_output_root="${script_dir}/output" + +build_variant() { + platform="$1" + build_dir="${script_dir}/build-ios-dynamic-${platform}" + install_dir="${ios_output_root}/${platform}" + + cmake -S "${script_dir}" -B "${build_dir}" -G Xcode \ + -DCMAKE_TOOLCHAIN_FILE="${toolchain_file}" \ + -DPLATFORM="${platform}" \ + -DENABLE_BITCODE=0 \ + -DDEPLOYMENT_TARGET=12.0 \ + -DBUILD_SQLITE3=OFF \ + -DBUILD_IOS_DYNAMIC_FRAMEWORK=ON \ + -DCMAKE_INSTALL_PREFIX="" + + cmake --build "${build_dir}" --config Release + cmake --install "${build_dir}" --config Release --prefix "${install_dir}" +} + +rm -rf "${ios_output_root}" "${final_output_root}/libsimple-dynamic.xcframework" +mkdir -p "${final_output_root}" + +build_variant OS64 +build_variant SIMULATOR64 +build_variant SIMULATORARM64 + +sim64_framework="${ios_output_root}/SIMULATOR64/bin/simple.framework" +simarm64_framework="${ios_output_root}/SIMULATORARM64/bin/simple.framework" +sim_universal_dir="${ios_output_root}/SIMULATOR_UNIVERSAL/bin" +sim_universal_framework="${sim_universal_dir}/simple.framework" + +mkdir -p "${sim_universal_dir}" +cp -R "${sim64_framework}" "${sim_universal_framework}" +lipo -create \ + "${sim64_framework}/simple" \ + "${simarm64_framework}/simple" \ + -output "${sim_universal_framework}/simple" + +xcodebuild -create-xcframework \ + -framework "${ios_output_root}/OS64/bin/simple.framework" \ + -framework "${sim_universal_framework}" \ + -output "${final_output_root}/libsimple-dynamic.xcframework" diff --git a/build-ios.sh b/build-ios.sh index 6a5bbee..3097d7e 100755 --- a/build-ios.sh +++ b/build-ios.sh @@ -1,15 +1,57 @@ #!/bin/sh -current_dir=$(pwd)/$(dirname "$0") -build_dir="${current_dir}/build-ios" -lib_prefix="${current_dir}/output" +set -eu -cmake "$current_dir" -G Xcode -DCMAKE_TOOLCHAIN_FILE=contrib/ios.toolchain.cmake \ - -DPLATFORM=OS64COMBINED -DENABLE_BITCODE=1 \ - -DCMAKE_INSTALL_PREFIX="" -B "$build_dir" \ - -DDEPLOYMENT_TARGET=8.0 +script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) +toolchain_file="${script_dir}/contrib/ios.toolchain.cmake" +ios_output_root="${script_dir}/output-ios" +final_output_root="${script_dir}/output" +headers_dir="${ios_output_root}/headers" -cd "$build_dir" || exit +build_variant() { + platform="$1" + build_dir="${script_dir}/build-ios-${platform}" + install_dir="${ios_output_root}/${platform}" -cmake --build "$build_dir" --config Release -cmake --install "$build_dir" --config Release --prefix "${lib_prefix}" + cmake -S "${script_dir}" -B "${build_dir}" -G Xcode \ + -DCMAKE_TOOLCHAIN_FILE="${toolchain_file}" \ + -DPLATFORM="${platform}" \ + -DENABLE_BITCODE=1 \ + -DDEPLOYMENT_TARGET=8.0 \ + -DCMAKE_INSTALL_PREFIX="" + + cmake --build "${build_dir}" --config Release + cmake --install "${build_dir}" --config Release --prefix "${install_dir}" +} + +rm -rf "${ios_output_root}" "${final_output_root}/libsimple.xcframework" "${final_output_root}/dict" +mkdir -p "${headers_dir}" "${final_output_root}" +cp "${script_dir}"/src/*.h "${headers_dir}/" + +build_variant OS64 +build_variant SIMULATOR64 +build_variant SIMULATORARM64 + +sim64_lib="${ios_output_root}/SIMULATOR64/bin/libsimple.a" +simarm64_lib="${ios_output_root}/SIMULATORARM64/bin/libsimple.a" +sim_universal_dir="${ios_output_root}/SIMULATOR_UNIVERSAL/bin" +sim_universal_lib="${sim_universal_dir}/libsimple.a" + +mkdir -p "${sim_universal_dir}" +sim64_archs="$(lipo -archs "${sim64_lib}")" +simarm64_archs="$(lipo -archs "${simarm64_lib}")" + +if printf '%s' "${sim64_archs}" | grep -q 'x86_64' && printf '%s' "${sim64_archs}" | grep -q 'arm64'; then + cp "${sim64_lib}" "${sim_universal_lib}" +elif printf '%s' "${simarm64_archs}" | grep -q 'x86_64' && printf '%s' "${simarm64_archs}" | grep -q 'arm64'; then + cp "${simarm64_lib}" "${sim_universal_lib}" +else + lipo -create "${sim64_lib}" "${simarm64_lib}" -output "${sim_universal_lib}" +fi + +xcodebuild -create-xcframework \ + -library "${ios_output_root}/OS64/bin/libsimple.a" -headers "${headers_dir}" \ + -library "${sim_universal_lib}" -headers "${headers_dir}" \ + -output "${final_output_root}/libsimple.xcframework" + +cp -R "${ios_output_root}/OS64/bin/dict" "${final_output_root}/dict" diff --git a/build-ohos.sh b/build-ohos.sh new file mode 100755 index 0000000..77f96aa --- /dev/null +++ b/build-ohos.sh @@ -0,0 +1,35 @@ +#!/bin/sh + +set -eu + +current_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) +build_dir="${current_dir}/build-ohos" +lib_prefix="${current_dir}/output" +: "${OHOS_SDK:?OHOS_SDK is required}" +CMAKE="${OHOS_SDK}/native/build-tools/cmake/bin/cmake" +TOOLCHAIN_FILE="${OHOS_SDK}/native/build/cmake/ohos.toolchain.cmake" +OHOS_ARCH="${OHOS_ARCH:-arm64-v8a}" + +if [ ! -x "${CMAKE}" ]; then + echo "CMake not found at ${CMAKE}" >&2 + exit 1 +fi + +if [ ! -f "${TOOLCHAIN_FILE}" ]; then + echo "OHOS toolchain file not found at ${TOOLCHAIN_FILE}" >&2 + exit 1 +fi + +mkdir -p "${build_dir}" + +"${CMAKE}" \ + -S "${current_dir}" \ + -B "${build_dir}" \ + -DCMAKE_TOOLCHAIN_FILE="${TOOLCHAIN_FILE}" \ + -DCMAKE_INSTALL_PREFIX="${lib_prefix}" \ + -DOHOS_ARCH="${OHOS_ARCH}" \ + -DCMAKE_CXX_FLAGS="-Wno-unused-command-line-argument" \ + -DCMAKE_C_FLAGS="-Wno-unused-command-line-argument" + +"${CMAKE}" --build "${build_dir}" --parallel +"${CMAKE}" --install "${build_dir}" diff --git a/contrib/CMakeRC.cmake b/contrib/CMakeRC.cmake index 5cad705..1a6c3b8 100644 --- a/contrib/CMakeRC.cmake +++ b/contrib/CMakeRC.cmake @@ -34,7 +34,7 @@ endif() set(_version 2.0.0) -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.19) include(CMakeParseArguments) if(COMMAND cmrc_add_resource_library) diff --git a/contrib/ios.toolchain.cmake b/contrib/ios.toolchain.cmake index edf5a10..0fbd671 100644 --- a/contrib/ios.toolchain.cmake +++ b/contrib/ios.toolchain.cmake @@ -129,7 +129,7 @@ # command. # -cmake_minimum_required(VERSION 3.8.0) +cmake_minimum_required(VERSION 3.19) # CMake invokes the toolchain file twice during the first build, but only once during subsequent rebuilds. if(IOS_TOOLCHAIN_HAS_RUN) diff --git a/contrib/pinyin-mini.txt b/contrib/pinyin-mini.txt new file mode 100644 index 0000000..720a792 --- /dev/null +++ b/contrib/pinyin-mini.txt @@ -0,0 +1,3 @@ +# demo custom pinyin dictionary for example.sql +U+5468: zhōu # 周 +U+4F26: lún # 伦 diff --git a/contrib/pinyin.txt b/contrib/pinyin.txt index 59330ae..3c5866c 100644 --- a/contrib/pinyin.txt +++ b/contrib/pinyin.txt @@ -806,6 +806,7 @@ U+37DF: xiàng U+37E0: gǎng U+37E2: qí U+37E4: lù +U+37E5: cēn U+37E6: yùn U+37E7: è U+37E8: duān @@ -896,6 +897,7 @@ U+3849: xián U+384A: qià U+384B: yé,ān U+384C: mào +U+384E: shān U+384F: shù,xū,tóu,shū U+3851: qiāo,jiǎo U+3852: zhūn @@ -1081,6 +1083,7 @@ U+3929: kè U+392A: yuàn U+392B: dé U+392C: huì +U+392D: jiāo U+392E: guàng U+392F: jiǒng U+3930: zuò @@ -1162,6 +1165,7 @@ U+3984: má U+3985: diē,dì,chài U+3986: hū,xiā U+3987: lù +U+3988: ài U+3989: yì U+398A: huá U+398B: zhā @@ -1240,6 +1244,7 @@ U+39D7: zǐ,jǐ,zhǐ U+39D8: zì U+39D9: bì,bié U+39DA: wǎ +U+39DB: lǎn U+39DC: liè U+39DF: kuǎi U+39E1: hài,wèi @@ -2999,6 +3004,7 @@ U+41D6: yǔn U+41D7: zhōng U+41D8: hù U+41D9: yǐn +U+41DA: pū U+41DB: zhǐ U+41DC: qiǎn,qiàn U+41DE: gān,gǎn @@ -4158,6 +4164,7 @@ U+46CF: ní,nǐ,nì U+46D0: cí U+46D1: mì U+46D2: biàn +U+46D3: jiān U+46D4: ná U+46D5: yù U+46D6: è @@ -4367,7 +4374,7 @@ U+47B1: hú U+47B3: bó U+47B4: zhāo U+47B5: juě,zhuò -U+47B6: tāng,tàng +U+47B6: tāng,tì U+47B7: jué U+47B8: fù U+47B9: huáng @@ -5513,6 +5520,7 @@ U+4C99: jié U+4C9A: lài U+4C9B: méng U+4C9C: yè +U+4C9D: chāng U+4C9E: lì U+4C9F: yìn U+4CA0: chūn @@ -19681,7 +19689,7 @@ U+8453: hóng U+8454: hóu U+8455: yàn U+8456: tū -U+8457: zhù,zhuó,chú,zhāo,zháo,zhe +U+8457: zhù,zhe,zhuó,chú,zhāo,zháo U+8458: zī U+8459: xiāng U+845A: rèn,shèn @@ -26855,6 +26863,7 @@ U+201B5: ní U+201B6: dàn U+201CB: dǎn U+201CF: tāo +U+201D0: yú U+201D2: gōng U+201D7: kuā U+201D8: chù @@ -26863,6 +26872,7 @@ U+201F1: mò U+201F3: shī U+201F5: gǎn U+201F7: shēng +U+201F9: jìng U+20201: tuō U+20205: shōu U+2020A: niě @@ -27333,6 +27343,7 @@ U+20854: yì U+2085C: kè U+2085D: yì U+2085E: mò +U+20860: chì U+20861: cán U+20863: gěng U+20864: kè @@ -27630,6 +27641,7 @@ U+20C1A: shì U+20C1B: hù,gào U+20C1C: è U+20C34: shí +U+20C37: lú U+20C39: tuō U+20C3A: dài U+20C3B: wài,wai @@ -27695,6 +27707,7 @@ U+20CCB: qiǎn U+20CCC: qià U+20CCE: sì U+20CD0: bāng +U+20CDE: què U+20CEC: jīng U+20CED: kè U+20CF3: āi @@ -28104,6 +28117,7 @@ U+21212: yuān U+21213: yuàn U+21217: mò U+21219: yóu +U+2121B: yuān U+2121E: wǔ U+21220: zhāng U+21223: xuān @@ -28143,6 +28157,7 @@ U+21284: fèn U+21285: fèn,biàn U+21289: mò U+2128D: shù +U+21291: xǐ U+2129B: áo U+2129D: pǐ U+2129E: píng,pìng @@ -28161,6 +28176,7 @@ U+212BC: qióng U+212D9: huí U+212DA: jì U+212DF: gào +U+212E4: jìn U+212E7: yóu U+212E8: chā U+212E9: dé @@ -28447,6 +28463,7 @@ U+21766: nǒu U+21767: xì U+21769: yāo U+2176B: chān +U+2178B: cān U+21798: xiè U+21799: miè U+2179A: kěng @@ -28494,6 +28511,7 @@ U+21819: tōng U+2181A: bì U+2181C: mó,mò U+2181E: fú +U+2181F: zhú U+21825: qiáng U+21839: zhì U+2183C: sōu @@ -28562,6 +28580,7 @@ U+2194D: xiào U+2194E: cí,zǐ U+2195E: jì,bèi U+21966: nǐ,jìn +U+21967: jìn U+21968: nǐ,jìn U+21969: tǐ U+21976: jù,rú @@ -28831,6 +28850,7 @@ U+21D59: àng U+21D5A: jiè U+21D5B: fù U+21D5C: yòng +U+21D5D: zōng U+21D5E: fēng U+21D6C: mù U+21D76: sè @@ -28937,6 +28957,7 @@ U+21E78: cōng U+21E79: hú U+21E7C: àn,yǎn U+21E7E: bǐng +U+21E83: qiāng U+21E87: duǒ U+21E90: zhǔ U+21E91: dié @@ -29016,6 +29037,7 @@ U+21F46: zhì U+21F49: jì U+21F4A: chēng U+21F56: xiè +U+21F57: xiǎn U+21F5B: zuǐ U+21F5C: án U+21F5D: hāo @@ -29619,6 +29641,7 @@ U+225C8: zhāo U+225C9: nè U+225CA: xiè,jiá U+225CE: yì +U+225D3: yǔ U+225EB: fǔ U+225ED: shè U+225EF: yuán @@ -29640,6 +29663,7 @@ U+2260A: shēn U+2260B: tè U+2260C: hóng U+2260D: bì,fú +U+22619: lǒng U+2261D: nǐ U+2261F: qiáo U+22627: ruǎn @@ -29653,6 +29677,8 @@ U+22642: xuān U+22645: xí U+22647: yǐ U+22650: náo +U+22651: mái +U+22652: xiāo U+22653: wèi U+2266E: kàn U+22671: lòng @@ -29778,6 +29804,7 @@ U+227F1: téng U+227F3: lǜ U+227F4: tuí U+227F5: bī +U+227FC: méng U+227FE: huì U+227FF: huán U+2281B: kuò @@ -29898,6 +29925,7 @@ U+229C5: qiáng U+229C7: shú U+229C8: zhuó U+229CF: shī +U+229D0: zhàn U+229D1: tì U+229D6: zhá U+229D7: zhān @@ -29974,6 +30002,7 @@ U+22A8B: pěng,féng,bàng U+22A8D: jié,jiā U+22A8E: xuē U+22A8F: hú,gǔ +U+22A93: yú U+22AA5: yǒu U+22AA6: nǔ U+22AA7: yè @@ -29986,6 +30015,7 @@ U+22ABE: zhàng U+22AD0: jié U+22AD3: nǔ U+22AD4: shàn,quán +U+22AD8: lú U+22AE2: jiá U+22AE7: zhǒu U+22AE8: rǒng,rēng @@ -30002,6 +30032,7 @@ U+22AF8: wǔ,wū U+22AFB: nié U+22AFF: xiān U+22B00: hóng +U+22B0D: yè U+22B2B: tìng U+22B2C: jǐn U+22B31: jié @@ -30400,6 +30431,7 @@ U+230B4: tíng U+230B5: zhuàn U+230BB: ōu,kōu U+230BD: wò +U+230C1: zhú U+230C4: xīn U+230C5: ruǎn U+230C8: zhuó @@ -30491,6 +30523,7 @@ U+2320D: dié U+2320E: wèi U+23220: biàn U+23221: tǐ +U+23223: yìng U+23225: tú U+23236: gèng U+23244: chí @@ -30510,6 +30543,7 @@ U+23261: tǐng U+23269: yà U+2326A: huǎng U+2326E: bīn +U+2327C: ài U+2327E: òu U+2327F: cáo U+23281: áo @@ -30595,6 +30629,7 @@ U+233DE: yóu U+233DF: pài U+233E0: xiáo U+233E1: jí +U+233E2: tuán U+233F6: qī U+233F7: hé U+233FA: kǒng @@ -30606,6 +30641,7 @@ U+2340E: wǎ U+2340F: niǎn U+23411: cí U+23413: yí +U+23415: jìng U+23424: jiu U+2342B: yāng U+2342C: lí @@ -30617,6 +30653,7 @@ U+2343F: yī U+23441: chòng U+23442: hù U+23443: zhuǎ +U+2345D: tuán U+23466: qióng U+23467: duò U+23478: tóng @@ -30628,6 +30665,8 @@ U+23484: xiē U+23485: zhèn U+23486: qiào U+23487: tū +U+2348C: guō +U+23497: què U+234B7: hàn U+234B8: kuàng U+234B9: suō @@ -30650,6 +30689,7 @@ U+234D5: òu U+234D7: hū U+234DB: yòu U+234DD: měng +U+234FF: lào U+23510: zhǐ U+23513: bǐ U+23517: shēn @@ -30682,6 +30722,7 @@ U+2354C: gōu U+2354D: sǔn U+2354E: xiǎn U+2354F: zhuǎn +U+23572: héng U+2357E: chóu,bì U+23584: qiāo U+23585: tí @@ -30711,6 +30752,7 @@ U+235BC: xún U+235BE: bèi U+235BF: zhí U+235C1: dùn,zā +U+235CA: mán U+235CB: dǎng U+235D0: réng U+235F2: gān @@ -30720,6 +30762,7 @@ U+235F8: tuò U+235F9: yàng U+235FA: kū U+235FB: zhì +U+23610: jí U+23616: jiān U+23617: nì U+23618: shēn,zhēn @@ -30776,6 +30819,7 @@ U+23695: yè,yǎn U+23696: ān U+23698: yì U+23699: jiān,jiàn +U+2369A: zhú U+2369C: sōng U+2369D: wǒ U+2369F: sè @@ -31112,6 +31156,7 @@ U+23A33: xiàng U+23A36: huì U+23A3A: gǔ U+23A3B: kǎo +U+23A3C: tì U+23A3E: xīng U+23A3F: hún U+23A40: biān @@ -31264,6 +31309,7 @@ U+23BCF: róng,rǒng U+23BD0: róng U+23BDA: tà U+23BDC: sōu +U+23BE3: mén U+23BE4: lí U+23BE7: cuǐ,suī U+23BE8: zōng @@ -31275,6 +31321,7 @@ U+23BEF: suī U+23BF1: péi U+23BF4: bì U+23BF5: dì +U+23BF6: sān U+23BF8: qú U+23BF9: qiáo U+23BFB: fēn @@ -31312,6 +31359,7 @@ U+23C50: yìn,zhì U+23C53: xiào,hào U+23C57: jì U+23C5C: yīn +U+23C5D: dōng U+23C66: fēn U+23C67: zhòng U+23C6B: gǔ @@ -31347,6 +31395,7 @@ U+23CBD: rǒng U+23CBE: guài U+23CC0: zhí U+23CC3: jí +U+23CC6: píng U+23CDC: fàn U+23CDF: jié U+23CE0: hǎi,mǔ @@ -31503,6 +31552,7 @@ U+23F70: guō U+23F73: ruán U+23F74: wèi U+23F77: sì +U+23F8D: lián U+23FA4: qì U+23FA6: zhǎng U+23FC5: dǒng @@ -31625,6 +31675,7 @@ U+2419D: pū U+2419E: xù U+2419F: sī U+241A1: wǔ +U+241A2: lún U+241AE: fū U+241B0: shì U+241B3: huì @@ -31637,6 +31688,7 @@ U+241C4: shǎn U+241DC: mín U+241DE: gē U+241E0: hū +U+241ED: lóng U+241EF: ēn,āo U+241F0: fá U+241F3: xù,xuè @@ -31715,6 +31767,7 @@ U+243AD: lǐn,yǐn U+243AF: zēng U+243B1: juǎn U+243B2: hū +U+243BB: xiāo U+243D7: shěn U+243D8: huò U+243DC: kuì @@ -31903,6 +31956,7 @@ U+24663: xīn U+24664: fū U+24669: jiè U+2466D: fǔ +U+2466F: láo U+24670: tè U+24671: shè U+24674: chāo @@ -32019,6 +32073,7 @@ U+2475A: chēn U+2475B: tuó U+2475E: chù U+2475F: fú,fèi +U+24762: zé U+24767: bào U+2476C: dǐ U+2476D: cǎi @@ -32034,6 +32089,7 @@ U+2477B: yí U+2477D: lǜ U+2477F: kuāng U+24782: zhì +U+24783: huì U+24787: wá,kuáng U+24788: dī U+24789: shù @@ -32504,6 +32560,7 @@ U+24D7E: chā U+24D80: jiǎo U+24D81: lǎo U+24D88: xī +U+24D8A: guì U+24D8B: bó U+24D93: zhǐ U+24D95: tùn @@ -32613,6 +32670,7 @@ U+24E74: mó U+24E75: duī U+24E77: dǎo U+24E78: qí +U+24E7A: è U+24E80: shuāi U+24E83: xiāo,jiāo,yāo U+24E84: zhǒng,tóng @@ -32642,6 +32700,7 @@ U+24EC4: suì U+24EC5: wèi U+24EC6: wán U+24EC7: chǔ +U+24ECA: tuí U+24ECC: wò U+24ED6: bì U+24ED8: yǐn @@ -32745,6 +32804,7 @@ U+24FE8: xiào U+24FE9: què U+24FEB: chuò U+24FED: fǔ +U+24FF2: fū U+24FF3: qǐn U+24FF4: lù U+24FF5: què @@ -33139,6 +33199,7 @@ U+25397: xù U+25398: ruǎn U+2539B: guì U+2539C: rǒng +U+2539D: zuǎn U+2539F: máo U+253A1: qín U+253A2: cuàn @@ -33300,6 +33361,7 @@ U+25558: cháo,suǒ U+25559: hēi U+25562: cáo U+25563: zhé +U+25564: kē U+25566: gǔn U+25570: xū U+25571: péng,pēng @@ -33468,6 +33530,7 @@ U+25798: zùn U+2579A: rú U+2579B: duò U+2579C: jiàng +U+257A6: jiá U+257A7: héng U+257A9: bēng,hé U+257AA: mò,mǐ @@ -33477,6 +33540,7 @@ U+257B4: kù U+257B5: jiá U+257BA: zhuō U+257BC: xiū +U+257C2: lái U+257C3: hé U+257C5: qiāo U+257CD: fěi @@ -33698,6 +33762,7 @@ U+25A63: zhù U+25A64: hài U+25A71: fá U+25A72: hài +U+25A7A: liè U+25A80: bū U+25A81: pīng U+25A82: liè @@ -34486,6 +34551,7 @@ U+2634F: fén U+26352: tà U+26353: tún U+26355: yáng +U+26360: lì U+26366: duō U+26367: cī U+26369: gǔ @@ -35398,6 +35464,7 @@ U+26CFD: juàn U+26CFE: rú U+26CFF: còu U+26D00: zhī +U+26D07: qióng U+26D09: hú U+26D0A: yāng U+26D0C: jùn @@ -35537,6 +35604,7 @@ U+26EC0: tí,tái U+26EC1: dàn U+26EC3: huán U+26EC5: sà +U+26ED5: yǐn U+26F06: ruí U+26F07: wū U+26F08: jù @@ -35839,6 +35907,7 @@ U+27248: fóu,fú U+2724A: fǔ U+2724D: jí U+2724E: xuán +U+27250: shuāng U+27251: tài U+27253: dù U+27257: yuán @@ -36005,6 +36074,8 @@ U+273C6: dǒu U+273CB: qiū U+273D1: píng U+273D3: liú +U+273D6: pín +U+273D7: cī U+273E5: jié U+273E7: huì U+273EB: shà @@ -36051,6 +36122,7 @@ U+2744B: kuì U+2744C: yì U+2744D: huá U+2744E: cuì +U+2744F: zhú U+27450: yù U+27451: běng U+27452: tūn @@ -36310,6 +36382,7 @@ U+27723: diǎo U+27724: yī U+27726: sù U+27727: chuāng +U+2772D: shǔ U+27731: cuì U+27732: tuò U+27735: xiè @@ -36330,6 +36403,7 @@ U+27755: wéi U+27757: dūn U+27758: bào U+2775C: liáo +U+2775D: lǎi U+27764: sī U+2776A: biǎo U+2776B: xiè @@ -36345,6 +36419,7 @@ U+2778F: yú U+27790: zhuó U+27791: méng U+27792: hú +U+27794: jiè U+27795: liè U+2779D: jiē U+2779E: xióng @@ -36501,6 +36576,7 @@ U+2791F: xì U+27920: zhé U+27921: zhì U+27923: ná +U+27924: xī U+27928: jiān U+2792E: yáo U+2792F: guó,yuè @@ -36510,6 +36586,7 @@ U+27935: jīng U+2793C: jué U+2793D: yuè,jiàn U+27944: jí +U+27945: xī U+27946: sù U+27948: jiān U+2794A: kūn @@ -37003,7 +37080,9 @@ U+27E4A: yuè U+27E4C: zī U+27E50: luò U+27E51: guì +U+27E52: mǎi U+27E53: chéng +U+27E54: zhàng U+27E55: jū U+27E56: tiǎn U+27E57: wàn @@ -37099,7 +37178,7 @@ U+27F2A: chì U+27F2B: yù U+27F2C: cuī U+27F2D: sù,sōu,sǒu,qiù -U+27F2E: tì +U+27F2E: tì,tāng,tàng U+27F2F: shù,yú U+27F30: zhá U+27F31: fú,bí @@ -37136,6 +37215,7 @@ U+27F68: mì U+27F69: qiāng U+27F6B: liàn U+27F72: lì +U+27F75: zōng U+27F76: é U+27F77: sù U+27F78: jué,guì @@ -37194,6 +37274,7 @@ U+27FD7: dǔn U+27FD8: shēng U+27FD9: wán U+27FDA: fēn +U+27FDB: zōng U+27FDD: xī U+27FDE: zī U+27FE0: hù,dì @@ -37214,6 +37295,7 @@ U+27FFB: jú U+27FFC: guā U+27FFD: pǒ U+28000: nǐ,niǎn +U+28001: lóng U+28004: wǎ U+28005: yǎn U+28014: chǒu @@ -37351,6 +37433,7 @@ U+28153: zhǎ,dá U+28154: cán U+28157: bǐ U+28158: pèng +U+2815B: zhú U+2815D: chéng U+28163: qiáo U+28164: jī @@ -37899,6 +37982,7 @@ U+2874F: lí U+28750: shēn U+28751: guàn U+28753: piáo +U+28755: gài U+28756: lí U+28758: hǔ U+2875B: tú @@ -37983,6 +38067,7 @@ U+28821: suān U+28824: sōng,nóng U+28825: wéi U+28826: xiáo +U+28828: jì U+2882C: hào U+2882D: yǎn U+28836: yí @@ -38002,6 +38087,7 @@ U+28850: zhì U+28851: chōu,chóu U+28852: táo U+28857: zhà +U+28859: dān U+2885E: miǎn U+28861: wǔ U+28862: yǐn @@ -38016,6 +38102,7 @@ U+28871: kù,dǐng U+28872: chōu U+28874: yǒu U+28878: zhuó +U+2887A: kuì U+2887B: sōu U+28882: yìn U+28885: zuì @@ -38139,6 +38226,7 @@ U+289E8: ruì,zhuì U+289EA: sào U+289EB: zì U+289ED: zhèng +U+289EE: mí U+289F0: zú U+289F1: qū U+289F3: chì @@ -39402,6 +39490,7 @@ U+295DA: yóu U+295DC: wèi U+295DD: huǐ U+295DE: sà +U+295E1: chóu U+295E2: hòng U+295E3: sōu U+295E4: hàn @@ -39691,6 +39780,7 @@ U+2984E: xiū U+29853: wěng U+29854: hài U+29855: péng +U+29856: yǐ U+2985D: tán U+2985F: bié U+29860: xiāng @@ -39869,7 +39959,9 @@ U+299F2: dòng U+299F3: zhōu U+299F4: quān U+299F5: jié +U+299F8: lì U+299FA: jú +U+299FB: wǎn U+299FC: bēn U+299FF: bī U+29A00: gé @@ -39920,6 +40012,7 @@ U+29A3E: wá U+29A40: nǎo U+29A44: yán U+29A45: tóng +U+29A48: kuài U+29A4B: xíng,jìng U+29A4C: gǔn U+29A4D: pīng @@ -40028,6 +40121,7 @@ U+29B3A: zhǐ U+29B3B: duǒ U+29B3C: cōng U+29B3D: bǎo +U+29B3E: yā U+29B47: lí U+29B4A: jú U+29B4B: wén,kūn @@ -40053,6 +40147,7 @@ U+29B6F: yā U+29B72: dí U+29B73: huō U+29B77: mín +U+29B79: sān U+29B7A: fù U+29B7C: bǎo U+29B7D: kè @@ -40131,6 +40226,7 @@ U+29C2A: yù U+29C2C: guō,wāi U+29C2D: guō U+29C2F: hú +U+29C30: jìng U+29C32: lì,fèi U+29C33: xié U+29C34: ér @@ -40237,6 +40333,7 @@ U+29D03: jí U+29D07: shài U+29D08: mèi U+29D09: yùn +U+29D0C: gān U+29D0D: shòu U+29D10: lǜ U+29D11: yòu @@ -40471,6 +40568,7 @@ U+29F88: shū U+29F8A: xū U+29F8B: zhòng U+29F8C: kāng +U+29F8E: yōu U+29F92: xiāo U+29F93: xiāo U+29F94: cì @@ -40920,6 +41018,7 @@ U+2A388: yì U+2A389: xiàn U+2A38A: chǎo U+2A38B: tiè +U+2A38C: lái U+2A392: shuò U+2A394: mǐ U+2A397: mí @@ -41016,6 +41115,7 @@ U+2A440: huī U+2A442: chà U+2A443: shān U+2A444: lú +U+2A445: huì U+2A446: yù U+2A448: yàn U+2A449: wǎn @@ -41304,326 +41404,1519 @@ U+2A6C8: líng U+2A6CA: yín U+2A6CE: pì U+2A6D2: lián +U+2A70E: qiào U+2A79D: duó +U+2A7CE: jiān U+2A7DD: jì,jī +U+2A800: kū +U+2A80F: yǎn +U+2A81F: zhěn +U+2A821: sā U+2A833: chē +U+2A835: lún +U+2A838: hù +U+2A83D: dāng +U+2A840: qiáo +U+2A843: mǎi U+2A848: bái +U+2A84B: yǎn U+2A84F: zhān +U+2A85B: hé +U+2A85E: kuì +U+2A87A: lài +U+2A88C: lán +U+2A892: shù +U+2A895: chuò +U+2A8A0: lán U+2A8AE: luán +U+2A8C6: dōng +U+2A8D2: huán U+2A8DD: zào U+2A8FB: lóu U+2A917: liào +U+2A91A: lín +U+2A960: réng +U+2A96B: shuāng +U+2A970: níng +U+2A97F: dú +U+2A9C0: yīng +U+2A9D8: jù U+2AA07: tuí U+2AA0A: sóng U+2AA17: juē U+2AA27: lún +U+2AA29: yà U+2AA30: qū U+2AA36: shē +U+2AA37: yán +U+2AA39: tuó +U+2AA47: lǒu +U+2AA4E: yǐng U+2AA58: yǎn +U+2AA5B: niè +U+2AA78: fén +U+2AA91: qiào U+2AA9D: yōng +U+2AA9E: qiáng +U+2AAB4: zé +U+2AACC: lòng +U+2AAE1: lì +U+2AAF7: chóu +U+2AAF8: què +U+2AAFA: xiān +U+2AB1A: pīn +U+2AB5D: wěi +U+2AB62: chuāng +U+2AB67: ér +U+2AB6F: guài +U+2AB75: liǎng +U+2AB7E: xiàn +U+2AB83: cán U+2AB8B: xiāo +U+2AB96: juē +U+2ABB6: là +U+2ABCB: shǎn +U+2AC36: fèi +U+2AC65: fèn U+2AC77: bèi +U+2AC8E: jī +U+2AC94: lì +U+2AC9B: jì +U+2ACAE: fèi +U+2ACCD: shè +U+2AD19: cuán +U+2AD2F: ē +U+2AD51: xiāo +U+2AD63: xì +U+2AD71: lì +U+2AD84: shí +U+2AD92: luó +U+2ADCD: qìng U+2ADFD: zàn +U+2AE15: xì +U+2AE29: huī +U+2AE40: yì +U+2AE73: zhú +U+2AE79: tà +U+2AEA3: dào +U+2AEAD: qiáo +U+2AEB7: cāng U+2AEB9: nǔ +U+2AEBB: nóng +U+2AEBD: yìn U+2AED0: cōng +U+2AEE8: dú +U+2AEF2: kūn +U+2AEFA: guī +U+2AF0B: bǐ U+2AF47: kē +U+2AF6A: zhàng +U+2AF6D: jiàn +U+2AF6E: lú +U+2AF74: shè +U+2AF77: yì U+2AF94: xǔ U+2AFA2: xiàn U+2AFA6: lú +U+2AFB8: pín +U+2AFCA: yà +U+2AFEB: pào +U+2B00C: shī +U+2B013: mǐn +U+2B028: jiǎn +U+2B02C: chú +U+2B02E: dǎng +U+2B042: bà +U+2B05F: zhuǎn U+2B061: lì +U+2B072: wú +U+2B073: wěi +U+2B077: shuāng +U+2B083: mǐn +U+2B086: guì U+2B088: fèi +U+2B096: ài U+2B099: sù +U+2B0BF: sà +U+2B0D7: jiū U+2B0DC: kòu +U+2B119: jiū +U+2B11A: hù U+2B11B: jīn +U+2B11C: mào +U+2B11D: diào +U+2B11E: bì U+2B11F: shī +U+2B120: huán +U+2B121: dòng +U+2B122: fú +U+2B123: nǒng U+2B124: da U+2B125: lí,xǐ +U+2B126: jié U+2B127: yán U+2B128: chī +U+2B129: fán +U+2B12A: sāng +U+2B12B: lì +U+2B12C: xiè +U+2B12D: fù +U+2B12E: tīng +U+2B130: bāng +U+2B131: sè +U+2B132: mù +U+2B133: xǐ +U+2B134: lǜ +U+2B135: bēng +U+2B136: qiǎng U+2B137: yì U+2B138: xūn +U+2B139: zuī +U+2B145: jǐ +U+2B157: fén +U+2B16D: pīn +U+2B17C: lián +U+2B18F: nǐ U+2B1E6: yìng +U+2B1EA: gāng U+2B1ED: wěi +U+2B1F4: chú +U+2B1FD: nóng +U+2B209: jiān +U+2B20E: qíng +U+2B21F: chén U+2B230: qià +U+2B235: jué U+2B241: ài +U+2B244: shǔ +U+2B2AA: cháng +U+2B2AE: lú +U+2B2B1: jiǎn +U+2B2B8: jiǎo +U+2B2BB: xún +U+2B2C7: xiāo U+2B2CC: tè U+2B2D0: gǒng +U+2B2F2: ōu +U+2B2F7: zé +U+2B2F9: ráo +U+2B2FB: guì U+2B300: jī +U+2B307: zhě +U+2B30B: shù U+2B328: luó +U+2B32A: mì +U+2B32B: liàn +U+2B32D: wéi +U+2B32F: jiào +U+2B350: pín U+2B359: yì +U+2B35A: yāo +U+2B35B: fēn +U+2B35C: qǔ +U+2B35E: hū U+2B35F: yí +U+2B360: yuǎn +U+2B361: tuó U+2B362: náo U+2B363: tóng +U+2B364: jiǎo +U+2B365: tiǎo +U+2B366: nóng +U+2B367: chī +U+2B368: zhèn +U+2B369: huà +U+2B36A: jì +U+2B36B: yàn +U+2B36C: wǎng +U+2B36E: qū U+2B36F: xián U+2B370: xǐ +U+2B371: zhuān U+2B372: xiǎo +U+2B373: zhòng +U+2B374: lóu +U+2B375: áo +U+2B376: zhǐ +U+2B377: kuì +U+2B378: shàn +U+2B379: jiē +U+2B37A: shǎ U+2B37B: xī +U+2B37C: zhòng U+2B37D: xuān +U+2B37E: níng +U+2B37F: jiǎn +U+2B386: zōng +U+2B38C: jù +U+2B3A6: lòng +U+2B3A7: gòu +U+2B3A8: liàn +U+2B3A9: chēn +U+2B3AA: chèn U+2B3AB: dàn U+2B3AC: gàn +U+2B3AD: chēng +U+2B3B1: lì +U+2B3B3: bì +U+2B3BA: cān +U+2B3C3: cháng +U+2B3C6: bō U+2B3CB: juē,qiāo +U+2B3CC: lái U+2B3D0: zàn +U+2B3D1: luò +U+2B3D5: qīng +U+2B3E8: xiān U+2B404: yuè +U+2B405: shān U+2B406: kuài +U+2B407: nà +U+2B408: bá U+2B409: líng +U+2B40A: fàn +U+2B40B: zhì +U+2B40C: píng +U+2B40D: tián +U+2B40E: yǐ +U+2B40F: zhōu U+2B410: ní U+2B411: guǎn +U+2B412: hōng U+2B413: bù,róu +U+2B414: kē +U+2B415: wèi U+2B416: jiāo +U+2B417: bú U+2B418: kǎn +U+2B419: léi +U+2B437: lóu +U+2B458: zhuān U+2B461: méng +U+2B477: yàn U+2B4B6: hán +U+2B4E5: bā +U+2B4E6: zǐ U+2B4E7: fū +U+2B4E8: guō U+2B4E9: cōng +U+2B4EA: qiān +U+2B4EC: zā +U+2B4ED: yāng +U+2B4EE: liú U+2B4EF: jī +U+2B4F0: xù +U+2B4F1: qiáo +U+2B4F2: jūn +U+2B4F4: máo +U+2B4F5: shèn U+2B4F6: xuān +U+2B4F8: wàn U+2B4F9: jī +U+2B4FA: lǔ +U+2B4FB: niè +U+2B4FC: hé +U+2B4FD: cóng +U+2B4FE: yù +U+2B500: liàn +U+2B501: fén +U+2B502: dī +U+2B504: róu +U+2B505: suǒ +U+2B506: bà +U+2B507: bī +U+2B508: shuò +U+2B50A: xiù +U+2B50B: kuǎn +U+2B50C: sǎn U+2B50D: fán U+2B50E: jué +U+2B50F: chán +U+2B511: zhǎn +U+2B514: xī +U+2B516: nóng +U+2B52D: kāi +U+2B52F: dòu +U+2B530: biàn +U+2B531: lán +U+2B532: xiāo +U+2B534: lìn +U+2B535: zhì U+2B536: niè +U+2B565: yǔn U+2B583: huì +U+2B585: lóng +U+2B587: xuē +U+2B591: qín +U+2B592: bì +U+2B593: bǐng U+2B594: bài +U+2B595: gōu +U+2B596: zhèng +U+2B5AA: kū +U+2B5AB: zhěn +U+2B5AC: zhěn U+2B5AE: yǐ U+2B5AF: fǔ +U+2B5B0: duǒ +U+2B5B1: gěn +U+2B5B2: hùn U+2B5B3: yūn +U+2B5B4: zé +U+2B5B5: yuè +U+2B5B6: qiān +U+2B5B8: yuàn +U+2B5B9: cù +U+2B5BA: fán +U+2B5C7: yù +U+2B5C8: bó +U+2B5C9: bèng +U+2B5CA: àn +U+2B5CB: sè +U+2B5DA: jiǎn +U+2B5DE: zhān +U+2B5DF: yuán U+2B5E0: zhāng +U+2B5E2: zuò +U+2B5E3: bì +U+2B5E4: shì +U+2B5E5: yùn U+2B5E6: bù U+2B5E7: sù +U+2B5E8: láng U+2B5E9: luó +U+2B5EA: wèi U+2B5EB: hú +U+2B5EC: nuǎn +U+2B5ED: wèi U+2B5EE: huáng +U+2B5EF: hóu U+2B5F0: duī +U+2B5F1: lián +U+2B5F3: qiǎng U+2B5F4: zhān +U+2B5F5: xiǎng +U+2B61B: hàn U+2B61C: wén U+2B61D: jué U+2B61E: tuó +U+2B61F: pǒ +U+2B620: zhì +U+2B621: jiōng U+2B623: hàn U+2B624: ái +U+2B625: kūn U+2B626: táo U+2B627: lù U+2B628: tí +U+2B629: huáng U+2B62A: yuán +U+2B62B: yàn U+2B62C: xí U+2B62D: shuāng +U+2B62E: zhé +U+2B62F: céng +U+2B630: zhān +U+2B631: xí +U+2B63D: kuò U+2B688: xù U+2B689: hóng +U+2B68A: yáng +U+2B68B: zhuān +U+2B68C: shā +U+2B68D: fén +U+2B68E: bǐng +U+2B68F: qiáo +U+2B690: yāng +U+2B691: bì U+2B692: fú +U+2B693: liè U+2B694: huí U+2B695: shī U+2B696: cǐ +U+2B697: gé U+2B699: pū +U+2B69A: zhé +U+2B69B: tuō +U+2B69C: guī +U+2B69D: huà +U+2B69E: lí +U+2B6A1: zhǒu +U+2B6A2: yǎn +U+2B6A3: biān +U+2B6A4: zī U+2B6A5: xiā +U+2B6A6: yóng +U+2B6A7: qiú +U+2B6A8: bù +U+2B6AA: yù +U+2B6AB: sāo U+2B6AD: liè +U+2B6DA: jiān U+2B6DB: zhī +U+2B6DC: fǒu +U+2B6DD: huān U+2B6DE: jué +U+2B6DF: lóng +U+2B6E0: zhá +U+2B6E1: fú U+2B6E2: níng U+2B6E3: yù +U+2B6E4: jiā +U+2B6E5: jiá +U+2B6E6: wū +U+2B6E8: pí +U+2B6E9: yàn +U+2B6EA: rú +U+2B6EB: yuán +U+2B6EC: tú U+2B6ED: kuáng +U+2B6EE: biē +U+2B6EF: fǎng +U+2B6F0: qí +U+2B6F1: zhuó +U+2B6F2: diāo U+2B6F3: fú +U+2B6F4: tí +U+2B6F5: qū U+2B6F6: chì +U+2B6F7: hú U+2B6F8: tí -U+2B728: bà +U+2B6FA: hóu +U+2B6FB: duó +U+2B6FC: kuí +U+2B6FD: lì +U+2B6FE: chú +U+2B700: chén +U+2B701: bì +U+2B702: zhāng +U+2B703: yín +U+2B704: zūn +U+2B705: huán +U+2B70A: wēn +U+2B711: fēng +U+2B712: tuō +U+2B714: pí +U+2B715: kū +U+2B719: yìng +U+2B71F: cù +U+2B728: bā,bà +U+2B729: niè +U+2B72A: yǎo U+2B72C: zé +U+2B72D: chǔ +U+2B72E: yǎn +U+2B72F: jiè +U+2B730: yà +U+2B732: lóng +U+2B733: rán U+2B737: shòu U+2B738: huà +U+2B748: hún +U+2B74B: shù +U+2B75C: zǎ +U+2B766: bèi +U+2B767: shù +U+2B768: huī +U+2B769: chóu +U+2B76A: gòng +U+2B76B: lái +U+2B76C: kuì +U+2B76D: yīng +U+2B76E: lǎn +U+2B775: dǎo +U+2B785: xún +U+2B797: mǐn +U+2B79A: zī +U+2B79B: zhū +U+2B79D: jué +U+2B7A0: líng +U+2B7A1: róng +U+2B7A2: zhì +U+2B7A3: chǎn +U+2B7A5: bèi +U+2B7A6: jì +U+2B7A7: láo +U+2B7A8: lán U+2B7A9: mén +U+2B7B7: kuài +U+2B7C3: rèn +U+2B7C4: hóng U+2B7C5: liáng +U+2B7C6: chóng +U+2B7D1: líng +U+2B7D5: jī +U+2B7DE: zhǐ +U+2B7DF: pàn +U+2B7E0: chǎn +U+2B7E1: cóng +U+2B7E2: tán +U+2B7E4: tuó +U+2B7E5: kēng U+2B7E6: suì +U+2B7EB: lì +U+2B7EC: zhì +U+2B7F2: qiú +U+2B7F3: sì +U+2B7F4: fēn +U+2B7F5: yǔn +U+2B7F6: shàn U+2B7F7: lì +U+2B7F8: shì U+2B7F9: hóng +U+2B7FA: kāi +U+2B7FB: zhōu U+2B7FC: dá +U+2B7FD: zú +U+2B7FE: zú +U+2B7FF: chā +U+2B800: cù +U+2B801: hé +U+2B802: líng +U+2B805: gé U+2B806: kuǐ +U+2B807: xín +U+2B808: bī U+2B80A: xuán +U+2B80B: lù +U+2B80C: bàng +U+2B80F: mù +U+2B810: hān +U+2B811: sōu +U+2B812: zhāng U+2B81C: ní U+2B825: lòu U+2B826: suǒ U+2B851: yīn +U+2B86C: wō +U+2B876: dàn U+2B892: lǒng +U+2B894: guì +U+2B898: duì +U+2B899: dòng +U+2B8AA: liàn +U+2B8AC: láo +U+2B8AD: wěi U+2B8B8: dàn +U+2B8B9: chòng +U+2B8BA: cān +U+2B8C9: lán +U+2B8CA: ài +U+2B8DB: qiān +U+2B8EB: sú +U+2B938: mào +U+2B93D: gǎn +U+2B94D: yán +U+2B973: lì +U+2B975: lì +U+2B97A: yè +U+2B97C: yā +U+2B981: suǒ +U+2B985: xiào +U+2B989: lóu +U+2B98C: jué +U+2B9A9: liǎng +U+2B9B3: jué U+2B9EF: xiè +U+2B9FF: dú +U+2BA06: xiá +U+2BA55: hōng +U+2BA5A: gāng +U+2BA5B: cóng +U+2BA64: yè +U+2BA65: huò +U+2BA69: chū +U+2BA6B: yè +U+2BA6F: hái +U+2BA7A: jìn +U+2BA80: guō +U+2BA81: lài +U+2BA82: yán +U+2BA83: lì +U+2BA84: jiān +U+2BA98: ōu +U+2BA9A: zàn +U+2BAA7: jiāo +U+2BAAA: zhì +U+2BABA: tì +U+2BABD: jìn U+2BAC7: ě,è U+2BAE6: dàn +U+2BAF5: lǜ +U+2BAFE: pín +U+2BB10: sǎi +U+2BB19: gùn +U+2BB1F: dié +U+2BB5E: zhuān U+2BB5F: ōu,qū U+2BB62: lǔn +U+2BB68: fá +U+2BB6A: qiāo +U+2BB6E: cè +U+2BB6F: xún +U+2BB72: yán U+2BB7C: láo U+2BB83: shàn +U+2BB85: chěn +U+2BB9C: yín U+2BBAC: jiàng +U+2BBD2: nóng +U+2BBE5: huò +U+2BBF6: yūn U+2BC02: duǒ +U+2BC0D: wéi U+2BC1B: xíng U+2BC21: huà +U+2BC22: huì +U+2BC28: mái +U+2BC30: xiān +U+2BC39: guī +U+2BC55: làn +U+2BC7F: qiā +U+2BC97: zàn +U+2BCB8: fēng +U+2BCC3: yā U+2BD52: tuí U+2BD58: bī +U+2BD75: yáng U+2BD77: lì +U+2BD78: kēng U+2BD87: dié +U+2BD8A: lǎn +U+2BD95: yíng +U+2BDB2: kēng +U+2BDC5: kūn +U+2BDC8: xū +U+2BDC9: xiǎn +U+2BDCC: lán +U+2BDEC: lái +U+2BDEE: yìn U+2BDF7: xīn +U+2BDF9: lóu +U+2BDFE: yǐng U+2BE29: kōu +U+2BE6E: yǔ +U+2BE74: wěi +U+2BE7C: dōng +U+2BE7D: shāng +U+2BE81: qiè +U+2BE82: qiè +U+2BE86: huá +U+2BE8A: qí +U+2BE8C: gōng +U+2BE93: mèn +U+2BE98: lào +U+2BEAA: yǎng +U+2BEB7: nán +U+2BEC1: xì U+2BF1B: diào U+2BF1D: chōu +U+2BF23: piē +U+2BF24: zè +U+2BF25: hāi +U+2BF27: huá +U+2BF2B: bì +U+2BF2E: nǎng +U+2BF31: gāng +U+2BF35: lǔ +U+2BF36: suǒ +U+2BF40: nà +U+2BF41: liǎn +U+2BF47: huò +U+2BF4A: jiǎo +U+2BF4B: luó +U+2BF50: jiān +U+2BF59: káng +U+2BF63: jiāng +U+2BF65: qiān +U+2BF67: fèi U+2BF6E: dǎng +U+2BF72: niǎo +U+2BF73: bì +U+2BF81: yǎn +U+2BF83: sù +U+2BFB2: lǐ +U+2BFD7: shǔ +U+2C025: huò U+2C029: wěi U+2C02A: xiàn +U+2C02E: duì +U+2C031: kǎi U+2C037: shāng U+2C060: xiǎng +U+2C075: ōu +U+2C07A: yà +U+2C07D: sǔn +U+2C080: rùn +U+2C082: luó +U+2C085: dān +U+2C089: shèng +U+2C0A0: mó U+2C0A9: jiā +U+2C0AE: shā +U+2C0B0: nóng +U+2C0C0: guī U+2C0CA: zhì +U+2C0D8: èr +U+2C0E6: jiān +U+2C0EB: xiǎn +U+2C0EE: fèi +U+2C0F2: lín +U+2C0F3: yán U+2C129: yǐn +U+2C162: xià +U+2C165: guā +U+2C16B: pēn U+2C19B: nǎo +U+2C1A6: jī U+2C1AE: kòu +U+2C1BE: mào +U+2C1C3: jiàn +U+2C1C4: bīn +U+2C1C7: dié U+2C1D5: wàn U+2C1D8: tuán U+2C1D9: pèi,bèi +U+2C1F0: mèn U+2C1F9: guó +U+2C201: tān +U+2C215: fèi +U+2C21C: yíng U+2C227: sè +U+2C231: pō +U+2C23E: huá +U+2C242: fèn +U+2C247: shà +U+2C24B: wěi +U+2C260: lán U+2C267: hán U+2C27C: ōu,ǒu +U+2C282: chǎo U+2C288: xún +U+2C289: dá +U+2C28D: chóu +U+2C28E: gòng +U+2C296: měi +U+2C297: jiān U+2C2A4: chǎn +U+2C2A6: shǎn +U+2C2B5: tǎng +U+2C2B6: làn +U+2C2BA: wēi +U+2C2BE: rán +U+2C2C3: liàn +U+2C2CD: lài U+2C317: hé +U+2C31D: chú +U+2C32E: yào +U+2C337: shān U+2C359: zhǎn U+2C35B: lì U+2C361: dàng U+2C364: xún U+2C386: yíng +U+2C391: làn +U+2C3A7: cóng +U+2C3DC: mà +U+2C3DF: jì +U+2C3E6: fèi +U+2C3EB: wén +U+2C3EE: guō +U+2C3F7: fèn U+2C41A: hǎi +U+2C420: yáng +U+2C446: gùn +U+2C447: chàng +U+2C44D: chǒu +U+2C44F: yǐng +U+2C452: kōu +U+2C453: yè U+2C454: zhòng -U+2C457: jiàn +U+2C455: hùn +U+2C457: xián,jiàn +U+2C459: mài U+2C461: chǒu +U+2C467: mián +U+2C486: yè +U+2C487: làn U+2C488: què +U+2C48D: yǔn +U+2C48E: kēng U+2C494: gěng +U+2C495: sù U+2C497: lán U+2C498: xìn +U+2C4E0: jǐ U+2C4EB: qiū +U+2C4F1: bèn +U+2C4F8: shài U+2C4FC: tuí +U+2C52F: ōu U+2C542: gōng,lǒng +U+2C544: shī +U+2C54A: jiàn +U+2C55B: kū +U+2C566: ōu +U+2C56C: lǐng +U+2C583: zuǎn +U+2C591: xiàn +U+2C596: chōu +U+2C598: shì +U+2C5A0: lǒu +U+2C5AE: xiàn U+2C613: xún +U+2C614: miè +U+2C615: qiú U+2C616: jiǎn U+2C618: dǎn +U+2C619: yuè +U+2C61A: wà +U+2C61B: qū U+2C61C: zhàn U+2C61D: zhěn +U+2C61E: diǎo +U+2C61F: xiàn +U+2C620: gǎi U+2C621: yīn U+2C622: kuàng +U+2C624: lǚ +U+2C625: quán +U+2C626: pài +U+2C627: jì +U+2C628: zhī U+2C629: tīng +U+2C62A: liǎn U+2C62B: huán U+2C62C: qiàn U+2C62D: lín,chēn +U+2C62E: mì U+2C62F: zhǔn +U+2C630: ruǎn +U+2C631: yāo +U+2C632: zhǎ +U+2C633: xū U+2C635: gēng +U+2C636: qiū +U+2C637: cuō +U+2C638: nì +U+2C63A: tào +U+2C63B: xiá +U+2C63C: suì +U+2C63D: zhì +U+2C63E: shuàng +U+2C641: yǎn U+2C642: yǎn +U+2C643: qiǎn +U+2C645: zhǔ U+2C646: fān,fán +U+2C647: rán +U+2C648: lín +U+2C649: dàn U+2C64A: mò U+2C64B: xiāng +U+2C64E: lú +U+2C65D: bì +U+2C66D: wěi +U+2C684: sù U+2C6F8: niē +U+2C6F9: dàng +U+2C6FC: yú +U+2C714: dá +U+2C725: lán +U+2C727: fà +U+2C728: huì U+2C72C: màn U+2C72F: liǎng +U+2C738: huái +U+2C73E: xián +U+2C73F: guó +U+2C741: yú +U+2C743: láo +U+2C74A: sī +U+2C74B: jié +U+2C760: zéi +U+2C76F: pín +U+2C774: rán +U+2C78B: jiàn +U+2C795: lán +U+2C798: tuī U+2C79F: pín +U+2C7A3: sēn +U+2C7AB: làn U+2C7C1: yì +U+2C7EA: xì +U+2C7FA: cóng U+2C7FD: dōng +U+2C803: bì +U+2C805: dāng +U+2C808: gòng +U+2C810: sǐ +U+2C820: zéi +U+2C837: tuó +U+2C847: niǎo +U+2C84D: diāo +U+2C84E: zhòu +U+2C852: jiá +U+2C853: diē +U+2C854: shī +U+2C855: shā +U+2C860: luò +U+2C877: zàn +U+2C87B: chǎn +U+2C887: hā +U+2C888: dòng +U+2C889: cī U+2C88A: sì +U+2C88B: tiào +U+2C88C: zhì +U+2C88E: dǔ +U+2C88F: dān +U+2C890: qì +U+2C891: yǐng +U+2C892: míng +U+2C894: dēng +U+2C8AA: zhōu +U+2C8AF: yīng +U+2C8B3: cān U+2C8C0: wèi U+2C8D9: xū +U+2C8DA: kǒu +U+2C8DB: zhì +U+2C8DC: hào +U+2C8DD: jùn U+2C8DE: zhǔ +U+2C8DF: tǒu +U+2C8E0: gàn U+2C8E1: jiàn +U+2C8E2: tā +U+2C8E3: zhù +U+2C8E4: juǎn +U+2C8E5: tāo +U+2C8E6: ào +U+2C8E7: fèi +U+2C8E8: yǎ +U+2C8E9: wēi +U+2C8EA: huì +U+2C8EC: huì +U+2C8ED: dǎng +U+2C8EE: míng +U+2C8EF: rén +U+2C8F0: huì +U+2C8F1: chóu +U+2C8F2: bìng U+2C8F3: hěn +U+2C8F6: zhà +U+2C8F7: sù +U+2C8F8: hàn +U+2C8F9: cù +U+2C8FB: tìng +U+2C8FC: lǎng +U+2C8FD: lián +U+2C8FE: zhā +U+2C8FF: láo +U+2C900: xià +U+2C901: táo +U+2C902: tàn +U+2C904: càn +U+2C905: jí +U+2C906: gùn U+2C907: yīn +U+2C909: huì U+2C90A: shì +U+2C90B: còng +U+2C90C: chā +U+2C90D: huáng +U+2C90E: xuān +U+2C90F: duó U+2C910: gē +U+2C911: gé +U+2C912: gé +U+2C913: chī +U+2C915: tà +U+2C916: gùn +U+2C918: chì +U+2C919: hū +U+2C91A: yīng +U+2C91B: chǎn +U+2C91C: zhā U+2C91D: huì +U+2C91E: yì +U+2C91F: liáo +U+2C920: yán +U+2C921: ào +U+2C922: zǔn +U+2C923: duì +U+2C924: zēng U+2C925: zhuàn +U+2C926: yí +U+2C928: zào +U+2C929: ài +U+2C92A: tà +U+2C92B: háo +U+2C92C: tuān +U+2C92D: huì +U+2C92E: zàn +U+2C92F: jiǎn U+2C930: huān +U+2C931: tuí +U+2C944: zhé +U+2C948: mào +U+2C973: chuàng +U+2C974: hān +U+2C975: yì +U+2C976: bì +U+2C977: zhèng +U+2C978: chuàn +U+2C97A: yǎn +U+2C97B: ruǎn +U+2C97C: zhuàn +U+2C97D: hòu +U+2C97E: làn +U+2C97F: zhàn +U+2C985: tàng +U+2C986: tuán +U+2C9A3: lì +U+2C9A5: bèi +U+2C9A7: lún +U+2C9A9: zǒu +U+2C9AB: duò +U+2C9AF: xié +U+2C9B4: bìng +U+2C9B5: zhāng +U+2C9BB: guàn U+2C9C0: qiāng,qiàng +U+2C9C3: téng +U+2C9D1: niǎn +U+2C9DA: diān +U+2C9E2: lǒng +U+2C9E4: lào +U+2C9E9: guó +U+2CA01: máo U+2CA02: qí +U+2CA04: yǎng +U+2CA05: niǎn +U+2CA06: gǒng +U+2CA07: kǎi +U+2CA08: wǎn +U+2CA09: tián +U+2CA0B: zàng +U+2CA0C: pì +U+2CA0D: gé U+2CA0E: yóu +U+2CA0F: xǔ +U+2CA10: huì +U+2CA11: sǎng +U+2CA12: guāng +U+2CA13: cháo +U+2CA14: pì +U+2CA4E: huò U+2CA7D: xún +U+2CA7E: lái +U+2CA8D: mào +U+2CAA7: yù +U+2CAA8: zhǎn U+2CAA9: nóng +U+2CABA: guàng +U+2CB07: zhē +U+2CB27: hàn +U+2CB28: huá U+2CB29: yì +U+2CB2A: kòu U+2CB2B: pī +U+2CB2C: wéi U+2CB2D: lún U+2CB2E: chǎng +U+2CB2F: rén +U+2CB30: qiāng U+2CB31: jīn -U+2CB32: pì +U+2CB32: zhāo,pì +U+2CB33: qí +U+2CB35: chén +U+2CB37: shí U+2CB38: shù U+2CB39: shén U+2CB3A: chú,jǔ U+2CB3B: lú +U+2CB3D: zuó +U+2CB3E: níng U+2CB3F: zhāo +U+2CB40: sì U+2CB41: mǔ +U+2CB42: hóng +U+2CB43: pī +U+2CB45: xiàng +U+2CB46: duǒ +U+2CB47: guó +U+2CB48: chá +U+2CB49: jī U+2CB4A: dù +U+2CB4C: huá +U+2CB4D: hàn U+2CB4E: hóng +U+2CB4F: yáng +U+2CB51: zì +U+2CB53: tiǎn +U+2CB54: chuò +U+2CB55: diāo +U+2CB56: péng +U+2CB57: nèi U+2CB5A: chún,duì U+2CB5B: bō +U+2CB5C: lèi +U+2CB5D: sǎn +U+2CB5E: dā +U+2CB5F: còu +U+2CB60: chǎ +U+2CB61: zhāo +U+2CB62: guì U+2CB63: xiàn U+2CB64: hóu +U+2CB65: zǒng +U+2CB66: nòu +U+2CB68: chuí U+2CB69: wēng +U+2CB6A: xiá +U+2CB6B: zhǎn U+2CB6C: wèi,huì +U+2CB6D: qī U+2CB6F: piě +U+2CB70: yí U+2CB72: suǒ U+2CB73: xǐ -U+2CB74: jiē +U+2CB74: jié,jiē U+2CB76: hēi +U+2CB77: chēng U+2CB78: lín +U+2CB7A: biāo +U+2CB7B: jiǎo U+2CB7C: suì +U+2CB7D: bì +U+2CB7F: jī +U+2CB81: bó +U+2CB83: zuǎn +U+2CB84: jiào +U+2CB98: huò +U+2CB9F: lìn +U+2CBA0: xiǎ +U+2CBA1: mǎ +U+2CBA2: hòng +U+2CBA3: kuāng U+2CBA4: gé,hé +U+2CBA5: chù +U+2CBA8: huò +U+2CBA9: qì +U+2CBAA: shā +U+2CBAD: kuī +U+2CBAF: què +U+2CBB0: ē U+2CBB1: yīn +U+2CBB2: yǎo +U+2CBB3: bǎn +U+2CBB4: àn +U+2CBB5: xiàn +U+2CBB9: dēng +U+2CBBA: mà +U+2CBBB: wù U+2CBBF: qí,gài U+2CBC0: jī U+2CBC5: xiá U+2CBCE: tuí U+2CC03: mù +U+2CC21: luǒ +U+2CC23: jiǎn +U+2CC24: zhòu +U+2CC25: liǎng +U+2CC31: jiān +U+2CC33: dié +U+2CC36: suì +U+2CC37: chān +U+2CC38: yù +U+2CC3A: yè +U+2CC53: qiú +U+2CC54: yòu +U+2CC55: mò U+2CC56: dí +U+2CC57: dān +U+2CC59: pàn +U+2CC5B: zhuàn +U+2CC5C: lèi +U+2CC5D: gěn U+2CC5F: wěi +U+2CC60: guā +U+2CC62: wèn +U+2CC63: cén +U+2CC65: hōng U+2CC66: qī +U+2CC6A: yí +U+2CC6B: dìng +U+2CC6C: sǎn +U+2CC6D: pò +U+2CC6E: āo +U+2CC6F: shǒu +U+2CC70: pī +U+2CC71: zhǎn +U+2CC73: yù +U+2CC75: wěi +U+2CC77: xuè +U+2CC78: xuè +U+2CC7C: kǎi +U+2CC7D: xuàn +U+2CC7F: guó +U+2CC80: hū +U+2CC85: liáo +U+2CC86: yù +U+2CC95: jiǎn U+2CC9C: chēng +U+2CCA6: sì +U+2CCAA: sōng +U+2CCAB: niǎn +U+2CCAD: jiě +U+2CCAE: dī +U+2CCAF: gōu +U+2CCB0: shāng +U+2CCB2: níng +U+2CCB3: yì +U+2CCB4: mǎn +U+2CCB6: cí +U+2CCB7: èn +U+2CCB8: guò +U+2CCB9: māng +U+2CCBA: bǎo +U+2CCBB: tí +U+2CCBC: yè +U+2CCBE: hú +U+2CCC0: yì +U+2CCC1: yáo +U+2CCC2: zhuì +U+2CCC3: bì +U+2CCC5: suǐ +U+2CCC6: jiān +U+2CCC9: dā +U+2CCCA: táo +U+2CCCB: xī +U+2CCCC: qiǔ +U+2CCCD: táng +U+2CCD0: chóng +U+2CCD1: yǐng +U+2CCD2: dèng +U+2CCD3: yōng +U+2CCD4: mó +U+2CCDF: fén U+2CCF2: chǎn U+2CCF3: fān +U+2CCF4: pēng U+2CCF5: pī U+2CCF6: jiōng +U+2CCF7: guā +U+2CCF8: wò +U+2CCFB: kuāng U+2CCFD: shēn +U+2CCFE: chí U+2CCFF: tú +U+2CD00: liáng +U+2CD01: àn U+2CD02: fēi U+2CD03: huō +U+2CD05: péng +U+2CD06: qí +U+2CD07: chì U+2CD0A: lín +U+2CD0B: tiě +U+2CD0C: méng +U+2CD0D: biāo +U+2CD0E: tuó +U+2CD0F: jiǎn U+2CD10: huān +U+2CD28: nào +U+2CD29: nóng U+2CD68: mì +U+2CD80: jiē +U+2CD81: rén +U+2CD82: dù +U+2CD84: diào +U+2CD86: bàng +U+2CD87: jiè +U+2CD89: wǔ +U+2CD8A: gèng U+2CD8B: jū +U+2CD8C: dài U+2CD8D: tuó +U+2CD8E: jié U+2CD8F: wéi U+2CD90: zhào +U+2CD93: tíng +U+2CD94: kào +U+2CD95: tí +U+2CD97: láo +U+2CD9B: shàn +U+2CD9E: hú U+2CD9F: là U+2CDA0: liàn +U+2CDA2: xīng +U+2CDA3: zhǎ +U+2CDA4: tí +U+2CDA6: yóu +U+2CDA7: róu U+2CDA8: jì +U+2CDAA: nì +U+2CDAB: huáng +U+2CDAC: qū U+2CDAD: jì U+2CDAE: xǐ +U+2CDAF: guǒ +U+2CDB1: jǐng +U+2CDB2: xiàng +U+2CDB4: pū +U+2CDB5: guān +U+2CDBA: guàn +U+2CDBB: bà U+2CDD5: bū,bǔ +U+2CDFC: yì +U+2CDFD: xiāo +U+2CDFE: hóng +U+2CE00: wén +U+2CE01: ǒu +U+2CE02: gē U+2CE05: chú +U+2CE06: shēng +U+2CE08: chī +U+2CE09: qióng +U+2CE0A: rén +U+2CE0C: shā +U+2CE0D: chóu +U+2CE0E: lí +U+2CE0F: láng +U+2CE10: chuáng +U+2CE11: yuè +U+2CE12: qí +U+2CE15: yīng +U+2CE16: yán U+2CE18: yǎn +U+2CE19: miáo U+2CE1A: yuè +U+2CE1B: huáng +U+2CE1C: piān +U+2CE1D: ān +U+2CE1E: lǘ +U+2CE20: gē +U+2CE21: lán +U+2CE22: gāo U+2CE23: xiān +U+2CE24: piāo +U+2CE25: chōng U+2CE26: zhuó +U+2CE27: yàn +U+2CE28: qī +U+2CE29: ǎo U+2CE2A: fán +U+2CE2C: kòu +U+2CE2D: yà +U+2CE2E: kuí +U+2CE2F: pì +U+2CE30: hè U+2CE31: qú +U+2CE35: zhàn +U+2CE36: chāng +U+2CE37: còu +U+2CE38: biàn +U+2CE39: gàn +U+2CE3E: qí +U+2CE45: cái +U+2CE47: bó +U+2CE49: hún +U+2CE4B: hún +U+2CE4C: nié +U+2CE4D: mò +U+2CE4E: shàn +U+2CE55: zhěn +U+2CE56: nóng +U+2CE57: lái +U+2CE58: téng +U+2CE63: zhū +U+2CE7A: yá +U+2CE7B: nà U+2CE7C: xiè +U+2CE7D: háng +U+2CE80: zhā +U+2CE81: zhì +U+2CE82: niè +U+2CE83: ái +U+2CE84: kuò +U+2CE85: chǎn +U+2CE87: cuó U+2CE88: yǐ +U+2CE89: zé +U+2CE8A: yǔn +U+2CE8B: zú +U+2CE8C: yá +U+2CE8D: jiān +U+2CE8E: cī +U+2CE8F: bó +U+2CE92: yǐn U+2CE93: chǔ +U+2CE94: jìn +U+2CE95: chà +U+2CE96: jué +U+2CE9C: lóng +U+2CE9F: yào U+2D016: lí -U+2D11B: jiǎn +U+2D11B: zuān,jiǎn +U+2D1DC: pō +U+2D1EF: liú +U+2D208: mǔ +U+2D209: yìng +U+2D22E: huán +U+2D268: zuǐ +U+2D2B8: là +U+2D382: zōng +U+2D39C: dǎo +U+2D3E6: ài +U+2D3F8: wěi U+2D546: suǒ +U+2D5E1: kē U+2D613: hū U+2D6A6: guō +U+2D74B: líng +U+2D784: ài +U+2D819: pū +U+2D85C: shěn U+2D8C7: diān +U+2D8E7: yì +U+2D90E: lí U+2D930: yú +U+2D9CB: tǎng +U+2DA5A: yǐ +U+2DA5B: mēn +U+2DA70: fá U+2DA86: zhuā +U+2DAC0: kuì +U+2DAD9: dí +U+2DB48: yà +U+2DC0E: dāng +U+2DC12: shā U+2DC4A: hòng +U+2DCAB: zhú +U+2DD0A: mó U+2DE5C: luó +U+2DED4: lái +U+2E024: láo +U+2E032: gèng U+2E18F: lán +U+2E1E4: líng +U+2E260: qiú U+2E261: lú U+2E262: zhì +U+2E263: zuó U+2E264: guà U+2E267: liǎng U+2E268: guā U+2E26A: zōu U+2E26B: ruí U+2E26C: cuì +U+2E26E: suì U+2E26F: cài +U+2E3FA: dú +U+2E41A: fán U+2E428: yīng U+2E502: jié U+2E505: fù @@ -41632,25 +42925,43 @@ U+2E581: liǎng U+2E5B1: xié U+2E64A: yǐn U+2E64B: dú +U+2E6D7: táng +U+2E736: dào U+2E774: gū U+2E775: ér +U+2E777: fén U+2E778: chūn -U+2E779: huán,huàn +U+2E779: huàn,huán U+2E77A: gé U+2E81E: qiāng +U+2E833: chěn +U+2E848: kē U+2E8F2: gāng U+2E8F3: hóng U+2E8F4: chán U+2E8F5: zhuì U+2E8F6: lǜ U+2E8F7: jù +U+2E92B: mén +U+2E92C: chù +U+2E92F: ruì U+2E932: bì +U+2E938: é U+2E9F4: chàng U+2E9F5: wěi +U+2EA34: fēng +U+2EA35: liù U+2EA5B: bì +U+2EA5C: hài +U+2EA5D: ài +U+2EA5E: yì +U+2EAA1: máng +U+2EAA2: hài U+2EAA3: zōng +U+2EAA4: cǎo U+2EAA5: dūn U+2EAC2: níng +U+2EB1B: xù U+2EB1C: yì U+2EB1D: guì U+2EB1E: cān @@ -41660,39 +42971,1240 @@ U+2EB21: huá U+2EB22: wēng U+2EB23: xián U+2EB24: zhēn +U+2EB61: jiè +U+2EB62: yāo +U+2EB64: miè U+2EB65: gōng U+2EB66: chén U+2EB68: yàn U+2EB6A: shuāng U+2EB70: gǎng +U+2EB87: móu U+2EBD9: kè,qiā +U+2EBF4: ní +U+2EBF8: lì +U+2EC04: chǒng +U+2EC09: píng +U+2EC14: wéi +U+2EC38: yǐ +U+2EC3D: lǎn +U+2EC3E: qiè +U+2EC3F: xíng +U+2EC41: yǔ +U+2EC47: lián +U+2EC4A: luó +U+2EC50: wān +U+2EC52: yàn +U+2EC60: jùn +U+2EC63: tuí +U+2EC69: yùn +U+2EC6F: dān +U+2EC75: qiāo +U+2EC81: qiàn +U+2EC83: lè +U+2EC84: tòng +U+2EC87: guó +U+2EC9A: wàn +U+2EC9D: yāo +U+2EC9F: xǔ +U+2ECAE: shuāng +U+2ECB0: bì +U+2ECB2: qìng +U+2ECB6: jiàn +U+2ECC6: hé +U+2ECCE: lán +U+2ECCF: xiàn +U+2ECD6: yàn +U+2ECD7: shū +U+2ECDC: yì +U+2ECE0: fèng +U+2ECE2: zhǔ +U+2ECE7: qiāo +U+2ECF3: zǒng +U+2ECF4: yè +U+2ED14: yè +U+2ED15: qìng +U+2ED28: cōng +U+2ED3C: yǎn +U+2ED3F: tián +U+2ED45: xī +U+2ED51: nǎo +U+2ED60: yī +U+2ED6E: diǎo +U+2ED71: lái +U+2ED7F: chà +U+2ED81: guān +U+2ED82: féng +U+2ED83: tāo +U+2ED94: wèi +U+2ED99: jiàn +U+2EDAC: jiàn +U+2EDC5: dǒng +U+2EDC7: luò +U+2EDC8: jī +U+2EDCA: bò +U+2EDCD: zhù +U+2EDD7: dài +U+2EDDA: zàng +U+2EDDB: hōng +U+2EDDD: tāo +U+2EDE3: yuǎn +U+2EDE8: lì +U+2EDF8: yuán +U+2EDFB: liú +U+2EE00: liàn +U+2EE08: jūn +U+2EE0A: chān +U+2EE0C: shì +U+2EE0F: cuān +U+2EE10: fāng +U+2EE15: fù +U+2EE17: xiǎn +U+2EE1B: kǎi +U+2EE1E: qī +U+2EE1F: cháng +U+2EE20: xiàn +U+2EE25: xié +U+2EE26: fù +U+2EE27: què +U+2EE2E: xié +U+2EE39: gù +U+2EE3C: biāo +U+2EE3D: fēi +U+2EE40: pīn +U+2EE42: fēi +U+2EE45: mò +U+2EE4B: zhōu +U+2EE4C: wēn +U+2EE58: yīng +U+2EE59: hè +U+2EE5C: yàn +U+2EE5D: tà U+2F835: huī U+30021: qiān U+30022: jiān U+3005C: luán +U+30067: sǒng +U+30078: dàng +U+3007E: fù +U+3008B: xiàn +U+3008E: xiàn +U+3008F: yù +U+30097: dì +U+3009C: tuǐ +U+300A6: lǚ +U+300AD: sì +U+300C6: zǎn U+300EE: xué -U+300F7: chuàng +U+300F7: cāng,chuàng +U+300FB: bì +U+300FF: dú +U+30101: shèn +U+3011E: tuán +U+30154: lǜ +U+30165: dú +U+30166: dān +U+3017B: xiá +U+30195: wēi +U+3019A: lán +U+301C0: gài +U+301CE: dǒng +U+301D5: jiá +U+301D6: hóng +U+301D8: jī +U+301E1: hé +U+301E3: xī +U+301F2: tǎn +U+301FC: shān +U+30206: lǎn +U+30207: cháng +U+3020A: diǎn +U+3020D: chèn +U+30213: làn +U+3022F: zá +U+30236: mó +U+30241: yán +U+30244: zá +U+30258: wěi +U+30259: gāng +U+3025A: xiàn +U+30263: yì +U+30265: jiā +U+30269: què +U+3026A: yè +U+30271: jiǎn +U+3027D: xué +U+30282: zhì +U+30285: zhàn +U+30288: qiǎng +U+30291: xiàn +U+3029B: kuí +U+3029F: mán +U+302A1: yán +U+302A2: qiàn +U+302D6: lǒu +U+302F8: dàng +U+302F9: zhuān +U+302FD: yān +U+302FE: bì +U+30300: yīng +U+30302: zhēng +U+30306: qiàn +U+30307: zé +U+30309: yùn +U+30319: lǎn +U+30326: yā +U+30337: wěi +U+3038C: sǒng +U+3038E: lóng +U+3038F: dōng +U+30390: lú +U+30391: yè +U+30394: yáo +U+30396: zè +U+3039B: kuài +U+3039E: náo +U+303A0: yán +U+303A2: wéi +U+303AB: yuè +U+303B9: yǎn +U+303C1: cuán +U+303D3: wù +U+303D5: sàn +U+303DC: lóu +U+303DF: lán +U+303F2: mà +U+303F6: kuài +U+303FC: dài +U+3043E: kuǎng +U+30441: tuán +U+30444: lún +U+30454: zhí +U+30455: yān +U+30459: liú +U+3045F: gōng +U+30465: xián +U+30467: cán +U+3046A: shéng +U+3046B: luǒ +U+3046C: zhì +U+30475: dì +U+30478: yì +U+3047F: tǎng +U+30486: mán +U+30492: chì +U+30496: miǎo +U+304C4: chéng +U+304C6: wǔ +U+304D4: shǎn +U+304D9: yè +U+304DC: zhì +U+304DF: zá +U+304E7: sūn +U+304EC: lì +U+304F1: kēng +U+304F7: ruán +U+304FB: guī +U+304FC: chàn +U+30507: dì +U+3050B: guì +U+30532: qiān +U+30536: yú +U+30541: xún +U+30545: qū +U+30548: jiǎo +U+30550: xié +U+3056D: kuài +U+30588: yǎn +U+305A0: nàn +U+305A9: lí +U+305C6: huì +U+305D3: huǎng +U+305D6: lún +U+305D9: chōu +U+305DA: xiě +U+305DB: zhái +U+305DC: yǎn +U+305E1: chǎn +U+305E2: huī U+305E3: zá +U+305E6: jǐn +U+305E8: shí +U+305EC: qiān +U+305F5: shěn +U+305F9: sù +U+305FA: fén +U+30600: dài +U+30608: jú +U+30613: qǐng +U+30620: yán +U+30623: mián +U+30629: xiāo +U+30633: lài +U+30636: sū +U+30638: sǒu +U+3064B: wū +U+3064E: hān +U+30651: xiào +U+30655: lòu +U+30694: níng +U+306A6: lǘ +U+306AA: rán +U+306B1: yè +U+306CA: mǎ +U+306CF: cóng +U+306D2: dōng +U+306DB: shǎn +U+306E1: bì +U+306E3: zhí +U+306E4: zhì +U+306E5: ái +U+306E6: huì +U+306E9: shǎn +U+306EA: luán +U+306EE: lǔ +U+306F2: liàn +U+306F5: xián +U+306FD: dǐng +U+30710: lǎn +U+3071C: qìn +U+3071D: yàng +U+30722: hè +U+30728: jiàn +U+30733: yíng +U+30745: yíng +U+3074B: yīn +U+3074D: huī +U+30757: jiǎn +U+3075F: xiǎn +U+30764: xiǎn +U+3077E: chuǐ +U+30787: yì +U+3078D: jié +U+307A4: jiān +U+307B2: bì +U+307B7: zào +U+307BB: fén U+307D8: yàn +U+3081B: jiān +U+3082B: gāng +U+30832: kēng +U+30834: yǎo +U+30839: pái +U+30844: lú +U+3084A: xiāo +U+3084B: huī +U+3084E: lái +U+3084F: wō +U+30854: chán +U+3085E: bèn +U+30862: yīng +U+30869: qí +U+30870: chē +U+30875: lú +U+3087B: nǎo +U+3087D: shú +U+30884: sù +U+308A2: wǔ +U+308A4: yīng +U+308A6: lǒu +U+308E6: wěi +U+308E9: níng +U+308EC: jì +U+308EF: xián +U+308F6: tuí +U+308FC: jī +U+308FD: luán +U+30913: tǎng +U+30915: lì +U+30928: tà +U+3092B: dú +U+3092C: dú +U+3094A: kàn +U+30952: cōng U+3095B: fèn +U+3095E: yíng +U+30960: mǎn +U+30962: xǐ +U+30963: kēng +U+30968: xián +U+3096A: guī +U+3096D: měng +U+30979: tǎng +U+3099C: bà +U+309A6: lì +U+309A8: huǐ +U+309AD: lóng +U+309B0: zé +U+309B4: yā +U+309B7: láo +U+309BE: yán +U+309BF: tuó +U+309C3: jiàn +U+309C7: xián U+309C9: zhì +U+309CE: qīn +U+309D4: jiān +U+309D8: zhū +U+309F0: yáng +U+309FB: guì +U+309FE: nóng +U+30A16: tí +U+30A1C: kuàng +U+30A26: jì +U+30A33: zī +U+30A45: lǒng +U+30A4F: tiǎo +U+30A53: chēng +U+30A67: xū +U+30A6E: tuán +U+30A72: lú +U+30A78: jiāo +U+30A79: dàng +U+30A7A: yuē +U+30A7B: zhuā +U+30A8A: zhōng +U+30A8F: lù +U+30AA3: yíng +U+30AAB: tuán +U+30AAD: zhāng +U+30AB6: tuán +U+30ACB: mén +U+30AD6: làn +U+30AFC: zhěng +U+30AFD: fóu +U+30B00: jì +U+30B03: liè +U+30B05: fū +U+30B06: qú +U+30B07: zhù +U+30B08: xián +U+30B09: tuó +U+30B0B: hóng +U+30B0C: gēng +U+30B0D: xié +U+30B0E: bì +U+30B0F: zhū +U+30B10: qiāo +U+30B11: zhēng +U+30B12: cì +U+30B13: gāi +U+30B14: bēng +U+30B16: chéng +U+30B17: qiú +U+30B18: fú +U+30B19: chēn +U+30B1A: yún +U+30B1B: zhèn +U+30B1C: miǎn +U+30B1E: qīn +U+30B1F: yù +U+30B20: huà +U+30B21: qiè +U+30B22: qī +U+30B23: xī +U+30B24: bì +U+30B25: zǒng +U+30B26: hū +U+30B27: tián +U+30B29: xiù +U+30B2A: shān +U+30B2B: zhì +U+30B2C: miáo +U+30B2D: běng +U+30B2E: qiàn +U+30B2F: zōng +U+30B30: zǒng +U+30B31: suǒ +U+30B32: quán +U+30B33: nì +U+30B34: zài +U+30B35: xuàn +U+30B36: qiè +U+30B37: sǎn +U+30B38: suì +U+30B39: zuǎn +U+30B3A: zūn +U+30B3B: yù +U+30B3C: là +U+30B3D: xiè +U+30B3E: lián +U+30B3F: pú +U+30B40: yōu +U+30B41: cōng +U+30B44: guàn +U+30B54: juàn +U+30B5A: lóu +U+30B62: shà +U+30B63: dào +U+30B79: lóng +U+30B85: xiāng +U+30B87: hū +U+30B99: zhù +U+30B9D: xiāo +U+30BB2: sōu +U+30BCB: ní +U+30BCE: mán U+30BF2: jiān +U+30C06: tān +U+30C0B: shuāng +U+30C0C: zhōu +U+30C0F: dài +U+30C11: lóu +U+30C20: xù +U+30C22: tāng +U+30C24: qiū +U+30C28: lún +U+30C2E: kuī +U+30C31: duì +U+30C33: shī +U+30C34: qǐ +U+30C35: shā +U+30C36: zá +U+30C37: fù +U+30C39: liú +U+30C3A: shè +U+30C3E: kē +U+30C40: gòng +U+30C47: jiān +U+30C48: yǒng +U+30C49: méng +U+30C4C: nà +U+30C4D: fén +U+30C50: xù +U+30C51: jiàn +U+30C5B: zhōng +U+30C5D: xiàng +U+30C5F: fén +U+30C66: fù +U+30C69: lǘ +U+30C6E: lǘ +U+30C6F: bēi U+30C71: cí +U+30C72: zhī +U+30C7E: lài +U+30C81: tuí +U+30C82: téng +U+30C92: lèi +U+30C9F: wū +U+30CA0: zhàn +U+30CAB: wú +U+30CAC: chē +U+30CAE: qiǎn +U+30CAF: zōng +U+30CB0: lún +U+30CB2: guǐ +U+30CB3: zhàn +U+30CB4: lóng +U+30CB5: zé +U+30CB6: xīng +U+30CB8: è +U+30CB9: yǐ +U+30CBA: guò +U+30CBB: dá +U+30CC1: jī +U+30CC2: jiàn +U+30CC4: chén +U+30CCA: féi +U+30CD7: yīng +U+30CDA: wèi +U+30CF2: lóng +U+30CF5: yì +U+30CF8: duì +U+30CF9: yǎn +U+30CFA: nóng +U+30CFC: shī +U+30D02: sēn +U+30D15: miè +U+30D16: shào +U+30D17: lì +U+30D18: jiàn +U+30D19: jūn +U+30D1A: lóu +U+30D1B: dōu +U+30D1C: qū +U+30D1D: luó +U+30D1E: qū +U+30D22: lì +U+30D23: xué +U+30D24: dǎi +U+30D2F: yíng +U+30D4A: jiào +U+30D4C: yáng +U+30D4D: zhùn +U+30D4E: yóu +U+30D4F: chāo +U+30D50: fǎn +U+30D51: é +U+30D52: chén +U+30D53: xù +U+30D54: yì +U+30D56: yì +U+30D57: yàng +U+30D59: dié +U+30D5A: líng +U+30D5B: gòu +U+30D5C: táo U+30D5D: hé +U+30D5E: yǒng +U+30D60: náo +U+30D61: kēng +U+30D62: jiá +U+30D63: hé +U+30D64: è +U+30D65: yí +U+30D66: xiào +U+30D67: zhì +U+30D68: zhān +U+30D69: bū +U+30D6B: dòu +U+30D6C: bié +U+30D6D: chán +U+30D6E: é +U+30D6F: shuà +U+30D70: xìng +U+30D71: chēn +U+30D72: qī +U+30D73: zhuó U+30D74: chù -U+30DE0: yí,yì +U+30D75: nì +U+30D76: zhuì +U+30D77: zhuó +U+30D78: tà +U+30D79: niè +U+30D7A: yuán +U+30D7B: huá +U+30D7C: huò +U+30D7D: tāo +U+30D7E: xǐ +U+30D7F: hè +U+30D80: yú +U+30D81: yí +U+30D82: còng +U+30D83: xí +U+30D84: zhā +U+30D85: hào +U+30D86: hàn +U+30D87: tuō +U+30D88: chí +U+30D89: tán +U+30D8A: yī +U+30D8C: nòu +U+30D8D: juàn +U+30D8E: sè +U+30D8F: yào +U+30D91: lóng +U+30D94: dú +U+30DA8: mì +U+30DAC: lóu +U+30DDE: tè +U+30DE0: yì,yí +U+30DE1: wán +U+30DE2: chí +U+30DE4: suì U+30DE5: cóng +U+30DE6: fèi +U+30DE7: jìn +U+30DE8: kuì +U+30DE9: shèng +U+30DEA: yì +U+30DEB: biāo +U+30DEC: zhuàn +U+30DED: zāng +U+30DEE: xuàn +U+30DF4: lì +U+30DF5: qiāo +U+30DF6: qiáo +U+30DF8: dú +U+30E07: shuāng +U+30E08: dié +U+30E0A: guì +U+30E0E: chǎn +U+30E10: xǐ +U+30E14: lián +U+30E1A: dài +U+30E1B: chì +U+30E1E: tuí +U+30E40: duǒ +U+30E72: wèi +U+30E73: chūn +U+30E74: fàn +U+30E75: yǐ +U+30E76: kuáng +U+30E77: yuè +U+30E78: tún +U+30E7A: fǎn +U+30E7B: bā +U+30E7C: zhàn +U+30E7D: pēng +U+30E7E: xiàn +U+30E7F: rǒng +U+30E80: dǐ +U+30E81: qú +U+30E82: páo +U+30E83: duì +U+30E84: gǒng +U+30E85: jú +U+30E86: hōng +U+30E87: wàn +U+30E88: yǐn +U+30E89: qiàn +U+30E8A: líng +U+30E8B: zhé +U+30E8C: lù +U+30E8D: guǒ +U+30E8E: pái +U+30E8F: péng +U+30E90: kǎn +U+30E91: yuān +U+30E92: chuán +U+30E93: fù +U+30E94: zǒng +U+30E95: duó +U+30E96: zhēn +U+30E97: jiàn +U+30E98: kēng +U+30E99: sè +U+30E9A: ér +U+30E9B: lǎo +U+30E9C: fān +U+30E9D: chōng +U+30E9E: zhàn +U+30E9F: jí +U+30EA0: niè +U+30EA1: xiá +U+30EA2: luò +U+30EA3: lìn +U+30EA4: mín +U+30EA8: huò +U+30EAD: chén +U+30EB2: dàng +U+30EB7: rào +U+30EC6: dì U+30EDD: biáng U+30EDE: biáng +U+30EE1: xǔ +U+30EE6: wéi +U+30EEE: qiāo +U+30EF3: bì +U+30F05: yì +U+30F0B: láo +U+30F0F: làn +U+30F11: mí +U+30F3B: qīng +U+30F55: jī +U+30F56: bā +U+30F57: huá +U+30F58: dì +U+30F5A: kuàng +U+30F5B: qiǎo +U+30F5C: ōu +U+30F5D: lì +U+30F5E: è +U+30F60: zhǐ +U+30F61: rì +U+30F62: xiàn +U+30F63: yǐn +U+30F65: bǐng +U+30F66: lóng +U+30F67: pī +U+30F68: zhǎi +U+30F69: kǎ +U+30F6B: shēng +U+30F6C: hé +U+30F6D: gōu +U+30F6E: fú +U+30F6F: zhù +U+30F70: bàn +U+30F71: fú +U+30F72: jí +U+30F73: qīn +U+30F74: kuǎ +U+30F75: guāng +U+30F76: tóng +U+30F77: huì +U+30F78: pī +U+30F79: chǐ +U+30F7A: guǐ +U+30F7B: rén +U+30F7C: bǐng +U+30F7D: róng +U+30F7E: guǒ +U+30F7F: zhé +U+30F80: bó +U+30F81: hòng +U+30F83: zhuó +U+30F84: méi +U+30F85: hán +U+30F86: yù +U+30F87: tūn +U+30F88: shā +U+30F8A: cōng +U+30F8C: jiàn +U+30F8D: biǎo +U+30F8E: pī +U+30F8F: táo +U+30F90: fèi +U+30F91: qiān +U+30F92: guǎn +U+30F93: tà +U+30F95: yè +U+30F96: duò +U+30F97: zhēn +U+30F98: chěn +U+30F99: yú +U+30F9A: wěi +U+30F9B: jiè +U+30F9C: duān +U+30F9D: shēng +U+30F9E: zōng +U+30F9F: fù +U+30FA1: hōng +U+30FA2: shī +U+30FA4: xiá +U+30FA5: kuí +U+30FA6: zuǎn +U+30FA9: yè +U+30FAA: yā +U+30FAB: jiàn +U+30FAC: dá +U+30FAD: ài +U+30FAE: lián +U+30FAF: táng +U+30FB0: cuī +U+30FB1: zǒng +U+30FB2: lù +U+30FB3: lán +U+30FB4: sī +U+30FB6: huì +U+30FB7: zān +U+30FB8: dǐng +U+30FB9: juān +U+30FBB: tián +U+30FBC: quān +U+30FBD: zhú +U+30FBE: niè +U+30FBF: là +U+30FC1: áo +U+30FC2: xū +U+30FC3: miè +U+30FC4: léi +U+30FC6: zuān +U+30FC7: zhá +U+30FC8: jiān +U+30FC9: lěi +U+30FCA: luó +U+30FE6: zhèn +U+30FE8: guǎn +U+30FE9: xiā +U+30FEA: xiè +U+30FEB: wěi +U+30FEC: pēng +U+30FF0: zuān +U+30FF3: shā +U+30FF4: yà +U+30FF5: yú +U+30FF9: guāng +U+30FFA: táng +U+30FFB: xì +U+30FFE: pì +U+31011: zhì +U+31021: fén +U+31052: duì +U+3105E: wān +U+31071: yáng +U+31073: duó +U+31074: bì +U+31076: qiān +U+31077: dú +U+31079: guì +U+3107A: lóu +U+3107D: lán +U+3107E: lán +U+31083: sǎ +U+31084: mèi +U+31085: bì +U+31086: gé +U+31087: quàn +U+31088: yùn +U+31089: qiào +U+3108A: shè +U+3108B: dú +U+3108C: hù +U+3108D: duì +U+3108E: xiè +U+31090: yùn +U+310A0: duó +U+310A1: kū +U+310A2: zhuō +U+310A3: yáo +U+310A4: huì +U+310A5: é +U+310A6: pīng +U+310A7: lèi U+310A8: è +U+310A9: fù +U+310AB: yǎn +U+310AC: kuí +U+310AD: chuí +U+310AE: pǐ U+310AF: cuì +U+310B0: hàn U+310B1: kǎn +U+310B2: sāi +U+310B3: gǎo +U+310B4: jiǎng +U+310B5: kuī +U+310B6: xìn +U+310B7: zhān +U+310B8: bīn +U+310BA: piǎo U+310BB: qiáo +U+310D4: biāo +U+310D5: xiā +U+310D6: yǒu +U+310D7: tái +U+310D8: liè +U+310D9: liú +U+310DA: lì +U+310DC: ruí +U+310DD: xiū +U+310DE: yú +U+310DF: xí +U+310E0: biāo +U+310F1: xì +U+310F2: yù +U+310F3: bó +U+310F4: fàn +U+310F5: yǐng +U+310F7: bǎn +U+310F8: zhì +U+310F9: èn U+310FA: tiǎn +U+310FC: fēn +U+310FD: è +U+310FE: bù +U+310FF: tán +U+31100: fēn +U+31101: tǒu +U+31103: jiàn +U+31104: huì +U+31106: xī +U+31107: shǎng +U+31108: méng +U+31109: èn +U+3110A: zàn +U+3113C: zhù +U+3113D: zhé +U+3113E: guāng +U+3113F: dú +U+31140: bǎo +U+31141: zhí +U+31142: sà +U+31143: jù +U+31144: bá +U+31145: lóng +U+31147: zhěn U+31148: bì +U+31149: róng +U+3114A: zhì +U+3114B: ěr +U+3114D: xù +U+3114E: fú +U+3114F: táo +U+31150: bó +U+31153: bó +U+31154: sǒng +U+31155: lái +U+31156: xuān +U+31157: ě +U+31159: tuì +U+3115A: shè +U+3115B: tuó +U+3115C: yǎo +U+3115D: piàn +U+3115E: xiá +U+3115F: róu +U+31160: hàn +U+31161: guī +U+31162: chéng +U+31163: tāo +U+31164: pīn +U+31166: huáng +U+31167: xū +U+31169: xí +U+3116A: diàn +U+3116B: yù +U+3116C: yàn +U+3116E: xiāo +U+31180: qiān +U+31181: qiān +U+31183: kuì +U+31184: fèi +U+31185: lán +U+31186: mán +U+31188: zuǎn +U+3118C: shāng +U+3118D: chǎo +U+31199: chāo +U+3119A: kuǐ +U+3119B: chǒu U+311A5: dān +U+311CD: yà +U+311CF: tuō +U+311D0: yuán +U+311D1: bèi +U+311D2: pí +U+311D3: hú +U+311D4: nà +U+311D5: yú +U+311D6: bǎn +U+311D7: lún +U+311D8: háng +U+311D9: shěn +U+311DA: mò +U+311DB: wèi +U+311DC: qū +U+311DD: pī +U+311DE: shàn +U+311DF: xiá +U+311E0: qiú +U+311E1: xīng +U+311E2: gǔn +U+311E3: bō +U+311E4: yǒu +U+311E5: gèng +U+311E6: kū +U+311E7: yí +U+311E8: shū +U+311E9: jiǎo +U+311EA: luò +U+311EB: rú +U+311EC: yí +U+311ED: lái +U+311EE: máng +U+311EF: shāo +U+311F0: yè +U+311F1: tiáo +U+311F2: fū +U+311F3: suō +U+311F4: nà +U+311F5: jì +U+311F6: lù +U+311F7: cuò +U+311F8: yìng +U+311F9: zhì +U+311FA: jì +U+311FB: xiàn +U+311FC: jiù +U+311FD: jú +U+311FE: zōng +U+311FF: sù +U+31200: jù +U+31201: shān +U+31202: yú +U+31203: wèi +U+31204: hóu +U+31205: zōng +U+31206: lóu +U+31207: gèng +U+31208: gèng +U+31209: qián +U+3120A: liú +U+3120B: hào +U+3120C: xiū +U+3120D: ruò +U+3120E: sǎng +U+3120F: zhú +U+31210: zhuān +U+31211: huī +U+31212: sī +U+31213: xún +U+31214: cén +U+31215: fān +U+31216: lài +U+31217: rú +U+31218: cháng +U+31219: miè +U+3121A: è +U+3121B: méng +U+3121C: xiān +U+31247: yǐ +U+31248: diāo +U+3124A: gān +U+3124B: hàn +U+3124C: yí +U+3124D: yù +U+3124E: fū +U+3124F: tuán +U+31250: jú +U+31251: bān +U+31252: huān +U+31253: hù U+31254: jué +U+31255: gē +U+31256: pí +U+31257: dàn +U+31258: jū +U+31259: zhǐ +U+3125A: dié +U+3125B: zhì +U+3125C: fū +U+3125D: bī +U+3125E: wǔ +U+3125F: zé +U+31260: jīng +U+31261: biǎn +U+31262: yǎo +U+31263: sōng +U+31264: guì +U+31265: jiá +U+31266: lǎo +U+31267: yuān +U+31268: yā +U+31269: tí +U+3126A: ài +U+3126B: zhū +U+3126C: jiāo +U+3126D: guǐ +U+3126E: luò +U+3126F: yáng +U+31270: yù +U+31271: kūn +U+31272: móu +U+31273: mǎi +U+31274: yáng +U+31275: lái +U+31276: hàn +U+31277: yì +U+31278: tú +U+31279: xī +U+3127A: fú +U+3127B: tuǒ +U+3127C: jí +U+3127D: jì +U+3127E: jùn +U+3127F: jùn +U+31280: lù +U+31281: qí +U+31282: míng +U+31283: lí +U+31284: yì +U+31285: zhuī +U+31286: tù +U+31287: jú U+31288: yì +U+3128A: kōng +U+3128B: jiān +U+3128C: jū +U+3128D: duò +U+3128E: zī +U+3128F: fú +U+31290: jiē +U+31291: chūn +U+31292: yuán +U+31293: mò +U+31295: tí +U+31296: tú +U+31297: róu +U+31298: chuàn +U+31299: zhèn +U+3129A: tián +U+3129B: sǔn +U+3129C: niǎo +U+3129D: táng +U+3129F: xiá +U+312A0: tú +U+312A1: yǎo +U+312A2: jí +U+312A3: ān +U+312A4: yōng +U+312A5: shāng +U+312A6: yì +U+312A7: jué +U+312A9: tú +U+312AA: líng +U+312AB: yú +U+312AC: chǔ +U+312AD: yù +U+312AE: luó +U+312AF: jī +U+312B0: yì +U+312B1: dí +U+312B2: bǔ +U+312B3: lěi +U+312B5: yuè +U+312BA: lǔ +U+312BB: lǔ +U+312BC: chuài +U+312BD: huái +U+312C6: shàn +U+312C7: hé +U+312C8: kuàng +U+312CA: mò +U+312CB: qù +U+312CD: tǒu +U+312CE: huá +U+312D0: qū +U+312D3: gé +U+312D4: bǐng +U+312D5: chǎo +U+312D6: fū +U+312D7: fū +U+312D8: shā +U+312D9: guǒ +U+312DC: chǎo +U+312DD: bù +U+312E1: lǒu +U+312E2: chǎo +U+312EA: zhí +U+312EB: kuàng +U+312EC: lián +U+312EE: méng +U+312F1: fén +U+312F4: chǎn +U+312F6: lú +U+312FE: cù +U+312FF: qù +U+31300: yāng +U+31301: qú +U+31303: wā +U+31304: zhī +U+31305: mí +U+31306: měng +U+31307: biē +U+31308: má +U+31309: bì +U+3130A: chāng +U+3130F: hún +U+31316: nòng +U+31317: jì +U+31318: zī +U+31319: zī +U+3132B: yǎn +U+3132C: zé +U+3132D: zōu +U+3132E: chū +U+3132F: chī +U+31330: yǎo +U+31331: xiè +U+31332: quán +U+31333: yǔn +U+31334: zhāi +U+31335: zhān +U+31336: zōu +U+31337: yí +U+31338: jì +U+31339: óu +U+3133A: diān +U+3133D: yì +U+31341: gōng +U+31342: dá +U+31344: rán +U+31345: gōu +U+31346: jiāo +U+31347: tóng +U+31348: biē U+31350: qí U+31355: pā U+3135B: áo diff --git a/contrib/sqlite3/CMakeLists.txt b/contrib/sqlite3/CMakeLists.txt index 0a16bf4..8edef82 100644 --- a/contrib/sqlite3/CMakeLists.txt +++ b/contrib/sqlite3/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.8) +cmake_minimum_required(VERSION 3.19) project(SQLite3 VERSION 3.31.1 LANGUAGES C diff --git a/example.sql b/example.sql index f2d7cbf..84b3c01 100644 --- a/example.sql +++ b/example.sql @@ -3,6 +3,19 @@ -- load so file .load libsimple +select '自定义拼音词典示例(只包含 周/伦 的拼音):'; +-- 在本仓库里从 output/bin 运行本例时,路径可使用 ../../contrib/pinyin-mini.txt +select pinyin_dict('../../contrib/pinyin-mini.txt'); +CREATE VIRTUAL TABLE t0 USING fts5(x, tokenize = 'simple'); +insert into t0(x) values ('周杰伦'); +select ' 搜索 zhou,命中数量(预期 1):', count(*) from t0 where x match simple_query('zhou'); +select ' 搜索 lun,命中数量(预期 1):', count(*) from t0 where x match simple_query('lun'); +select ' 搜索 jie,命中数量(预期 0):', count(*) from t0 where x match simple_query('jie'); +select ' 搜索 zhou lun,命中数量(预期 1):', count(*) from t0 where x match simple_query('zhou lun'); +drop table t0; +-- 切回默认词典,保证下面示例行为不变 +select pinyin_dict('../../contrib/pinyin.txt'); + select '启用拼音分词:'; -- set tokenize to simple CREATE VIRTUAL TABLE t1 USING fts5(x, tokenize = 'simple'); diff --git a/examples/cpp/CMakeLists.txt b/examples/cpp/CMakeLists.txt index 34e780f..d0301ec 100644 --- a/examples/cpp/CMakeLists.txt +++ b/examples/cpp/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.2) +cmake_minimum_required(VERSION 3.19) project(simple_cpp_example) include_directories(${SQLITE3_HEADERS_DIR}) diff --git a/examples/react-native-op-sqlite.md b/examples/react-native-op-sqlite.md new file mode 100644 index 0000000..7fb958b --- /dev/null +++ b/examples/react-native-op-sqlite.md @@ -0,0 +1,132 @@ +# React Native op-sqlite usage + +This note shows how to load `simple` as a SQLite runtime extension in a React +Native app using [`@op-engineering/op-sqlite`](https://github.com/OP-Engineering/op-sqlite). + +`simple` is an FTS5 tokenizer extension, so op-sqlite must be built with FTS5 +enabled and with runtime extension loading available. + +## Build the extension + +Build an iOS xcframework: + +```sh +./build-ios-dynamic.sh +``` + +The output is: + +```text +output/libsimple-dynamic.xcframework +``` + +For Android, use the shared library from the normal Android build/release +artifact and package it as `libsimple.so` for each target ABI. + +## iOS + +Add the xcframework to your app, for example: + +```text +ios/Frameworks/simple.xcframework +``` + +If you use CocoaPods, create a small local podspec: + +```ruby +Pod::Spec.new do |s| + s.name = 'SimpleSQLiteExtension' + s.version = '0.1.0' + s.summary = 'SQLite simple tokenizer extension' + s.homepage = 'https://github.com/wangfenjin/simple' + s.license = { :type => 'MIT' } + s.author = { 'simple contributors' => 'https://github.com/wangfenjin/simple' } + s.source = { :git => 'https://github.com/wangfenjin/simple.git', :tag => s.version.to_s } + s.platform = :ios, '12.0' + s.vendored_frameworks = 'Frameworks/simple.xcframework' +end +``` + +Then reference it from `ios/Podfile`: + +```ruby +target 'YourApp' do + pod 'SimpleSQLiteExtension', :path => '.' +end +``` + +Run: + +```sh +cd ios +pod install +``` + +## Android + +Package `libsimple.so` in your React Native project, for example: + +```text +android/app/src/main/jniLibs/arm64-v8a/libsimple.so +android/app/src/main/jniLibs/armeabi-v7a/libsimple.so +android/app/src/main/jniLibs/x86/libsimple.so +android/app/src/main/jniLibs/x86_64/libsimple.so +``` + +## Load the extension + +Load the extension before creating FTS5 tables that use the `simple` tokenizer. + +```ts +import { Platform } from 'react-native'; +import { getDylibPath, open } from '@op-engineering/op-sqlite'; + +const db = open({ name: 'search.db' }); + +if (Platform.OS === 'android') { + db.loadExtension('libsimple', 'sqlite3_simple_init'); +} else if (Platform.OS === 'ios') { + const path = getDylibPath('com.wangfenjin.simple', 'simple'); + db.loadExtension(path, 'sqlite3_simple_init'); +} +``` + +After loading succeeds, `simple` can be used like any other FTS5 tokenizer: + +```ts +db.execute(` + CREATE VIRTUAL TABLE IF NOT EXISTS docs + USING fts5(title, body, tokenize = 'simple'); +`); + +db.execute('INSERT INTO docs(title, body) VALUES (?, ?)', [ + '中华人民共和国国歌', + '支持中文和拼音搜索', +]); + +const result = db.execute( + 'SELECT rowid, title FROM docs WHERE docs MATCH simple_query(?)', + ['中华国歌'], +); +``` + +To disable pinyin tokens, use the tokenizer option supported by `simple`: + +```ts +db.execute(` + CREATE VIRTUAL TABLE IF NOT EXISTS docs_no_pinyin + USING fts5(title, body, tokenize = 'simple 0'); +`); +``` + +## Troubleshooting + +- `no such tokenizer: simple` usually means the extension was not loaded before + the FTS5 table was created. +- `not authorized` or `load extension` errors usually mean runtime extension + loading is disabled in the SQLite build used by your app. +- iOS apps should load the framework from the app bundle. With op-sqlite, use + `getDylibPath('com.wangfenjin.simple', 'simple')` for the framework named + `simple.framework`. +- Android should load the library name without the `.so` suffix: + `db.loadExtension('libsimple', 'sqlite3_simple_init')`. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0c23eee..a088e9b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.2) +cmake_minimum_required(VERSION 3.19) project(simple CXX) if(SIMPLE_WITH_JIEBA) @@ -27,8 +27,20 @@ set(SOURCE_FILES ) OPTION(BUILD_STATIC "Option to build static lib" OFF) -if (IOS OR BUILD_STATIC) -# iOS only support static library. +OPTION(BUILD_IOS_DYNAMIC_FRAMEWORK "Option to build iOS dynamic framework" OFF) +if (IOS AND BUILD_IOS_DYNAMIC_FRAMEWORK) +add_library(simple SHARED ${SOURCE_FILES}) +set_target_properties(simple PROPERTIES + FRAMEWORK TRUE + MACOSX_FRAMEWORK_IDENTIFIER "com.wangfenjin.simple" + PUBLIC_HEADER "pinyin.h;simple_highlight.h;simple_tokenizer.h" + XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO" + XCODE_ATTRIBUTE_DEFINES_MODULE "YES" + XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "com.wangfenjin.simple" + XCODE_ATTRIBUTE_SKIP_INSTALL "NO" +) +elseif (IOS OR BUILD_STATIC) +# iOS releases default to static libraries unless BUILD_IOS_DYNAMIC_FRAMEWORK is enabled. add_library(simple STATIC ${SOURCE_FILES}) else() add_library(simple SHARED ${SOURCE_FILES}) diff --git a/src/entry.cc b/src/entry.cc index e203f89..c365eab 100644 --- a/src/entry.cc +++ b/src/entry.cc @@ -97,6 +97,23 @@ static void simple_query(sqlite3_context *pCtx, int nVal, sqlite3_value **apVal) sqlite3_result_null(pCtx); } +static void pinyin_dict(sqlite3_context *pCtx, int nVal, sqlite3_value **apVal) { + if (nVal >= 1) { + const char *text = (const char *)sqlite3_value_text(apVal[0]); + if (text) { + std::string err; + std::string path(text); + if (simple_tokenizer::SimpleTokenizer::set_pinyin_dict(path, err)) { + sqlite3_result_text(pCtx, path.c_str(), -1, SQLITE_TRANSIENT); + } else { + sqlite3_result_error(pCtx, err.c_str(), -1); + } + return; + } + } + sqlite3_result_null(pCtx); +} + int sqlite3_simple_init(sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi) { (void)pzErrMsg; int rc = SQLITE_OK; @@ -104,6 +121,8 @@ int sqlite3_simple_init(sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines rc = sqlite3_create_function(db, "simple_query", -1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, NULL, &simple_query, NULL, NULL); + rc = sqlite3_create_function(db, "pinyin_dict", 1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, NULL, &pinyin_dict, NULL, + NULL); #ifdef USE_JIEBA rc = sqlite3_create_function(db, "jieba_query", -1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, NULL, &jieba_query, NULL, NULL); diff --git a/src/pinyin.cc b/src/pinyin.cc index e63ab86..47602c0 100644 --- a/src/pinyin.cc +++ b/src/pinyin.cc @@ -1,6 +1,7 @@ #include "pinyin.h" #include +#include #include #include #include @@ -12,7 +13,9 @@ CMRC_DECLARE(pinyin_text); namespace simple_tokenizer { -PinYin::PinYin() { pinyin = build_pinyin_map(); } +PinYin::PinYin() : PinYin("") {} + +PinYin::PinYin(const std::string &pinyin_file_path) { pinyin = build_pinyin_map(pinyin_file_path); } std::set PinYin::to_plain(const std::string &input) { std::set s; @@ -49,21 +52,45 @@ std::set PinYin::to_plain(const std::string &input) { } // clang-format off -std::map > PinYin::build_pinyin_map() { +std::map > PinYin::build_pinyin_map(const std::string &pinyin_file_path) { std::map > map; // clang-format on - auto fs = cmrc::pinyin_text::get_filesystem(); - auto pinyin_data = fs.open("contrib/pinyin.txt"); - std::istringstream pinyin_file(std::string(pinyin_data.begin(), pinyin_data.end())); + std::istringstream embedded_pinyin_file; + std::ifstream custom_pinyin_file; + std::istream *pinyin_file = nullptr; + if (pinyin_file_path.empty()) { + auto fs = cmrc::pinyin_text::get_filesystem(); + auto pinyin_data = fs.open("contrib/pinyin.txt"); + embedded_pinyin_file = std::istringstream(std::string(pinyin_data.begin(), pinyin_data.end())); + pinyin_file = &embedded_pinyin_file; + } else { + custom_pinyin_file.open(pinyin_file_path); + if (!custom_pinyin_file.is_open()) { + throw std::runtime_error("failed to open pinyin file: " + pinyin_file_path); + } + pinyin_file = &custom_pinyin_file; + } std::string line; char delimiter = ' '; std::string cp, py; - while (std::getline(pinyin_file, line)) { + int line_no = 0; + while (std::getline(*pinyin_file, line)) { + ++line_no; if (line.length() == 0 || line[0] == '#') continue; std::stringstream tokenStream(line); std::getline(tokenStream, cp, delimiter); std::getline(tokenStream, py, delimiter); - int codepoint = static_cast(std::stoul(cp.substr(2, cp.length() - 3), 0, 16l)); + if (cp.length() < 4 || cp.rfind("U+", 0) != 0 || cp.back() != ':' || py.empty()) { + throw std::runtime_error("invalid pinyin format at line " + std::to_string(line_no)); + } + + int codepoint = 0; + try { + codepoint = static_cast(std::stoul(cp.substr(2, cp.length() - 3), 0, 16l)); + } catch (const std::exception &) { + throw std::runtime_error("invalid pinyin codepoint at line " + std::to_string(line_no)); + } + std::set s = to_plain(py); std::vector m(s.size()); std::copy(s.begin(), s.end(), m.begin()); diff --git a/src/pinyin.h b/src/pinyin.h index 514c226..d7317bc 100644 --- a/src/pinyin.h +++ b/src/pinyin.h @@ -106,7 +106,7 @@ class PinYin { }; // clang-format on std::set to_plain(const std::string &input); - std::map > build_pinyin_map(); + std::map > build_pinyin_map(const std::string &pinyin_file_path); static int codepoint(const std::string &u); std::vector _split_pinyin(const std::string &input, int begin, int end); @@ -115,6 +115,7 @@ class PinYin { static int get_str_len(unsigned char byte); std::set split_pinyin(const std::string &input); PinYin(); + explicit PinYin(const std::string &pinyin_file_path); }; } // namespace simple_tokenizer diff --git a/src/simple_tokenizer.cc b/src/simple_tokenizer.cc index 983670e..22f2647 100644 --- a/src/simple_tokenizer.cc +++ b/src/simple_tokenizer.cc @@ -3,20 +3,48 @@ #include #include #include +#include +#include #include #include #include namespace simple_tokenizer { +namespace { +std::mutex pinyin_mutex; +std::shared_ptr global_pinyin; +} + SimpleTokenizer::SimpleTokenizer(const char **azArg, int nArg) { if (nArg >= 1) { enable_pinyin = atoi(azArg[0]) != 0; } } -PinYin *SimpleTokenizer::get_pinyin() { - static auto *py = new PinYin(); - return py; +std::shared_ptr SimpleTokenizer::get_pinyin() { + std::lock_guard lock(pinyin_mutex); + if (global_pinyin == nullptr) { + global_pinyin = std::make_shared(); + } + return global_pinyin; +} + +bool SimpleTokenizer::set_pinyin_dict(const std::string &pinyin_file_path, std::string &err) { + std::shared_ptr new_pinyin; + try { + if (pinyin_file_path.empty()) { + new_pinyin = std::make_shared(); + } else { + new_pinyin = std::make_shared(pinyin_file_path); + } + } catch (const std::exception &e) { + err = e.what(); + return false; + } + + std::lock_guard lock(pinyin_mutex); + global_pinyin = new_pinyin; + return true; } static TokenCategory from_char(char c) { @@ -73,7 +101,16 @@ std::string SimpleTokenizer::tokenize_jieba_query(const char *text, int textLen, std::vector words; jieba.Cut(text, words); for (auto word : words) { + // if all char is the same category, then use that category + // otherwise use OTHER + // fix https://github.com/wangfenjin/simple/issues/176 TokenCategory category = from_char(text[word.offset]); + for (auto c : word.word) { + if (from_char(c) != category) { + category = TokenCategory::OTHER; + break; + } + } append_result(result, word.word, category, word.offset, flags); } return result; @@ -150,7 +187,8 @@ int SimpleTokenizer::tokenize(void *pCtx, int flags, const char *text, int textL rc = xToken(pCtx, 0, result.c_str(), (int)result.length(), start, index); if (enable_pinyin && category == TokenCategory::OTHER && (flags & FTS5_TOKENIZE_DOCUMENT)) { - const std::vector &pys = SimpleTokenizer::get_pinyin()->get_pinyin(result); + std::shared_ptr pinyin = SimpleTokenizer::get_pinyin(); + const std::vector &pys = pinyin->get_pinyin(result); for (const std::string &s : pys) { rc = xToken(pCtx, FTS5_TOKEN_COLOCATED, s.c_str(), (int)s.length(), start, index); } diff --git a/src/simple_tokenizer.h b/src/simple_tokenizer.h index bea697a..9c99422 100644 --- a/src/simple_tokenizer.h +++ b/src/simple_tokenizer.h @@ -26,13 +26,14 @@ enum class TokenCategory { class SimpleTokenizer { private: - static PinYin *get_pinyin(); + static std::shared_ptr get_pinyin(); bool enable_pinyin = true; public: SimpleTokenizer(const char **zaArg, int nArg); int tokenize(void *pCtx, int flags, const char *text, int textLen, xTokenFn xToken) const; static std::string tokenize_query(const char *text, int textLen, int flags = 1); + static bool set_pinyin_dict(const std::string &pinyin_file_path, std::string &err); #ifdef USE_JIEBA static std::string tokenize_jieba_query(const char *text, int textLen, int flags = 1); #endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e3e1e82..1f52d2b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.2) +cmake_minimum_required(VERSION 3.19) project(simple_tests) # https://github.com/maps-gpu/MAPS/issues/7#issuecomment-418200278 @@ -8,13 +8,20 @@ cmake_policy(SET CMP0057 NEW) set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/src) # Download and unpack googletest at configure time +set(GTEST_CMAKE_ARGS + "-DCMAKE_POLICY_DEFAULT_CMP0048=NEW" +) configure_file(CMakeLists.txt.in googletest-download/CMakeLists.txt) -execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . +execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" ${GTEST_CMAKE_ARGS} . RESULT_VARIABLE result WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download ) if(result) message(FATAL_ERROR "CMake step for googletest failed: ${result}") endif() + +# Set policy version minimum before adding googletest +set(CMAKE_POLICY_VERSION_MINIMUM "3.5" CACHE STRING "Minimum CMake policy version" FORCE) + execute_process(COMMAND ${CMAKE_COMMAND} --build . RESULT_VARIABLE result WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download ) @@ -32,13 +39,6 @@ add_subdirectory(${CMAKE_CURRENT_BINARY_DIR}/googletest-src ${CMAKE_CURRENT_BINARY_DIR}/googletest-build EXCLUDE_FROM_ALL) -# The gtest/gtest_main targets carry header search path -# dependencies automatically when using CMake 2.8.11 or -# later. Otherwise we have to add them here ourselves. -if (CMAKE_VERSION VERSION_LESS 2.8.11) - include_directories("${gtest_SOURCE_DIR}/include") -endif() - include_directories(${SIMPLE_HEADERS_DIR}) include_directories(${SQLITE3_HEADERS_DIR}) set(SOURCE_FILES main.cpp pinyin_test.cc tokenizer_test.cc) diff --git a/test/CMakeLists.txt.in b/test/CMakeLists.txt.in index ad17f66..ab7d9df 100644 --- a/test/CMakeLists.txt.in +++ b/test/CMakeLists.txt.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.2) +cmake_minimum_required(VERSION 3.19) cmake_policy(SET CMP0057 NEW) project(googletest-download NONE) diff --git a/test/pinyin_test.cc b/test/pinyin_test.cc index 2c74e4e..2a57025 100644 --- a/test/pinyin_test.cc +++ b/test/pinyin_test.cc @@ -1,5 +1,9 @@ #include "pinyin.h" +#include +#include +#include + #include "gtest/gtest.h" using namespace simple_tokenizer; @@ -15,3 +19,35 @@ TEST(simple, pinyin_split) { for (auto r : res) std::cout << r << "\t"; std::cout << std::endl; } + +TEST(simple, pinyin_custom_file) { + std::string path = "simple_custom_pinyin_test.txt"; + std::ofstream file(path); + ASSERT_TRUE(file.is_open()); + file << "# custom pinyin file\n"; + file << "U+6770: jié # trailing comment\n"; + file.close(); + + PinYin pinyin(path); + auto res = pinyin.get_pinyin("杰"); + ASSERT_EQ(res.size(), 2); + ASSERT_EQ(res[0], "j"); + ASSERT_EQ(res[1], "jie"); + std::remove(path.c_str()); +} + +TEST(simple, pinyin_invalid_custom_file) { + std::string path = "simple_invalid_pinyin_test.txt"; + std::ofstream file(path); + ASSERT_TRUE(file.is_open()); + file << "invalid line\n"; + file.close(); + + EXPECT_THROW( + { + PinYin pinyin(path); + (void)pinyin; + }, + std::runtime_error); + std::remove(path.c_str()); +} diff --git a/test/tokenizer_test.cc b/test/tokenizer_test.cc index 0246cc3..6f6f4f3 100644 --- a/test/tokenizer_test.cc +++ b/test/tokenizer_test.cc @@ -18,6 +18,8 @@ TEST(simple, tokenizer_with_pinyin) { query.push_back(R"VAGON(( z+h+o+u* OR zhou* ) AND "杰" AND "伦")VAGON"); arr.push_back("杰伦 zhou 123"); query.push_back(R"VAGON("杰" AND "伦" AND ( z+h+o+u* OR zhou* ) AND "123"*)VAGON"); + arr.push_back("c#"); + query.push_back(R"VAGON(c* AND "#")VAGON"); for (int i = 0; i < arr.size(); i++) { std::string s = arr[i]; std::cout << s << " as doc:\n"; @@ -66,6 +68,8 @@ TEST(simple, jieba_tokenizer_with_pinyin) { query.push_back(R"VAGON(( z+h+o+u* OR zhou* ) AND "杰伦")VAGON"); arr.push_back("杰伦 zhou 123"); query.push_back(R"VAGON("杰伦" AND ( z+h+o+u* OR zhou* ) AND "123"*)VAGON"); + arr.push_back("c#"); + query.push_back(R"VAGON("c#")VAGON"); for (int i = 0; i < arr.size(); i++) { std::string s = arr[i]; std::cout << s << " as doc:\n";