bool SentenceBroker::Fetch(lem::UFString &line, int & line_paragraph_id) { line.clear(); line_paragraph_id = cur_paragraph_id; if (eof) return false; int n_quote = 0; // для учета символов " // Пропустим начальные пробелы. while (!eof) { wchar_t c = GetChar(); if (c == WEOF) { break; } if (!lem::is_uspace(c) || IsEndOfSentenceMarker(c)) { UngetChar(c); break; } } while (!eof || !chars.empty()) { wchar_t c = GetChar(); if (c == WEOF) { eof = true; return true; } else if (c == L' ') { line += c; continue; } else if (IsEndOfSentenceMarker(c)) { line += c; break; } bool line_ready = false; if ( (line.empty() || IsTokenDelimiter(line.back()) || IsTokenDelimiter(c)) && tokenizer.NotNull() && tokenizer->IsUnbreakableFront(c) ) { // Возможно далее идет исключительный случай. Выбираем символы из входного потока в // попытке сконструировать максимально длинное исключение. lem::UCString substr; substr = c; while (!eof) { wchar_t c2 = GetChar(); if (c2 == WEOF) { // Достигли конца файла. Считали полный исключительный случай? if (tokenizer->IsMatched(substr)) { // Да! line += substr.c_str(); c = c2; // Считанный токен является разделителем предложений (типа ...) if (sent_delims.find(substr) != UNKNOWN) { line.trim(); if (!line.empty() && (n_quote % 2) == 0) { count++; return true; } } } else { // нет - вернем накопленные символы в поток чтения for (int k = substr.length() - 1; k >= 0; --k) UngetChar(substr[k]); c = GetChar(); } break; } substr += c2; // добавили еще один символ. // С символов substr начинается хоть одно исключение? if (!tokenizer->IsUnbreakableFront(substr) || substr.length() == lem::UCString::max_len) { // Нет. // Возможно, предыдущая подстрока является исключительной ситуацией. UCString substr1 = lem::left(substr, substr.length() - 1); if (tokenizer->IsMatched(substr1) && IsTokenDelimiter(substr.back())) { // Да! line += substr1.c_str(); // Считанный токен является разделителем предложений (типа ...) if (sent_delims.find(substr1) != UNKNOWN) { if ((n_quote % 2) == 0) { line.trim(); if (!line.empty()) { UngetChar(c2); count++; return true; } } else { if (c2 == L'"') { // Ситуация типа Кошка говорит "Мяу!" Собака говорит "Гав!" bool continuation_found = true; lem::MCollect<wchar_t> tmp_chars; while (!eof || !chars.empty()) { const wchar_t c4 = GetChar(); tmp_chars.push_back(c4); if (!lem::is_uspace(c4)) { if (IsUpperChar(c4)) { continuation_found = false; } break; } } for (int k = CastSizeToInt(tmp_chars.size()) - 1; k >= 0; --k) UngetChar(tmp_chars[k]); if (!continuation_found) { line += c2; return true; } } } } #if defined SOL_CAA // Если считанный токен делит предложения в случае, когда за ним // идет слово с первой заглавной буквой. if (use_lexicon && casing_sent_delim.find(to_lower(substr1)) != UNKNOWN && CharCasingCoord != UNKNOWN && LowerCasingState != UNKNOWN) { Lexem next_token = PickNextToken(); if (IsUpperChar(next_token.front())) { la->TranslateLexem(next_token); int ie = UNKNOWN; if (seeker != NULL) { ie = seeker->Find(next_token, false); } else { MCollect<Word_Coord> found_list; la->ProjectWord(next_token, found_list, LanguageID); if (!found_list.empty()) ie = found_list.front().GetEntry(); } if (ie != UNKNOWN) { const Solarix::SG_Entry &e = sg->GetEntry(ie); const int casing = e.attrs().FindOnce(Solarix::GramCoordAdr(CharCasingCoord)); if (casing == LowerCasingState || casing == UNKNOWN) { UngetChar(c2); count++; return true; } } } } #endif c = c2; } else { // Возвращаем все загруженные символы обратно в поток. for (int k = substr.length() - 1; k >= 0; --k) UngetChar(substr[k]); c = GetChar(); } break; } } } if (c == WEOF) { // закончился исходный текст, принудительно прерываем текущее предложение. eof = true; return true; } const bool ItIsSentDelim = sent_delims1.find(c) != UNKNOWN; if (ItIsSentDelim) { if (IsEndOfSentenceMarker(c)) break; // Обычный конец предложения. Для точки надо проверять, если сразу после точки идет цифра // или символ в нижнем регистре, то это НЕ конец предложения. bool breaker = false; bool add_char = true; // Если у нас есть незакрытая пара ", то проверим следующий непустой символ. if ((n_quote % 2) != 0) { const wchar_t c2 = GetChar(); // Если это закрывающая " if (c2 == L'"') { n_quote++; line += c; line += c2; const wchar_t c4 = GetChar(); if (sent_delims1.find(c4) != UNKNOWN) { line += c4; count++; return true; } else { UngetChar(c4); } // если дальше - пробел, и после него идет символ в нижнем регистре, то это не конец предложения. bool continuation_found = true; UFString tmp_chars; while (!eof) { const wchar_t c5 = GetChar(); if (c5 == WEOF) { break; } tmp_chars += c5; if (!lem::is_uspace(c5)) { // найден не-пробельный символ. if (IsUpperChar(c5)) { continuation_found = false; } break; } } // возвращаем все символы обратно for (int i = CastSizeToInt(tmp_chars.size()) - 1; i >= 0; --i) UngetChar(tmp_chars[i]); if (!continuation_found) { // обрываем предложение. line.trim(); count++; return true; } } else { // нет - продолжим считывание символов предложения. line += c; line += c2; } } else { #if defined SOL_CAA if (tokenizer.NotNull() && seeker.NotNull() && use_lexicon && c == L'.') { // надо выделить слово, предшествующее точке. идем влево до разделителя токенов. int icur = line.length() - 1; while (icur >= 0) { if (tokenizer->IsTokenDelimiter(line[icur])) break; // нашли начало последнего слова else icur--; // сдвигаемся влево } Solarix::Lexem last_word; for (int j = icur + 1; j < line.length(); ++j) last_word.Add_Dirty(line[j]); last_word.calc_hash(); la->TranslateLexem(last_word); if (seeker->Find(last_word, false) != UNKNOWN) { breaker = true; } } #endif if (!breaker) { breaker = true; wchar_t c2 = PeekChar(); if (c == L'.') { if (lem::is_udigit(c2)) { breaker = false; } else if (IsLowerChar(c2)) { breaker = false; } else if (c2 == L',') { breaker = false; } else if (lem::is_uspace(c2)) { // Дойдем до первого не-пробельного символа. line += c; add_char = false; while (c != WEOF) { c = GetChar(); line += c; if (IsEndOfSentenceMarker(c)) break; c = PeekChar(); if (!lem::is_uspace(c)) { if (IsLowerChar(c)) { breaker = false; } break; } } c = L'.'; } wchar_t c0 = c; c2 = PeekChar(); if (c2 == c0) { line += c; add_char = false; while (c != WEOF && c == c0) { c = GetChar(); line += c; c = PeekChar(); } } } else if (c == '!' || c == '?') { wchar_t c0 = c; c2 = PeekChar(); if (c2 == L'?' || c2 == L'!') // То есть токены типа !!! и !? { line += c; add_char = false; while (c != WEOF && (c == L'!' || c == L'?')) { c = GetChar(); line += c; c = PeekChar(); } } } } if (c == WEOF) { eof = true; add_char = false; } if (breaker) { line_ready = true; } if (add_char) line += c; } } else if (line.length() > max_sentence_length && (lem::is_uspace(c) || c == L',' || c == L'-' || c == L';' || c == L':')) { // Слишком длинные предложения обрываем на безопасных символах. line_ready = true; line += c; } else if (c == L'\r' || c == L'\n' || c == L'\t' || c == L'\b') { // некоторые управляющие символы заменяем пробелами line += L' '; } else { line += c; if (c == L'"') n_quote++; else if (c == L'(') { // если предложение начинается с (, то надо смотреть, какой токен будет перед ), и если это терминатор - обрывать предложение. if (line.size() == 1) { if (ReadCharsUntilClosingParen(line)) { line.trim(); return true; } } else { ReadCharsUntilClosingParen(line); } } } if (line_ready) { if (line.length() > 0 && IsEndOfParagraphMarker(line.last_char())) { line.remove(line.length() - 1); cur_paragraph_id++; } line.trim(); if (!line.empty()) count++; return true; } } if (line.length() > 0 && IsEndOfParagraphMarker(line.last_char())) { line.remove(line.length() - 1); cur_paragraph_id++; } line.trim(); if (!line.empty()) count++; return true; }
void SG_ComplexLink::LoadPoint( Macro_Parser &txtfile, SynGram &gram, lem::UFString &entry ) { BethToken t = txtfile.read(); if( t.GetToken()==B_ENTRY ) { // Особый формат entry Класс:Статья { уточнение } // преобразуется в ключ статьи и возвращается в виде #ключ UCString class0 = txtfile.read().string(); const int ic0 = class0==L"?" ? ANY_STATE : gram.FindClass(class0); if( ic0==UNKNOWN ) { Print_Error( txtfile ); gram.GetIO().merr().printf( "Unknown class %us\n", class0.c_str() ); throw E_BaseException(); } txtfile.read_it( B_COLON ); UCString entry0 = sol_read_multyname( gram.GetIO(), txtfile, B_OFIGPAREN ); entry0.strip(L'"'); entry0.trim(); // Может быть задана дополнительная фильтрующая координата Solarix::CP_Array coords0; coords0.LoadTxt( txtfile, gram ); if( gram.IsOmonym(ic0,lem::to_upper(entry0)) && coords0.empty() ) { Print_Error( txtfile ); gram.GetIO().merr().printf( "Omonym %us:%us requires the coordinate array\n", class0.c_str(), entry0.c_str() ); throw E_BaseException(); } const int ie0 = coords0.empty() ? gram.FindEntry(entry0,ic0,false) : gram.FindEntryOmonym(entry0,ic0,coords0); if( ie0==UNKNOWN ) { Print_Error( txtfile ); gram.GetIO().merr().printf( "Unknown entry %us:%us\n", class0.c_str(), entry0.c_str() ); throw E_BaseException(); } const int ekey = gram.GetEntry(ie0).GetKey(); entry = lem::format_str( L"#%d", ekey ); return; } bool figparen = t.GetToken()==B_OFIGPAREN; if( !figparen ) txtfile.seekp(t); entry.reserve(128); if( t.string()==L'@' ) { entry = L'@'; t = txtfile.read(); } while( !txtfile.eof() ) { BethToken t = txtfile.read(); if( figparen && t.GetToken()==B_CFIGPAREN ) break; if( !entry.empty() ) entry.Add_Dirty(L' '); UFString ts( t.GetFullStr() ); ts.strip(L'"'); entry.Add_Dirty( ts ); if( !figparen ) break; } entry.calc_hash(); if( entry.front()==L'@' ) { // Спецсимвол @ заставляет запомнить строку в виде "как есть" entry.remove(0); entry.trim(); } else { entry.strip(L'"'); gram.GetDict().GetLexAuto().TranslateLexem(entry,true); } return; }