// Считываем символы до закрывающей ) // Возвращает true, если последний токен перед ) был терминатором предложения. bool SentenceBroker::ReadCharsUntilClosingParen( lem::UFString & line ) { int n_paren=1; lem::UCString unbreakable; bool last_is_sentence_terminator=false; while( !Eof() || !chars.empty() ) { wchar_t c = GetChar(); if( c==WEOF ) { eof=true; break; } if( lem::is_uspace(c) ) { line.Add_Dirty(L' '); if( line.length()>=max_sentence_length ) break; } else { // считаем символ или группу неразрывных символов. ReadCharOrUnbreakable(c,unbreakable); if( unbreakable==L')' ) { line += unbreakable.front(); n_paren--; if( !n_paren ) break; } else if( unbreakable==L'(' ) { line.Add_Dirty( unbreakable.front() ); n_paren++; last_is_sentence_terminator=false; } else { line.Add_Dirty( unbreakable.c_str() ); last_is_sentence_terminator = sent_delims.find(unbreakable)!=UNKNOWN; } if( line.length()>=max_sentence_length*2 ) break; } } line.calc_hash(); return last_is_sentence_terminator; }
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.Add_Dirty(c); continue; } else if( IsEndOfSentenceMarker(c) ) { line.Add_Dirty(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.Add_Dirty( substr.c_str() ); c = c2; // Считанный токен является разделителем предложений (типа ...) if( sent_delims.find(substr)!=UNKNOWN ) { line.calc_hash(); 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.Add_Dirty( substr1.c_str() ); // Считанный токен является разделителем предложений (типа ...) if( sent_delims.find(substr1)!=UNKNOWN ) { if( (n_quote%2)==0 ) { line.calc_hash(); 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.Add_Dirty( 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.Add_Dirty( c ); line.Add_Dirty( c2 ); const wchar_t c4 = GetChar(); if( sent_delims1.find(c4)!=UNKNOWN ) { line.Add_Dirty( 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.Add_Dirty(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.Add_Dirty( c ); line.Add_Dirty( 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.Add_Dirty( c ); add_char=false; while( c!=WEOF ) { c = GetChar(); line.Add_Dirty( 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.Add_Dirty( c ); add_char=false; while( c!=WEOF && c==c0 ) { c = GetChar(); line.Add_Dirty( c ); c = PeekChar(); } } } else if( c=='!' || c=='?' ) { wchar_t c0 = c; c2 = PeekChar(); if( c2==L'?' || c2==L'!' ) // То есть токены типа !!! и !? { line.Add_Dirty( c ); add_char=false; while( c!=WEOF && (c==L'!' || c==L'?') ) { c = GetChar(); line.Add_Dirty( c ); c = PeekChar(); } } } } if( c==WEOF ) { eof = true; add_char=false; } if( breaker ) { line_ready=true; } if( add_char ) line.Add_Dirty( c ); } } else if( line.length()>max_sentence_length && (lem::is_uspace(c) || c==L',' || c==L'-' || c==L';' || c==L':' ) ) { // Слишком длинные предложения обрываем на безопасных символах. line_ready=true; line.Add_Dirty( c ); } else if( c==L'\r' || c==L'\n' || c==L'\t' || c==L'\b' ) { // некоторые управляющие символы заменяем пробелами line.Add_Dirty( L' ' ); } else { line.Add_Dirty( 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.calc_hash(); 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.calc_hash(); 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; }