Реалізація додатки TextProcessor в Visual C # (Sharp)

При реалізації обробника тексту ми стикаємося з проблемою, яким чином виправити потік даних, щоб він був однорідним Основними причинами пріворечівих даних є помилки і недбалість оператора

На рис 103 показаний приклад виведення вмісту текстового файлу з номерами попередніх тиражів лотереї в текстовому редакторі Notepad (Блокнот) Очевидно, що вміст в такому вигляді не несе легко вловлюється смислової інформації для людей

Але зовнішній вигляд даних, виведених в Notepad, не є справжньою проблем Коли вміст файлу завантажене в інший текстовий редактор, наприклад Vim, то воно виглядає набагато більш інформативним (рис 104) Як можна веть, в Vim вміст файлу виводиться відформатованим належним чином

ПРИМІТКА

Текстовий редактор Vim можна завантажити з Web-сайтуhttp://wwwvimorg  Це клон версії для UNIX-систем, який можна використовувати на Windows-системах

Рис 103 Вміст файлу з лотерейними номерами в Notepad

Рис 104 Вміст файлу з лотерейними номерами в редакторі Vim

Але реальна нагальна проблема полягає в структурі даних (рис 105) Віо, що дані представлені в іншому форматі, з додатковими стовпцями, а формат дати в першому стовпці неправильний Що ще гірше, дані містять повторювану інформацію

Таким чином, нам необхідно, щоб додаток міг читати потік і Іспрі всі наявні в ньому проблемні аспекти Для цього необхідно мати оовательное розуміння процесу обробки рядків і різних способів зберігання тексту (Див розділ 3) При обробці потокових даних необхідно знати їх Фоат У даному прикладі ми працюємо з текстом у форматі ASCII і тому будемо маніпулювати бітами згідно з правилами таблиці відповідностей ASCII

Особливе місце в таблиці відповідностей займають недруковані символи Ці Сіол мають відповідні коди, але вони представляються не у вигляді друкованих символів, а у вигляді дії Наприклад, символ між одиночними лапками (1 1) представляє пробіл, символ \ t – табуляцію, а символ \ п – повернення Кареі з переходом на новий рядок Причина, по якій вміст файлу лотерея номерів не виводиться відформатованим в Notepad, полягає в непечаемих символах переходу на новий рядок На рис 106 виділений код ОА вказує символ перекладу рядка у файлі лотерейних номерів

А на рис 107 показано вміст файлу, створеного в Notepad Для переходу на новий рядок Notepad очікує не один символ, а два: OD і ОА

Рис 107 Символи переходу на новий рядок для Notepad

Розшифровка формату

На даному етапі у нас є додаток з належною потоком даних між компонентами, забезпечуваним відображенням Наступним кроком буде видалення коду відображення і заміна його кодом для виправлення похибок в потоці даних

Це завдання не з легких, т к тут нам знову доводиться мати справу з проблемою станів Ми не хочемо виправити одну частину потоку, тільки щоб отримати прлему в іншій його частині Тому процес виправлення повинен здійснюватися покроково, з перевіркою після кожного кроку на відсутність небажаних стороя ефектів

Першим кроком буде розбивка потоку даних на окремі поля У даному випадку полем вважається окреме значення стовпця На рис 105 потік має дві частини Дані у верхній частині розділені одним пропуском і не вирівняні, а в нижній частині вони вирівняні Різниця між верхньою і нижньою частинами полягає

у використовуваних в них недрукованих символах Тому першим кроком буде Уден недрукованих символів

Далі наводиться код для виконання цього завдання Він зчитує текст з буфера і розбиває його на поля, після чого знову збирає вміст в новому буфері Код виконує проміжну обробку, додаючи спеціальні мітки у вигляді дужок, щоб вказати вміст тексту

using System10 namespace TextProcessor {

/ / ТОГО: Fix up this class

class LottoTicketProcessor : IProcessor { public string Process(string input) {

TextReader reader = new StringReader(input) StringBuilder retval = new StringBuilder() while (readerPeek() = -1) {

string lineOfText = readerReadLineO

string[] splitUpText = lineOfTextSplit(new char[] {    \t }) foreach (string item in splitUpText) {

retvalAppend(&quot(&quot + item + &quot)&quot)

}

retvalAppend(&quot\n&quot)

}

return retvalToString()

}

}

}

У методі Process () текст спочатку розбивається на рядки, після чого кожен рядок розбивається на окремі поля Можна було б написати процедури преобразова тексту в рядки і поля самому, але для цього ефективніше використовувати обєкт stringBuilder Обєкт stringBuilder приймає як параметр рядок, корую потрібно перетворити, а потім присвоюється екземпляру інтерфейсу

TextReader

Так як перетвориться кожен рядок тексту, то використання обєкта stringBuilder буде найбільш ефективним способом створити буфер даних Можна було б використовувати операцію + =, що надається кожною рядком, але занадто часте застосування цієї операції негативно відібється на продуктивності пррамми Причиною цьому є необхідність відстеження використання памяті, що стає вузьким місцем, а також необхідність відстеження болого числа ідентифікаторів посилань на обєкти

Рядки є незмінним типом Це означає, що після ініціалізації обєкта його стан не можна змінити Перевага незмінних типів складається

в тому, що вони дозволяють прискорити виконання програми, т к код може * бути впевненим у тому, що присвоєне обєкту значення ніколи не зміниться А недоліком є ​​те, що для того, щоб змінити присвоєне значення, необхідно створювати новий обєкт Це і відбувається у випадку з застосуванням оператора + = Тип stringBuilder схожий на рядок, але міститься в ньому текст можна модифікувати

У реалізації методу Process про цикл while викликає метод рейок, які счивает, але не видаляє значення символу з потоку Якщо більше немає даних для читання, то метод повертає значення -1 В іншому випадку дані є, і можна викликати метод ReadLine () Метод ReadLine () зчитує символи буфа до першого символу переходу на новий рядок або символу повернення Кареі (символи \ п і \ г, відповідно) Прочитана рядок тексту присвоюється змінної lineOfText Після цього застосовується метод spli t о, який разбана рядок на окремі поля, як роздільники між якими примяти символи пробілу і табуляції (\ t)

По поверненню управління методом spli t про окремі поля зберігаються в Маів splitUpText Елементи масиву обробляються в циклі і додаються в КЄЦ зміною retval типу stringBuilder, але при цьому кожен елемент заключтся в дужки Дужки служать видимою межею символу, щоб можна було перевірити, які дані були лічені Дужки застосовуються з єдиною цью полегшення налагодження Так як ми намагаємося переформатувати потік, то в КЄЦ вмісту змінної retval додається символ переходу на нову стру (\ п)

Коли всі рядки тексту і всі поля в кожному рядку будуть оброблені, возвращаея рядок, що міститься в обєкті stringBuilder, для чого застосовується метод Tostring обєкта Виконання даного коду надає інформацію про колічтве символів у кожному рядку тексту і про те, як потрібно відформатувати файлу Таким чином, ми отримуємо уявлення про структуру файлу

Далі наводиться висновок для файлу lottotxt:

(20000115)(6)(10)(25)(26)(38)(42)(20)

(20000119)(2)(16)(18)(23)(32)(43)(26)

(20000122) (4) (5) (6) (24) (34) (38) (9)

(20000126) (3) (20) (22) (24) (34) (39) (9)

(20000129)(7)(12)(13)(34)(38)(39)(28)

(20000202)(1)(18)(22)(28)(35)(43)(32)

(20000205)(4)(13)(15)(31)(32)(45)(37)

(20000209) (1) (29) (31) (34) (39) (41) (25)

(2006-12-27) (11) (13) (17) (21) (24) (26) (38) (578 199) (735 993) О ()

(2006-12-30) (3) (13) (22) (30) (35) (41) (34) (142968) (472679) ()

Про Про

Про

(2007-01-03) (5) (24) (37) (39) (41) (44) (9) (049802) (133875) () Про

(2007-01-06) (3) (7) (23) (27) (30) (32) (38) (687 442) (874 814) () Про

(2007-01-10) (7) (9) (13) (23) (35) (37) (25) (039 498) (648 301) О ()

(2007-01-13) (3) (17) (22) (37) (39) (43) (34) (968 842) (162 860) () Про

(2007-01-17) (12) (16) (27) (33) (37) (41) (24) (663 824) (765 917) О ()

І з полученног про висновок а м и бачимо, чт про у файл е имеютс я следующи е проблеми, требующ е виправлення:

• порожні е рядків і тексту, н е имеющи е визначені х даних

• в деяких х строчка х в конц е имеютс я порожні е поля

• деяк е дат и вказано и в неправильно м форматі

• деяк е дат и маю т дублікати, якому е нужн про видалити

• деяк е рядку і маю т слишко м мног про полів На м необхідний про визначити, каки е підлогу я м и хоти м залишити, а каки е видалити

ПРИМІТКА

При обробці та упорядкування потоків важливо спочатку розбити потік на відбулась і визначити, з чим ми маємо справу Не слід робити ніяких предполеній до тих пір, поки ви не досліджували окремі елементи даних Тільки після цього ви зможете визначити, що необхідно зробити, щоб виправити потік

Виправлення потоку

У звичайно м решени і пріменяетс я ко д дл я розбивкою і текст а н а рядки і і строчити до н а поля:

IList _dates = new List О

public string Process(string input) { TextReader reader = new StringReader(input) StringBuilder retval = new StringBuilder)) while (readerPeek() = -1) {

string lineOfText = readerReadLine()

string[] splitUpText = lineOfTextSplit(new chart] { , \t }) if (_datesContains(splitUpText[0])) {

continue

}

if (splitUpText[0]Length == 0) { continue

}

if (splitUpText[0]Contains(&quot-&quot)) {

string[] dateSplit = splitUpText[0]Split(-)

string newDate = dateSplit[0] + &quot.&quot + dateSplit[l] + &quot.&quot + dateSplit[2]

if (_datesContains(newDate)) { continue

}

_datesAdd(newDate) retvalAppend(newDate)

for (int cl = 1 cl &lt 8 cl++) { retvalAppend(&quot &quot + splitUpText[cl])

}

}

else {

_datesAdd(spli tUpText[0]) retvalAppend(lineOfText)

}

retvalAppend(&quot\n&quot)

}

return retvalToString()

}

ПРИМІТКА

У вихідному коді, який можна завантажити з Інтернету, демонстріруютс я відділах кроки по впорядкуванню потоку даних Проміжні кроки розробки в иодную коді називаються ProcessOl (), .., ProcessOS ()

Тепер розглянемо в подробицях, яким чином цей код виправляє обнаренние похибки у вмісті файлу лотерейних номерів

Порожні текстові рядки

Порожні текстові рядки видаляються наступним кодом:

if (splitUpText[0]Length == 0) { continue

}

При обробці файлу lottotxt для порожнього рядка створюється масив з одного ЕЛЕНТ Тому ми знаємо, що якщо довжина першого елемента поля дорівнює нулю, то дану строчку тексту слід ігнорувати

Порожні і зайві поля

Наступною проблемою, яку треба виправити, є порожні текстові поля в кінці рядка Дану проблему, напевно, можна було б вирішити спос, подібним способом, використаному для рішення попередньої проблеми Але це вирішило б тільки дану конкретну проблему, а нам необхідно наіться думати в загальному плані і дивитися, чи можна, вирішуючи одну проблему, заодно вирішити ще яку-небудь У даному випадку рішення проблеми порожніх полів може бути також застосовно до проблеми зайвих полів

Для вирішення обох цих проблем необхідно знати дані, з якими виполнтся маніпуляції Потік даних передбачає наступний формат: дата, номери лотерейних квитків з першого по шостий і завершальний бонусний номер Непрільние частини потоку даних мають такий же формат, але містять дополнители інформацію, наприклад, повторюється номер або порожні поля Тому рішенням буде скопіювати дату і додати інші поля, як показано в слующем коді:

retvalAppend(newDate)

for (int cl = 1 cl &lt 8 cl++) { retvalAppend(&quot  &quot + splitUText[cl])

}

У першій рядку коду дата додається в буфер обєкта stringBuilder (перемеая retval) Потім запускається цикл for, в якому в stringBuilder копіюється пробіл і поля з першого по сьоме

Неправильний формат дати

Для деяких полів як роздільник частин дати використовується точка, а для інших – дефіс Правильним роздільником є ​​крапка Код для виправлення формату виглядає наступним чином:

if (splitUpText[0]Contains(&quot-&quot)) {

string [] dateSplit = splitUpText [0] Split С-)

string newDate = dateSplit[0] + &quot.&quot + dateSplit[l] + &quot.&quot + dateSplit[2]

}

Спочатку виконується перевірка, чи не містить перше поле, тобто поле дати, дефіс Для цього застосовується оператор if з методом containsO Якщо поле дати соді дефіс, значить, формат дати неправильний, і його потрібно виправити Для цього поле розділяється по дефіс на три частини, кожна представляє частину дати (місяць, рік і день) Потім ці подполя знову обєднуються, але з використанням точки як роздільник Отриманий результат присвоюється змінної newDate

Повторювані дати

Останньою проблемою, яку нам потрібно вирішити, є повторювані дати в потоці даних Ця проблема вирішується за допомогою наступного коду (соответс частини коду виділені жирним шрифтом):

if (_&ltSatesContains (splitUpText [0])) { continue

&gt&nbsp

if (splitUpText[0]Length == 0) { continue

}

if (splitUpText[0]Contains(&quot-&quot)) {

string[] dateSplit = splitUpText[0]Split(-) string newDate =

dateSplit[0] + &quot.&quot + dateSplit[1] + &quot.&quot + dateSplit[2]

i £ (_datesContains(newDate)) { continue

&gt&nbsp

_datesAdd(newDate)

retvalAppend(newDate)

for (int cl = 1 cl &lt 8 cl++) { retvalAppend(&quot &quot + splitUpText[cl])

}

}

else {

„datesAdd(splitUpText[0])

retvalAppend(lineOfText)

}

З усіх проблем, які нам потрібно було вирішити, проблема повторюваних дат є найскладнішою, т к код для її вирішення розкиданий по різних місцях При обробці потоку даних код створює список дат У список додаються тільки ті дати, яких у ньому ще немає Перевірка на наявність конкретної дати в списку волняется за допомогою методу contains () Цей метод, що надається обєктами списку NET, перевіряє переданий йому обєкт на збіг з одним з елемеов списку Більшість обєктів списку реалізує даний метод, порівнюючи в циклі за допомогою методу Equals () кандидата на додавання в список з кожним елементом списку Але тут мається проблема, яка полягає в тому, що для користувальницького типу дією за замовчуванням методу Equals () є прерка рівності одного посилального значення іншому У таких випадках необхідно реалізовувати свій метод Equals ()

ПРИМІТКА

Необхідність розкидати код для виконання одного завдання по різних місцях є ознакою того, що код, що вийшов буде важко підтримувати У такому випадку слід уважно вивчити цей код і, якщо це є можливим, перісать його Звичайно ж, все залежить він безлічі факторів Тому іноді просто доводиться використовувати код як є, протестувати його і сподіватися, що він буде працювати належним чином

На цьому останньому рішенні створення консольного застосування TextProcessor зерщено Тепер при обробці файлу lottotxt вихідний потік даних буде оорматірован належним чином

Джерело: Гросс К С # 2008: Пер з англ – СПб: БХВ-Петербург, 2009 – 576 е: ил – (Самовчитель)

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*