Խնդիրը բաղկացած է երկու մասից ա) տրված տեքստը տրոհել բառերի, և բ) հաշվել տեքստում ամեն մի բառի հանդիպելու հաճախությունը։
Ֆայլից սիմվոլ առ սիմվոլ տեքստը կարդալու և բառեր կազմելու հավես ես չունեի։ C լեզվի գրադարանային
strtok ֆունկցիան էլ տարբեր պատճառներով չէի ուզում օգտագործել։ Նպատակ ունեի օգտագործել C++ լեզվի istream դասը և այդ դասի համար սահմանված operator>> գործողությունը։ Ավելի պարզ ասած, ուզում էի, որ հետևյալ read_file ֆունկցիան filename ֆայլից կկարդա բառերը և կլցնի words վեկտորի մեջ.
void read_file( char* filename, std::vector<std::string>& words )
{
std::string w{ "" };
std::ifstream fin{ filename };
while( !fin.eof() ) {
fin >> w;
words.push_back( w );
}
fin.close();
}
Բայց պարզ է, որ words վեկտորը պարզապես լցվելու է տեքստի՝ բացատներով բաժանված հատվածներով, որովհետև լռելությամբ operator>> գործողությունը բաժանիչ (delimiter) է համարում միայն բացատները։Իմ խնդրի համար բաժանիչ պետք է համարել այբուբենի մեծատառերից ու փոքրատառերից տարբերովող բոլոր նիշերը։ Եվ
istream դասի օբյեկտը պետք է մի որևէ եղանակով կարգավորել այնպես, որ բաժանիչ համարվեն և անտեսվեն բոլոր ոչ պետքական նիշերը։
Ինտերնետում քչփորելուց հետո հասկացա, որ իմ ուզած բաժանիչները սահմանելու համար պետք է ստեղծեմ նոր locale օբյեկտ։ Հետո այդ locale-ի համար էլ սահմանեմ այնպիսի ctype ֆասետ (facet - սրա անունը այդպես էլ չհասկացա), որում արդեն այբուբենի տառերից տարբերվող նիշերին տրված է space դիմակը (mask)։ Ահա այդ նոր սահմանված դասը, որ ժառանգած է std::ctype<char>-ից։
class word_ctype : public std::ctype<char> {
private:
static const mask* custom_table()
{
mask* wcs = new mask[table_size];
std::copy_n(classic_table(), table_size, wcs);
for( int c = 32; c < table_size; ++c ) {
if( isalpha(c) ) continue;
wcs[c] = (mask)space;
}
return wcs;
}
public:
word_ctype( std::size_t refs = 0 )
: ctype(custom_table(), true, refs)
{}
};
Հետո արդեն ավելի հետաքրքիր մասն է։ Սահմանեցի create_dictionary ֆունկցիան, որի առաջին արգումենտը վերլուծվող ֆայլի անունն է, իսկ երկրորդը՝ կառուցվելիք բառարանի ֆայլի անունն է։ Ստացվեց համարյա ֆունկցիոնալ կոդ։
void create_dictionary( char* infile, char* outfile )
{
// բառարան է, որը հաշվում է ամեն մի բառի քանակը
std::map<std::string,int> dict;
// ընթերցման հոսքի ստեղծում՝ տրված ֆայլի անունով
std::ifstream fin{infile};
// ընթերցման հոսքում ներդնել նոր locale օբյեկտ՝ վերը սահմանված facet-ով
fin.imbue(std::locale{std::locale::classic(), new word_ctype});
// հոսքից կարդալու երկու իտերատորներ
std::istream_iterator sbegin(fin), send;
// կարդալ հոսքը սկզբից մինչև վերջ և բառերն ավելացնել բառարանում
std::for_each( sbegin, send, [&dict](std::string w){ ++dict[downcase(w)]; } );
fin.close();
// ստեղծել արտածման հոսք՝ բառարանը գրելու համար
std::ofstream fout{outfile};
// բառարանի ամեն մի գրառման համար ...
for( auto w : dict )
// ֆայլում գրել բառը և նրա քանակը
fout << w.first << ',' << w.second << std::endl;
fout.close();
}
Այս ֆունկցիայում հոսքից կարդացած բառի բոլոր տառերը փոքրատառ դարձնելու համար օգտագործված downcase ֆունկցիան սահմանված է հետևյալ կերպ։
std::string downcase( std::string sr )
{
std::transform( sr.begin(), sr.end(), sr.begin(), ::tolower );
return sr;
}
No comments:
Post a Comment