Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ВВП - лекция 10.doc
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
229.89 Кб
Скачать

Работа со строками слов.

Обзор главы.

10.1.1 Слова и языки.

Люди общаются устным и письменным языком, используя для этого последовательности звуком и символов. Слова являются значимой частью этих последовательностей. Слова могут быть переведены в слова со сходным значением из одного языка в другой.

Слова формируются путем расстановки символов в определенных последовательностях, но не любые последовательности символов могут образовывать слова. Подобным образом, предложения формируются путем расстановки слов, но, опять-таки, не все расстановки слов являются предложениями. Одно из правил формирования предложений в английском языке говорит, что предложение должно иметь подлежащее и сказуемое. Структура предложений может быть задана в виде синтаксических правил в нотации Бекуса-Наура, как это мы делали для программ на CF-Pascal. Однако, структура английского и других естественных языков не может быть охвачена синтаксическими и контекстными правилами, поскольку полного набора правил для этих языков попросту не существует. Многие естественные языки продолжают развиваться по причине развития человечества. Паскаль является формальным языком, структура которого установлена и зафиксирована раз и навсегда.

Во многих языках слова отделяются друг от друга пробелами и знаками препинания. По правде говоря, большинство языков используют пробелы и знаки препинания, которые не используются для формирования слов.

Поскольку слова в общем случае не имеют ограничения по длине, то единственный способ хранения всех возможных слов с использование CF-Pascal – это файл. (Если известна длина слова, то мы можем использовать для хранения отдельных его символов фиксированное количество переменных типа CHAR). Подобным образом файлы могут быть использованы для хранения предложений. Гораздо сложнее пословная обработка текста, нежели посимвольная, поскольку сложнее манипулировать отдельными элементами. Но обработка текста является сложной в основном потому, что гораздо больше вещей может быть представлено и осуществлено на уровне слов.

10.1.2 Использование BNF в проектировании программ.

Когда текстовые файлы имеют структуру, которая может быть описана при помощи BNF, синтаксические правила будут полезны для проектирования программ обработки текстов. В действительности, при решении проблем типа «слово-строка», хорошей практикой является составление описания в форме BNF как входного, так и выходного файла и ссылаться к этому описанию во время проектирования. Многие трудности, которые возникают при обработке текстов, происходят от несовершенного понимания того, как эти тексты выглядят

10.1.3 Удаление лишних пробелов.

Одной из простейших структур является файл, который состоит из чередующихся слов и пробелов, где под словом понимается любая строка не пробельных символов. Возможно, самая простая из интересных задач, которая может явиться в файле типа «слово/пробел» - это задача удаления лишних пробелов, так чтобы слова отделялись друг от друга одним пробелом, а в конце и в начале текста пробелов не было. Такая операция может быть проведена над файлом, содержащим программу на CF-Pascal, например, для уменьшения его размера.

Рассмотрим строку символов, содержащую лишние пробелы в файле FileIn:

† Four score and seven years ago †

Она должна быть скопирована в файл FileOut без лишних пробелов:

†Four score and seven years ago†

Текст в FileIn может начинаться с пробелов либо слова, в конце этого текста всегда будет символ пробельный символ, вызванный маркером конца последней строки. За исключением этого маркера строки файл может быть пустым. Запоминание подобных возможностей делает задачу обработки текста сложной. BNF-нотация может быть полезной, охватив все варианты. Если мы условимся не делать различий между маркерами конца строки и пробелами (поскольку маркеры конца строк считываются в виде пробелов), то следующие правила будут описывать входной файл:

  1. <входной файл> ::= <список-слов-пробелов> <пробел >

| <пробелы> <список-слов-пробелов> <пробел>

  1. <список-слов-пробелов> ::= <пустая строка> | <слово> <пробелы> <список-слов-пробелов>

  2. <слово> ::= <НеПробел> | <слово>< НеПробел >

  3. <пробелы > ::= <пробел> | <пробелы> <пробел>

где <пустая строка>, <про,ел>, <не пробельный символ> описывают строку не содержащую символов, один пробельный символ и один не пробельный символ соответственно. <Список-слов-пробелов> может быть пустым, но <пробелы> или <слово> должны содержать хотя бы один символ.

Если выходной файл содержит одну строку, она может быть описана всего двумя дополнительными синтаксическими правилами:

  1. <выходной файл> ::= <список слов> | <пустая строка>

  2. <список слов> ::= <слово> | <слово> <пробел> <список слов>

Таким образом, <выходной файл> может быть пустым, либо должен начинаться и заканчиваться <словом>, а все соседние <слова> должны отделяться одним пробелом.

Нотация BNF точно описывает входной и выходной форматы, но она не охватывает важную часть задачи: она не указывает на то, что слова на выходе должны быть такими же и в том же порядке, что и на входе. Такое утверждение должно быть задано при помощи контекстного правила:

CR. <Слова> в <списке слов> должно быть таким же и идти в том же порядке, что и в <списке-слов-пробелов>.

Подумав над тем, как читать файл FileIn, удалить из него лишние пробелы и записать файл FileOut, двумя полезными программными шаблонами будут являться пропуск пробелов и копирование слова. Используя переменную Ch типа CHAR с начальным значением □, выполнение раздела программы

{Пропуск пробелов}

WHILE NOT EOF(FileIn) AND (Ch = ‘ ‘)

DO

READ(FileIn, Ch);

будет начинаться в произвольном месте файла FileIn. Считывание пробелов будет происходить до тех пор, пока не будет встречен конец файла (в этом случае переменная Ch будет содержать пробел), либо пока не будет встречен не пробельный символ (в этом случае переменная Ch будет содержать первый символ следующего слова).

В том случае, когда переменная Ch содержит начальный символ слова (поскольку предшествующие ему пробелы мы уже пропустили), это слово может быть скопировано в FileOut про помощи раздела программы:

{копирование слова}

WHILE Ch <> ‘ ‘

DO

BEGIN

WRITE(FileOut, Ch);

Read(FileIn, Ch)

END

Если в Ch перед выполнением этого раздела будет пробельный символ, то оператор WHILE не будет выполнен. Действительно, незачем выводить несуществующее слово. Поскольку текст завершается маркером конца строки (который выглядит как пробел), конец файла не будет встречен во время копирования слова.

Раздел программы, который пропускает пробелы и копирует слово, может быть использован в виде высокоуровневой операции обработки файлов, в частности для проектирования процедуры RemoveExtraBlanks (удаление лишних пробелов). Согласно правилу 1, описывающему вход, есть 2 возможных ситуации, с которыми мы можем встретиться в файле FileIn, а именно:

  1. <Список-слов-пробелов> <пробел>

  2. <пробелы> <список-слов-пробелов> <пробел>

Случай 2 сводится к случаю 1 путем предварительного пропускания пробелов. Независимо от того, были или не были пробелы, первый символ <Списка-слов-пробелов> будет считан в Ch.

Поэтому изначальный проект будет таким:

Раздел проекта 1.

PROCEDURE RemoveExtraBlanks(VAR FileIn, FileOut: TEXT);

{копирует слова из FileIn в FileOut, удаляя головные и хвостовые пробелы, а также лишние пробелы между словами}

VAR

Ch: CHAR;

PROCEDURE FlushBlanks(VAR ch: Char);

BEGIN {FlushBlanks}

WHILE NOT EOF (FileIn) AND (Ch <> ‘ ‘)

DO

READ(FileIn, Ch)

END

BEGIN {Remove Extra Blanks}

{инициализация};

FlushBlanks(Ch);

{удаляем лишние пробелы для случая 1};

WRITELN(FileOut)

END {Remove Extra Blanks}

Раздел проекта 1.1

BEGIN {инициализация}

RESET(FileIn)

REWRITE(FileOut)

Ch := ‘ ‘

END

Для удаления лишних пробелов в случае 1, термин <Список-слов-пробелов> раскрывается с использованием провила 2, приводящего нас к двум случаям:

    1. <пробел>

    2. <слово> <пробелы> <список-слов-пробелов> <пробел>

Последовательность:

{Копирование слова};

FlushBlanks(Ch)

всякий раз будет приводить нас либо к <списку-слов-пробелов>, либо к концу файла. Если достигнут <список-слов-пробелов> (случай 1.2), эта последовательность действий вновь должна быть повторена. Если же будет достигнут конец файла (случай 1.1), обработка должна быть прекращена. Поэтому эти операции следует поместить внутрь оператора WHILE с условием завершения EOF(FileIn).

Раздел проекта 1.2

BEGIN {удаляем лишние пробелы для случая 1}

WHILE NOT EOF(FileIn)

DO

BEGIN

{копируем слово};

FlushBlanks(Ch);

{вставляем пробел, если это не последнее слово}

IF NOT EOF(FileIn)

THEN

WRITE(FileOut, ‘ ‘)

END

END

Задание 1. Написать недостающие разделы проекта и провести сборку процедуры RemoveExtraBlanks.

(20 баллов)