Строки и текстовые файлы

Содержание |  Назад  |  Вперед

1. Работа с символами и строками
    1.1
Символы
            Функции для работы с символами.

    1.2
Строки
            Функции и процедуры для работы со строками.
          Процедуры преобразования строковых и числовых значений Str и Val.


2.
Работа c текстовыми файлами

3.
Примеры решения задач
      
Запись и чтение таблиц данных
      
Выделение слов из строки

      
Выделение предложений из текстового файла

 

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

1.1 Символы

Для хранения и обработки отдельных символов в Паскале используется тип данных с именем Char. Переменная символьного типа занимает в памяти один байт, что позволяет закодировать 256 различных символов. Значение символьного типа в программе может изображается двумя способами:

а) если символ имеет графическое представление, например, буква латинского алфавита – A, то она просто заключается в одиночные кавычки (апострофы) – ‘A’;

б) если символ не имеет графического представления как, например, символ перевода каретки”, возникающий при нажатии на клавишу Enter (Ввод), то для его изображения используется конструкция следующего вида:

#номер_символа_в_таблице_ASCII

Например: #13 – символ перевода каретки, #65 – прописная буква А латинского алфавита.

С переменными символьного типа можно производить операции присваивания и логические операции проверки на равенство и неравенство. При этом сравнение по величине (больше или меньше) производится по порядку номеров кодовой таблицы.

Функции для работы с символами.

UpCase ( ch : Char ) : Char – функция преобразует строчные буквы латинского алфавита к прописным. Ее аргумент – исходный символ, а результат – преобразованный символ.

Пример:

program dialog;
var ch : Char;
begin
  repeat
{внешний цикл}
    {операторы программы}
    ...
    ...
    repeat {цикл, требующий корректного ответа на запрос}
      write(‘Продолжить? (Y/N)’); {вывод запроса}
      readln(ch); {считывание символа}
      ch := UpCase(ch); {преобразование: “y->Y”}
    until (ch = ‘Y’) or (Ch = ‘N’);
    {цикл завершается, если введен допустимый символ}
  until ch = ‘N’;
  {внешний цикл завершается, если ответ “N”}
end.

 

1.2. Строки

Для хранения и обработки строк (последовательности символов максимальной длиной 255) предназначен тип String[n], где n – длина строки. По умолчанию, если размер строки после слова String не указан, предполагается, что ее длина максимальна – 255 символов. Кроме максимальной длины строки, определенной при объявлении переменной, при операциях со строками используют понятие динамической или текущей длины строки. Динамическая длина строки показывает число реально используемых символов строки и не может превышать максимальную длину. Например, если мы объявим строковую переменную

var name : String[20];

и присвоим этой переменной значение: name := ‘Привет!’, то динамическая длина строки будет равна 7, а максимальная длина 20 символов. Важно отметить, что в памяти компьютера для переменной name будет отведено не 20 байт, как может показаться, а на один байт больше, т.е. 21 байт. При этом первый байт (точнее нулевой) памяти, отведенной для хранения переменной name будет содержать информацию о динамической длине строки.

Значения строкового типа в программе изображаются в виде последовательности символов, заключенный в апострофы.

Например:

const
  sc = ‘Нажмите F1 для справки’; {строковая константа}
var
  sv : string; {строковая переменная}
begin
  sv := ‘Доброе утро!’; {инициализация строковой переменной}
  write(sv, ‘ ’, sc)
  {вывод переменной, символа пробел и константы на экран}
end.

С переменными строкового типа можно производить операции присваивания, объединения (конкатенации) и логические операции сравнения. Операция объединения строк записывается символом “+” и приписывает символы строки, расположенной справа от знака “плюс”, вслед за символами строки, стоящей слева от знака.

Пример:

var
  PathString,
  FileName,
  FileExtention,
  CompleteName : string;
begin
  PathString := ‘c:\MyDirectory’;
  FileNmae := ‘prog7’;
  FileExtention := ‘pas’;
  CompleteName := PathString + ‘\’ + FileName + ‘.’ + FileExtention;
  write(CompleteName)
end.

В результате строка CompleteName будет иметь значение: ‘c:\MyDirectory\prog7.pas’.

К отдельным символам строки, которые имеют символьный тип Char, можно обращаться также как к элементам одномерного массива, указывая порядковый номер символа в квадратных скобках. Например: s := ‘Computer’, s[1] – символ ‘C’, s[2] – символ ‘o’, s[3] – символ ‘m’ и т.д.

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

program VertPrint;
var
  s : string; {строковая переменная}
  i, n : Integer; {целочисленные переменные}
begin
  s := ‘Hello Word!’; {инициализация строковой переменной}
  n := Length(s); {определение текущей длины строки}
  for i := 1 to n do Writeln( s[i] ); {циклический вывод всех
                                       символов строки с переходом на новую строчку экрана}

end.

В результате на экране будет напечатано:

H
e
l
l
o

W
o
r
d
!

Функции и процедуры для работы со строками.

Функция Length( s : string ) : Integerвозвращает текущую (динамическую) длину строки. Эта функция анализирует нулевой байт строки, который содержит информацию о текущей длине. Поэтому альтернативно длину строки можно узнать следующим образом:

ord( S[0] ), где S – строковая переменная.

Функция Concat( s1, s2, s3, … : string ) : stringвозвращает строку, являющуюся результатом конкатенации строк s1, s2 и т.д. Действии функции тождественно операции конкатенации +”.

Функция Copy( s : string; fs, ln : Integer ) : stringвозвращает подстроку (часть строки s), содержащей ln символов, начиная позиции fs. Например:

FileName := ‘prog7.pas’;

Extention := Copy( FileName, 6, 3)

Строка Extention будет иметь значение ‘pas’.

Процедура Delete( var s : string; fs, ln : integer )преобразует строку s, удаляя из нее ln символов, начиная с позиции fs.

Процедура Insert( s1 : string; var s2 : string; fs : integer )преобразует строку s2, вставляя в неё подстроку s1, начиная с позиции fs.

Функция Pos( subS, S : string ) : integerопределяет первое вхождение подстроки S в строку S и возвращает номер позиции в строке S, начиная с которой в ней расположена искомая подстрока subS. Если такой подстроки не найдено, функция возвращает 0.

Следующий пример иллюстрирует использование функции Pos и процедуры Delete для удаления из строки лишних пробелов, оставляя не более одного пробела между словами.

program Delete_extra_spaces;
var
  s : string;
begin
  s := ‘Строка, в которой много пробелов – плохая строка!’;
  while Pos( ‘ ’, s)<>0 do Delete( s, Pos( ‘ ’, s), 1);
  write(s)
end.

Здесь первый параметр функции Pos – это строка, содержащая два пробела ‘??’. Оператор while проверяет наличие двух следующих подряд пробелов. Если такое обнаружено, значение Pos не равно нулю, происходит удаление первого из двух пробелов. Процедура циклически повторяется до полного удаления повторных пробелов, тогда значение Pos станет равным нулю и цикл завершится.

Процедуры преобразования строковых и числовых значений Str и Val.

Процедура Str( value; var s : string )преобразует численную величину value в строку s. Для величины value можно использовать те же спецификации формата, что и при выводе числовых значений процедурой write.

Пример:

var
  a, b : integer;
  s1, s2 : string;
begin
  a := 5; b := 12;
  Str( a, s1 );
  Str( b, s2 );
  writeln(‘Сумма a+b: ’, a+b);
  writeln(‘Сумма s1+s2: ’, s1+s2);
end.

В результате на экране будет напечатано:

Сумма a+b: 17

Сумма s1+s2: 512

Процедура Val( s : string; var value; errcd : integer )преобразует строку s, содержащую запись числа, в значение переменной value. Тип переменной зависит от формы представления числа в строке. Если преобразование прошло успешно, целочисленный параметр errcd равен 0, если возникла ошибка (строка не соответствует формату числа), то errcd указывает на позицию символа в строке s, который привел к возникновению ошибки.

Пример:

var
  s1, s2 : string;
  x : real;
  ec : integer;
begin
  s1 := ‘123.45’;
  s2 := ‘123,45’;
  Val( s1, x, ec );
  writeln (ec, ‘ ’, x);
  Val( s2, x, ec );
  writeln (ec, ‘ ’, x);
end.

В результате на экране будет напечатано:

0 1.2345000000Е+02
4 0.0000000000Е+00

В приведенном примере преобразование строки s1 в числовое значение прошло успешно, поэтому значение переменной ec оказалось равным 0, а переменная x получила значение 123.45. Строка s2 содержит последовательность символов, которая не может быть интерпретирована как числовое значение, записанное по правилам языка Паскаль, потому что целая и дробная части отделены друг от друга символом ,’. При попытки преобразовать такую строку в числовое значение возникла ошибка и переменная ec получила значение 4 – позиция символа в строке s2 где встретилась некорректный символ, а переменная x получила нулевое значение.

 

2. Работа c текстовыми файлами

Физический файл – это именованная (т.е. снабженная именем) область на магнитном, оптическом или другом носителе, содержащая некоторую информацию. Имя файла это средство доступа к конкретной информации, хранящейся на носителе. Работа с физическими файлами и логическими файлами (клавиатура, экран, порт ввода/вывода) в языке Паскаль (равно как и в других языках высокого уровня) осуществляется через переменную файлового типа.

files.gif (9024 bytes)

Текстовые файлы – это файлы в которых:

а) информация представлена в текстовом виде посредством символов из набора ASCII;

б) информация может разделяться на строки произвольной длины. Признаком конца строки служат два специальных символа #10 и #13;

в) в конце файла присутствует символ #26;

г) при записи чисел, строк и логических значений они преобразуются в символьный (текстовый вид);

д) при чтении чисел они автоматически преобразуются из текстового в машинное представление.

Приведем пример текстового файла с указание всех невидимых (управляющих) символов, которые в нем присутствуют:

Это[20]текстовый[20]файл,[10][13]
который[20]хранит[20]некоторые[10][13]
числа:[10][13]
[10][13]
[20][20]123[20][20]15.0[10][13]
[20][20]131[20][20]16.4[10][13]
[20][20]162[20][20]19.2[10][13]
[20][20]185[20][20]23.6[10][13]
[20][20]193[20][20]40.8[10][13]
[10][13]
Конец[20]файла[10][13]
[26]

Управляющие символы обозначены их ASCII кодом, заключенным в квадратные скобки: [10] – конец строки, [13] – возврат каретки, [20] – пробел, [26] – признак конца файла.

При выводе на экран этот файл выглядит так:

Это текстовый файл,
который хранит некоторые
числа:

123  15.0
131  16.4
162  19.2
185  23.6
193  40.8

Конец файла

Для работы с текстовым файлом необходимо определить файловую переменную соответствующего типа. Это можно сделать так:

var
f : Text;

здесь f – файловая переменная, Text – имя типа для работы с текстовыми файлами.

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

Assign( f, ‘c:\work\test.txt’ );

здесь ‘c:\work\test.txt’ – строковая константа, задающая полное имя (включая путь) файла. Если путь не указан, то подразумевается, что файл расположен в текущем каталоге.

Следующая операция – открытие файла необходима для подготовки файла для записи или чтения. Для текстовых файлов применимы три операции открытия:

ReWrite(f)создание нового файла.

Эта процедура создает на диске и открывает новый файл, имя которого связано с переменной f процедурой Assign. Указатель работы с файлом помещается в начальную позицию. Если файл с таким именем уже существовал, он становится пустым, т.е. его предыдущее содержание теряется. После выполнения процедуры ReWrite файл доступен для чтения и записи.

ReSet(f)открытие существующего файла для записи/чтения.

Эта процедура ищет уже существующий файл на диске и открывает его для работы, помещая указатель в начальную позицию. Если файл не существует происходит ошибка ввода/вывода.

Append(f) – открытие существующего файла для добавления текста в конец файла.

Эта процедура ищет уже существующий файл на диске и открывает его для работы, помещая указатель в конец файла. Если файл не существует происходит ошибка ввода/вывода.

Для осуществления операций чтения или записи используются процедуры:

– чтение

Read( f, список_ввода )

Readln( f, список_ввода )

– запись

Write( f, список_вывода )

Writeln( f, список_вывода )

здесь f – файловая переменная.

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

Список вывода может содержать константы, имена переменных и выражения, значения которых будут записаны в файл. Допускается использование объектов следующих типов: целые, вещественные, символьный, строковый и логический. В последнем случае логические значения будут представлены в текстовом файле словами TRUE и FALSE. Процедура Writeln после вывода всех элементов списка вывода добавляет два управляющих символа #10 и #13, которые служат признаком конца строки, что приводит к перемещению указателя вывода в начало новой строки.

Все файлы, открытые в результате работы программы, должны быть закрыты при завершении программы процедурой – Close(f).

Рассмотрим пример чтения информации из текстового файла в котором поясним различие в работе процедур Read и Readln. Пусть на диске C: в каталоге WORK имеется текстовый файл test.dat следующего содержания:

0.45 12.5 678.89
0.47 13.1 791.45
0.49 15.2 871.21
0.52 17.7 936.52

program ReadDataFile;
var
  f : text; { файловая переменная }
  a, b, c : real;
begin
    Assign( f, 'c:\work\test.dat');{ связываем файловую
                                     переменную f c физическим
                                     файлом c:\work\test.dat }

    Reset( f ); { открываем существующий файл }
{1} Read( f, a ); { читаем первое число первой строчки }
{2} Read( f, b ); { читаем второе число первой строчки }
{3} Read( f, c ); { читаем третье число первой строчки }
    Writeln( a:7:2, b:7:2, c:7:2 ); { вывод на экран }
{4} Read( f, a, b, c ); { читаем 1-е, 2-е и 3-е числа
                          второй строчки }
    Writeln( a:7:2, b:7:2, c:7:2 ); { вывод на экран }
{5} Readln( f, a ); { читаем первое число третьей строчки }
{6} Read( f, b ); { читаем первое! число четвертой строчки }
{7} Read( f, c ); { читаем второе число четвертой строчки }
    Writeln( a:7:2, b:7:2, c:7:2 ); { вывод на экран }
    Close( f ); { закрываем файл }
end.

Для удобства обсуждения строчки, в которых осуществляется операция чтения, пронумерованы. Первые три процедуры Read последовательно считывают три значения, находящиеся в первой строчке файла и присваивают их переменным a, b и c, соответственно. При считывания очередного значения, позиция ввода остается в прежней строке. И только при считывании последнего значения, за которым следует невидимый управляющий символ конца строки, позиция ввода переводится на новую строчку. Процедура Read в строчке с номером 4 считывает сразу три значения. Её вызов аналогичен последовательному вызову трех процедур Read для каждого значения (строчки 1, 2 и 3). В пятой строке для чтения использована процедура Readln, которая считывает первое значение третей строчки текстового файла, заносит его в переменную a и переводит позицию ввода на новую строчку. Оставшиеся два числа в третей строке текстового файла уже не могут быть прочитаны! Процедуры Read в 6-й и 7-й строках считывают, соответственно, первое и второе значение четвертой строчки текстового файла. В результате работы программы на экран будет выведена следующая информация:

0.45 12.50 678.89
0.47 13.10 791.45
0.49 0.52 17.70

При считывании из файла строковых значений следует использовать процедуру Readln.

 

3. Примеры решения задач

Задача. Запись и чтение таблиц данных

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

Начнем с первой программы.

I. Исходные данные и результат

Исходные данные:

  1. функция, значения которой вычисляются. Для вычисления значения функции организуем подпрограмму-функцию.
  2. диапазон изменения аргумента и шаг изменения аргумента. Для хранения этих данных выделим три вещественные переменные, значения которых будем вводить с клавиатуры.

    Результатом работы программы должен явится текстовый файл, содержащий заголовок и две колонки чисел. Первая колонка – значения аргумента, вторая – значения функции.

II. Алгоритм решения первой части задачи

  1. Ввести с клавиатуры исходные данные.
  2. Создать новый файл и открыть его.
  3. Вывести в файл заголовок
  4. Организовать циклическое вычисление значений функции и запись результатов в файл.
  5. Закрыть файл.

III. Программа

program WriteTabData;
var
  f : text;
  x, xmin, xmax, dx : real;

function y( x : real ) : real;
begin
  y := ...;
end;

begin
  Writeln(‘Введите Xmin, Xmax и dx:’);
  Readln(xmin, xmax, dx);
  Assign( f, ‘tab.dat’ );
  Rewrite( f );
  Writeln( f, ‘Результат табулирования функции ...’ );
  x := xmin;
  while x<=xmax do begin
    Writeln( f, x, ‘ ‘, y(x) );
    x := x + dx
  end;
  Close( f );
end.

Теперь обратимся ко второй программе.

I. Исходные данные и результат

Исходными данными является текстовый файл, содержащий строчку-заголовок и два столбца чисел. Количество пар данных неизвестно.

Результатом работы программы должны явится два массива, содержащие значения аргумента и значения функции. Поскольку количество пар чисел заранее не известно, то, строго говоря, размер массивов определить невозможно и следовало бы использовать динамические массивы. Мы же ограничимся тем, что опишем статические массивы большого размера, а использовать их будем по мере необходимости, предполагая, что количество пар данных не превосходит указанных размеров массивов.

II. Алгоритм решения второй части задачи

  1. Открыть файл для чтения.
  2. Считать из файла строку-заголовок.
  3. Считывать пары чисел из файла и записывать их в массивы до тех пор, пока не будет достигнут конец файла или исчерпаны резервы массивов. Здесь же выводить пары чисел на экран.
  4. Закрыть файл.

Для реализации 3-го этапа алгоритма необходимо уметь определять достигнут ли конец файла или нет. Для этих целей можно использовать логическую функцию EOF( f ), которая возвращает логическое значение TRUE, если достигнут конец файла, связанного с файловой переменной f, и значение FALSE в противном случае.

III. Программа

program ReadTabData;
const
  nmax = 1000;
var
  f : text;
  x, y : array[1..nmax] of real;
  title : string;
  i : integer;
begin
  Assign( f, ‘tab.dat’ );
  Reset( f );
  Readln( f, title );
  Writeln( title );
  i := 1;

  while (not EOF(f))and(i<=nmax) do begin
    Readln( f, x[i], y[i] );
    Writeln( x[i], y[i] );
    i := i+1;
  end;
  Close( f );
end.

 

Задача. Выделение слов из строки

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

I. Исходные данные и результат

Исходные данные: строка неизвестной длины, содержащая неизвестное количество слов, разделенных произвольным количеством пробелов.

Результатом являются отдельные слова, начинающиеся на букву а.

II. Алгоритм решения задачи

  1. Ввести строку с клавиатуры
  2. Определить её длину
  3. Анализируя строку посимвольно, выделять слова. Если встречается слово с нужным первым символом, выводить его на экран.

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

III. Программа

Ниже приводится текст программы.

PROGRAM Words;
USES Crt;
VAR
  s, w : string;
  n, l, r : integer;
BEGIN
  ClrScr;
  Writeln('Исходная строка: ');
  Readln(s);

  n := Length(s); { длина строки }
  l := 1;

  r := 1;
  WHILE r<=n DO BEGIN
    WHILE
(s[l]=' ') and (l<=n) DO l:=l+1; { поиск начала слова }
    r := l;
    WHILE (s[r+1]<>' ') and (r+1<=n) DO r:=r+1; { поиск конца слова }
    w := copy(s,l,r-l+1); { выделяем слово из строки }
    l := r+1;

    IF w[1] = 'a' THEN Writeln(w); { выводим слово на экран }
  END;
ReadKey;
END.

После ввода строки, с помощью функции Length определяется её длина. Затем переменным l и r, которые используются для хранения левой и правой границ слова, присваивается значение 1. Для сканирования строки организуется цикл с помощью оператора while. Первый вложенный цикл, сканирует строку до тех пор, пока не встретится непустой символ или не будет достигнут конец строки. Позиция этого символа запоминается в переменной l – левая граница слова. Второй вложенный цикл продолжает сканировать строку до тех пор, пока не встретится пробел или не будет достигнут конец строки. В результате в переменной r оказывается правая граница слова. Затем из строки s с помощью функции Copy выделяется последовательность символов начиная с позиции l длиной (r-1+1). После чего анализируется первый символ слова и в случае, если этот символ равен “а”, слово выводится на экран.

 

Задача. Выделение предложений из текстового файла

Написать программу, которая считывает текст из файла и выводит на экран только предложения, не содержащие символа ,”. Предложения содержат не более 256 символов.

I. Исходные данные и результат

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

Результатом являются отдельные предложения, не содержащие запятых.

II. Алгоритм решения задачи

  1. Открыть файл
  2. Анализируя посимвольно содержимое исходного файла выделять предложения, сохраняя их в определенной строковой переменной. Если предложение не содержит запятых, выводить его на экран.
  3. Закрыть файл

Разберем более подробно последний пункт алгоритма. Предложением будем считать последовательность произвольных символов, заканчивающихся точкой, восклицательным или вопросительным знаком. Выделение предложения начинается с поиска первого непустого символа – начала предложения. Затем все считанные символы добавляются к строковой переменной, до тех пор, пока не будет встречен один из символов, завершающих предложение. Далее операция выделения повторяется. При этом важно помнить, что в конце каждой строки текстового файла присутствуют управляющие символы с номерами 10 и 13, которые при посимвольном методе чтения также будут считаны. И поскольку они не являются частью предложения, их нужно исключать.

III. Программа

Ниже приводится текст программы.

program sentences;
uses crt;
var
  f : text;
  c : char;
  w,s : string;

function IsComma( s : string ) : boolean;
var i : integer;
begin
  IsComma := FALSE;
  for i:=1 to Length(s) do
  if
s[i]=',' then IsComma := TRUE;
end;

begin
  clrscr;
  assign(f,'text.txt');
  reset(f);
  while not eof(f) do begin
    s := '';
    repeat
     read(f,c);
    until not (c in [' ',#13,#10]) or eof(f);
    s := s + c;
    while not (c in ['.','?','!']) and not eof(f) do begin
      read(f,c);
      if (c<>#10) and (c<>#13) then s := s + c;
    end;
    if not IsComma(s) then writeln(s);
  end;
  close(f);
  readkey;
end.

После открытия файла (процедура reset) организуется внешний цикл, завершающийся при достижении конца файла. В этот цикл вложены два других цикла в ходе выполнения которых происходит выделение одного предложения и запись его в переменную s. Первый вложенный цикл (repeat) выполняется до тех пор, пока не будет встречен символ, отличный от пустого или управляющих символов, – это начало предложения или не будет достигнут конец файла. Этот символ добавляется к строке s. Далее второй вложенный цикл (while) считывает все символы и добавляет их к строке s, если только это не управляющие символы. Цикл завершается, если будет считан один из символов, завершающих предложение или достигнут конец файла. Для выяснения наличия в выделенном предложении запятой используется описанная выше функция IsComma, которая возвращает логическое значение true, если предложение содержит символ запятой, и значение false в противном случае. Предложения, не содержащие запятых, выводятся на экран. Процесс выделения предложений продолжается пока не будет достигнут конец файла.

 

Содержание |  Назад  |  Вперед