Онлайн час в Google Meet
Лого на 91. НЕГ „Проф. Константин Гълъбов“

11. клас · Избираем модул · Урок 25

Анализ на текст на C#

Честотен речник с Dictionary<string, int> и класификатор на тона по списъци от думи — третата програма на C# за годината. Разбиване на думи, броене, подреждане, класификация на съобщения.

От тетрадката към програмата

Миналия час броихте думи на ръка. Днес това го прави програмата — и то за текст с хиляди думи за части от секундата. Това е третата и последна програма на C# за годината (след съветника по правила и перцептрона) и влиза в оценката като тях: работи върху дадените примери, издържа граничните случаи, обяснявате всеки ред.

Програмата има две части: честотен речник — кои думи колко пъти, — и класификатор по списък от думи, който казва дали съобщението е с положителен, отрицателен или неутрален тон.

Стъпка 1: от текст към думи

Както на хартия: малки букви, махаме препинателните знаци, режем по интервалите. В C# трите неща са един ред:

char[] razdeliteli = { ' ', '.', ',', '!', '?', ';', ':', '-', '"' };
string[] dumi = tekst.ToLower().Split(razdeliteli, StringSplitOptions.RemoveEmptyEntries);
  • ToLower() прави всички букви малки — работи и с кирилица.
  • Split(razdeliteli, …) реже низа при всеки от изброените знаци. Така препинателните знаци изчезват заедно с интервалите.
  • RemoveEmptyEntries изхвърля празните парчета — иначе „лае.“ с интервал след точката би дало празна „дума“.

Стъпка 2: честотен речник

Dictionary<string, int>

Колекция „ключ → стойност“: за всеки ключ (дума) пази една стойност (брой). Търсенето по ключ е почти мигновено, независимо колко думи има вътре.

Dictionary<string, int> chestoti = new Dictionary<string, int>();
foreach (string duma in dumi)
{
    if (chestoti.ContainsKey(duma)) chestoti[duma]++;   // виждали сме я → +1
    else chestoti[duma] = 1;                            // нова дума → 1
}

След цикъла chestoti.Count е броят различни думи, а chestoti["котката"] — колко пъти се среща „котката“.

Подреждане по честота

Речникът няма ред. За „топ 5“ го подреждаме: първо по честота (намаляващо), а при равна честота — по азбучен ред, за да е изходът винаги един и същ:

var podredeni = chestoti
    .OrderByDescending(p => p.Value)                  // по брой, най-честите отпред
    .ThenBy(p => p.Key, StringComparer.Ordinal)       // при равенство — по азбука
    .Take(5);                                         // само първите пет

foreach (var p in podredeni)
    Console.WriteLine($"{p.Key,-10} {p.Value}");
  • p е двойка ключ–стойност; p.Key е думата, p.Value — броят.
  • StringComparer.Ordinal сравнява по кода на буквите — за малки кирилски букви това съвпада с азбучния ред и не зависи от настройките на компютъра.
  • {p.Key,-10} подравнява думата в поле от 10 знака — за да се четат колоните. Трябва using System.Linq;.

Стъпка 3: класификатор по списък от думи

Идеята е от урок 5 — правила, написани от човек. Два списъка: положителни и отрицателни думи. За всяка дума в съобщението: ако е в първия списък, +1; ако е във втория, −1. Сборът е оценката на тона: над нула — положителен, под нула — отрицателен, нула — неутрален.

static string[] polozhitelni = { "страхотен", "харесах", "обичам", "супер", "благодаря" };
static string[] otricatelni = { "ужасен", "мразя", "скучен", "проблем", "зле" };

static int Tochki(string izrechenie)
{
    int tochki = 0;
    foreach (string duma in Dumi(izrechenie))
    {
        if (Array.IndexOf(polozhitelni, duma) >= 0) tochki++;
        if (Array.IndexOf(otricatelni, duma) >= 0) tochki--;
    }
    return tochki;
}

Array.IndexOf връща позицията на думата в списъка или −1, ако я няма. Това е цялото „разпознаване“ — сравнение на низове.

Списък от думи или обучен модел

Нашият класификатор е торба с думи плюс ръчно написани тегла: +1 за „супер“, −1 за „ужасен“, 0 за всичко останало. Спам филтърът в пощата ви прави абсолютно същата сметка — но теглата на думите не са писани от човек, а научени от милиони писма с етикет „спам“ / „не спам“.

Какво трябва да запомниш

  • ToLower() + Split(разделители, RemoveEmptyEntries) превръщат текста в масив от думи.
  • Dictionary<string, int> брои думите: ContainsKey, после ++ или = 1.
  • Подреждане по честота, а при равенство по азбука (Ordinal) дава еднозначен изход.
  • Класификаторът по списък събира +1 и −1 за думите от два списъка; знакът на сбора е тонът.
  • Списъкът греши при отрицание, при непредвидени форми на думите и при ирония — това са граничните случаи.
  • Обученият спам филтър е същата сметка с тегла, научени от примери — перцептрон върху торба с думи.