forked from openkylin/ukui-search
361 lines
14 KiB
C++
361 lines
14 KiB
C++
/*
|
||
* Copyright (C) 2022, KylinSoft Co., Ltd.
|
||
*
|
||
* This program is free software: you can redistribute it and/or modify
|
||
* it under the terms of the GNU General Public License as published by
|
||
* the Free Software Foundation, either version 3 of the License, or
|
||
* (at your option) any later version.
|
||
*
|
||
* This program is distributed in the hope that it will be useful,
|
||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
* GNU General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU General Public License
|
||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||
*
|
||
* Authors: jixiaoxu <jixiaoxu@kylinos.cn>
|
||
*
|
||
*/
|
||
|
||
#include <mutex>
|
||
#include <cctype>
|
||
#include "hanzi-to-pinyin.h"
|
||
#include "hanzi-to-pinyin-private.h"
|
||
#include "chinese-segmentation.h"
|
||
#include "cppjieba/Unicode.hpp"
|
||
|
||
HanZiToPinYin * HanZiToPinYin::g_pinYinManager = nullptr;
|
||
std::once_flag g_singleFlag;
|
||
|
||
bool HanZiToPinYinPrivate::contains(string &word)
|
||
{
|
||
return m_pinYinTrie.Contains(word);
|
||
}
|
||
|
||
int HanZiToPinYinPrivate::getResults(string &word, QStringList &results)
|
||
{
|
||
results.clear();
|
||
|
||
string directResult = m_pinYinTrie.Find(word);
|
||
|
||
if (directResult == string()) {
|
||
if (m_segType == SegType::NoSegmentation) {//无分词、无结果直接返回-1
|
||
return -1;
|
||
} else {//无结果、启用分词
|
||
vector<string> segResults = ChineseSegmentation::getInstance()->callMixSegmentCutStr(word);
|
||
string data;
|
||
for (string &info : segResults) {
|
||
if (info == string()) {
|
||
continue;
|
||
}
|
||
data = m_pinYinTrie.Find(info);
|
||
if (data == string()) {//分词后无结果
|
||
if (cppjieba::IsSingleWord(info)) {//单个字符
|
||
if (m_exDataProcessType == ExDataProcessType::Default) {//原数据返回
|
||
results.append(QString().fromStdString(info));
|
||
} else if (m_exDataProcessType == ExDataProcessType::Delete) {//忽略
|
||
continue;
|
||
}
|
||
} else {//多个字符
|
||
string oneWord;
|
||
cppjieba::RuneStrArray runeArray;
|
||
cppjieba::DecodeRunesInString(info, runeArray);
|
||
for (auto i = runeArray.begin(); i != runeArray.end(); ++i) {
|
||
oneWord = cppjieba::GetStringFromRunes(info, i, i);
|
||
data = m_pinYinTrie.Find(oneWord);
|
||
if (data == string()) {//单字无结果则按设置返回
|
||
if (m_exDataProcessType == ExDataProcessType::Default) {//原数据返回
|
||
results.append(QString().fromStdString(oneWord));
|
||
} else if (m_exDataProcessType == ExDataProcessType::Delete) {//忽略
|
||
continue;
|
||
}
|
||
}
|
||
if (m_polyphoneType == PolyphoneType::Enable) {//启用多音字
|
||
results.append(QString().fromStdString(data));
|
||
} else if (m_polyphoneType == PolyphoneType::Disable) {//不启用多音字
|
||
if (limonp::IsInStr(data, ',')) {
|
||
results.append(QString().fromStdString(data.substr(0, data.find_first_of(",", 0))));
|
||
} else {
|
||
results.append(QString().fromStdString(data));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} else {//分词后有结果
|
||
if (cppjieba::IsSingleWord(info)) {//单个字符
|
||
if (m_polyphoneType == PolyphoneType::Enable) {//启用多音字
|
||
results.append(QString().fromStdString(data));
|
||
} else if (m_polyphoneType == PolyphoneType::Disable) {//不启用多音字
|
||
if (limonp::IsInStr(data, ',')) {
|
||
results.append(QString().fromStdString(data.substr(0, data.find_first_of(",", 0))));
|
||
} else {
|
||
results.append(QString().fromStdString(data));
|
||
}
|
||
}
|
||
} else {//多个字符
|
||
vector<string> dataVec = limonp::Split(data, "/");
|
||
if (dataVec.size() == 1) {//无多音词
|
||
vector<string> dataVec = limonp::Split(data, ",");
|
||
for (auto &oneResult : dataVec) {
|
||
results.append(QString().fromStdString(oneResult));
|
||
}
|
||
} else {
|
||
if (m_polyphoneType == PolyphoneType::Enable) {//启用多音字
|
||
int wordSize = limonp::Split(dataVec[0], ",").size();
|
||
for (int i = 0; i < wordSize; ++i) {
|
||
QStringList oneResult;
|
||
for (size_t j = 0; j < dataVec.size(); ++j) {
|
||
oneResult.append(QString().fromStdString(limonp::Split(dataVec[j], ",")[i]));
|
||
}
|
||
results.append(oneResult.join('/'));
|
||
}
|
||
} else if (m_polyphoneType == PolyphoneType::Disable) {//不启用多音字
|
||
vector<string> tmp = limonp::Split(dataVec[0], ",");
|
||
for (auto &oneResult : tmp) {
|
||
results.append(QString().fromStdString(oneResult));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} else {//可以直接查到结果
|
||
if (cppjieba::IsSingleWord(word)) {//单个字符
|
||
if (m_polyphoneType == PolyphoneType::Enable) {//启用多音字
|
||
results.append(QString().fromStdString(directResult));
|
||
} else if (m_polyphoneType == PolyphoneType::Disable) {//不启用多音字
|
||
if (limonp::IsInStr(directResult, ',')) {
|
||
results.append(QString().fromStdString(directResult.substr(0, directResult.find_first_of(",", 0))));
|
||
} else {
|
||
results.append(QString().fromStdString(directResult));
|
||
}
|
||
}
|
||
} else {//多个字符
|
||
vector<string> dataVec = limonp::Split(directResult, "/");
|
||
if (dataVec.size() == 1) {//无多音词
|
||
vector<string> dataVec = limonp::Split(directResult, ",");
|
||
for (auto &oneResult : dataVec) {
|
||
results.append(QString().fromStdString(oneResult));
|
||
}
|
||
} else {
|
||
if (m_polyphoneType == PolyphoneType::Enable) {//启用多音字
|
||
int wordSize = limonp::Split(dataVec[0], ",").size();
|
||
for (int i = 0; i < wordSize; ++i) {
|
||
QStringList oneResult;
|
||
for (size_t j = 0; j < dataVec.size(); ++j) {
|
||
oneResult.append(QString().fromStdString(limonp::Split(dataVec[j], ",")[i]));
|
||
}
|
||
results.append(oneResult.join('/'));
|
||
}
|
||
} else if (m_polyphoneType == PolyphoneType::Disable) {//不启用多音字
|
||
vector<string> tmp = limonp::Split(dataVec[0], ",");
|
||
for (auto &oneResult : tmp) {
|
||
results.append(QString().fromStdString(oneResult));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
convertDataStyle(results);
|
||
return 0;//todo
|
||
}
|
||
|
||
void HanZiToPinYinPrivate::setConfig(PinyinDataStyle dataStyle, SegType segType, PolyphoneType polyphoneType, ExDataProcessType processType)
|
||
{
|
||
m_pinyinDataStyle = dataStyle;
|
||
m_segType = segType;
|
||
m_polyphoneType = polyphoneType;
|
||
m_exDataProcessType = processType;
|
||
}
|
||
|
||
void HanZiToPinYinPrivate::convertDataStyle(QStringList &results)
|
||
{
|
||
QString value;
|
||
if (m_pinyinDataStyle == PinyinDataStyle::Default) {
|
||
for (QString &info : results) {
|
||
if(info == ",") {
|
||
continue;
|
||
}
|
||
//if info's length was been changed, there's someting wrong while traverse the chars of info
|
||
for (const QChar &c : info) {
|
||
if (!isalpha(c.toLatin1())) {
|
||
value = PhoneticSymbol.value(c);
|
||
if (!value.isEmpty()) {
|
||
info.replace(c, value.at(0));
|
||
}
|
||
}
|
||
}
|
||
|
||
QStringList tmpList = info.split(',', QString::SkipEmptyParts); //去重(保持原顺序)
|
||
QStringList tmpValue;
|
||
for (auto &str : tmpList) {
|
||
if (!tmpValue.contains(str)) {
|
||
tmpValue.push_back(str);
|
||
}
|
||
}
|
||
info = tmpValue.join(",");
|
||
}
|
||
} else if (m_pinyinDataStyle == PinyinDataStyle::Tone) {
|
||
//无需处理
|
||
} else if (m_pinyinDataStyle == PinyinDataStyle::Tone2) {
|
||
for (QString &info : results) {
|
||
for (int i = 0; i < info.size();) {
|
||
auto c = info.at(i);
|
||
if (!isalpha(c.toLatin1())) {
|
||
value = PhoneticSymbol.value(c);
|
||
if (!value.isEmpty()) {
|
||
info.replace(c, PhoneticSymbol.value(c));
|
||
i += PhoneticSymbol.value(c).size();
|
||
continue;
|
||
}
|
||
}
|
||
i++;
|
||
}
|
||
}
|
||
} else if (m_pinyinDataStyle == PinyinDataStyle::Tone3) {
|
||
for (QString &info : results) {
|
||
if(info == "/") {
|
||
continue;
|
||
}
|
||
bool isPolyphoneWords(false);
|
||
if (info.contains("/")) {
|
||
isPolyphoneWords = true;
|
||
info.replace("/", ",");
|
||
}
|
||
|
||
for (int i = 0; i < info.size();) {
|
||
auto c = info.at(i);
|
||
if (!isalpha(c.toLatin1())) {
|
||
value = PhoneticSymbol.value(c);
|
||
if (!value.isEmpty()) {
|
||
info.replace(i, 1, value.at(0));
|
||
//多音词模式
|
||
if (info.contains(",")) {
|
||
int pos = info.indexOf(',', i);
|
||
if (isPolyphoneWords) {
|
||
info.replace(",", "/");
|
||
}
|
||
//最后一个读音时
|
||
if (pos == -1) {
|
||
info.append(value.at(1));
|
||
break;
|
||
}
|
||
info.insert(pos, value.at(1));
|
||
i = pos + 1; //insert导致','的位置加一,将i行进到','的位置
|
||
i++;
|
||
continue;
|
||
} else {
|
||
info.append(value.at(1));
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
i++;
|
||
}
|
||
|
||
}
|
||
} else if (m_pinyinDataStyle == PinyinDataStyle::FirstLetter) {
|
||
for (QString &info : results) {
|
||
if(info == "," or info == "/") {
|
||
continue;
|
||
}
|
||
|
||
bool isPolyphoneWords(false);
|
||
if (info.contains("/")) {
|
||
isPolyphoneWords = true;
|
||
info.replace("/", ",");
|
||
}
|
||
|
||
for (int i = 0; i < info.size();i++) {
|
||
auto c = info.at(i);
|
||
if (!isalpha(c.toLatin1())) {
|
||
value = PhoneticSymbol.value(c);
|
||
if (!value.isEmpty()) {
|
||
info.replace(c, value.at(0));
|
||
}
|
||
}
|
||
}
|
||
|
||
QStringList tmpList = info.split(',', QString::SkipEmptyParts); //去重(保持原顺序)
|
||
QStringList tmpValue;
|
||
for (auto &str : tmpList) {
|
||
if (!tmpValue.contains(str)) {
|
||
tmpValue.push_back(str.at(0));
|
||
}
|
||
}
|
||
if (isPolyphoneWords) {
|
||
info = tmpValue.join("/");
|
||
} else {
|
||
info = tmpValue.join(",");
|
||
}
|
||
}
|
||
} else if (m_pinyinDataStyle == PinyinDataStyle::English) {
|
||
//暂不支持
|
||
}
|
||
}
|
||
|
||
HanZiToPinYinPrivate::HanZiToPinYinPrivate(HanZiToPinYin *parent) : q(parent)
|
||
{
|
||
//const char * const SINGLE_WORD_PINYIN_PATH = "/usr/share/ukui-search/res/dict/singleWordPinyin.txt";
|
||
//const char * const WORDS_PINYIN_PATH = "/usr/share/ukui-search/res/dict/wordsPinyin.txt";
|
||
//m_pinYinTrie = new Pinyin4cppDictTrie(SINGLE_WORD_PINYIN_PATH, WORDS_PINYIN_PATH);
|
||
//m_pinYinTrie = new Pinyin4cppTrie;
|
||
}
|
||
|
||
HanZiToPinYinPrivate::~HanZiToPinYinPrivate()
|
||
{
|
||
// if (m_pinYinTrie){
|
||
// delete m_pinYinTrie;
|
||
// m_pinYinTrie = nullptr;
|
||
// }
|
||
}
|
||
|
||
HanZiToPinYin * HanZiToPinYin::getInstance()
|
||
{
|
||
call_once(g_singleFlag, []() {
|
||
g_pinYinManager = new HanZiToPinYin;
|
||
});
|
||
return g_pinYinManager;
|
||
}
|
||
|
||
bool HanZiToPinYin::contains(string &word)
|
||
{
|
||
return d->contains(word);
|
||
}
|
||
|
||
bool HanZiToPinYin::isMultiTone(string &word)
|
||
{
|
||
return d->isMultiTone(word);
|
||
}
|
||
|
||
bool HanZiToPinYin::isMultiTone(string &&word)
|
||
{
|
||
return d->isMultiTone(word);
|
||
}
|
||
|
||
bool HanZiToPinYin::isMultiTone(const string &word)
|
||
{
|
||
return d->isMultiTone(word);
|
||
}
|
||
|
||
bool HanZiToPinYin::isMultiTone(const string &&word)
|
||
{
|
||
return d->isMultiTone(word);
|
||
}
|
||
|
||
int HanZiToPinYin::getResults(string word, QStringList &results)
|
||
{
|
||
return d->getResults(word, results);
|
||
}
|
||
|
||
void HanZiToPinYin::setConfig(PinyinDataStyle dataStyle, SegType segType, PolyphoneType polyphoneType, ExDataProcessType processType)
|
||
{
|
||
d->setConfig(dataStyle, segType, polyphoneType, processType);
|
||
}
|
||
|
||
HanZiToPinYin::HanZiToPinYin() : d(new HanZiToPinYinPrivate)
|
||
{
|
||
}
|