Главная страница

Электронный конспект по программированию часть 1. Программа на языке Си, так же как и на большинстве современных языков программирования, создается в два этапа


Скачать 32.35 Mb.
НазваниеПрограмма на языке Си, так же как и на большинстве современных языков программирования, создается в два этапа
АнкорЭлектронный конспект по программированию часть 1.doc
Дата04.09.2018
Размер32.35 Mb.
Формат файлаdoc
Имя файлаЭлектронный конспект по программированию часть 1.doc
ТипПрограмма
#24059
страница7 из 12
1   2   3   4   5   6   7   8   9   ...   12
часть, а остальное – при следующих вызовах fgets;

указатель на файл.

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

#include

main()

{

char s[80];

FILE *fp;

fp = fopen ( "input.dat", "r" );

if ( NULL == fgets ( s, 80, fp ) )

printf ( "Не удалось прочитать строку" );

else

printf ( "Перваястрокафайла - %s", s );

fclose ( fp );

}

Функция fgets читает строку из файла, пока не случится одно из двух событий:

• встретится символ перехода на новую строку '\n';

• прочитано столько символов, что они заняли всю строку (с учетом последнего нуля), например, в нашем случае она остановится, если прочтет 79 символов.

В конце строки будет поставлен символ '\0'. Кроме того, если был найден символ перехода на новую строку '\n', он сохраняется и в строке s.

Пример. В каждой строке файла input.dat заменить все буквы 'A' на 'Б' и вывести изме-

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

Для чтения из файла используем цикл while. Он заканчивает работу, если функция

fgets вернет значение NULL, то есть все строки обработаны.

#include

main()

{

char s[80];

int i;

FILE *fin, *fout;

fin = fopen ( "input.dat", "r" );

fout = fopen ( "output.dat", "w" );

while ( NULL != fgets ( s, 80, fin ) ) // читаемстроку

{

i = 0; // начинаемсs[0]

while ( s[i] != '\0' ) { // пока не конец строки

if ( s[i] == 'А' ) s[i] = 'Б'; // меняемсимвол

i ++; // переходим к следующему символу

}

fprintf ( fout, "%s", s ); // выводим строку в файл

}

fclose ( fin );

fclose ( fout );

}

Обратите внимание, что мы не поставили символ перехода на новую строку при вызове

функции fprintf. Это связано с тем, что при чтении функция fgets сохраняет символ '\n'

в конце каждой строки (кроме последней), поэтому строки будут выведены в выходной файл так же, как они были записаны в исходном файле.

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

В языке Си есть достаточно много специальных функций, которые работают со строками

– последовательностями символом с нулевым символом на конце. Для использования этих

функций надо включить в программу заголовочный файл

#include

Многие из этих функций достаточно опасны при неправильном использовании, ведь они не проверяют, достаточно ли выделено памяти для копирования, перемещения или другой операции, единственным признаком окончания строки для них является символ '\0'.

Длина строки – strlen

Это самая простая функция, которая определяет, сколько символов в переданной ей строке (не считая '\0'). Ее имя происходит от английских слов string length (длина строки).

#include

#include

main()

{

int len;

char s[] = "Prodigy";

len = strlen(s);

printf ( "Длинастроки %s равна %d", s, len );

}

В этом примере функция определит, что длина строки равна 7. Теперь рассмотрим более сложную задачу.

Задача. В текстовом файле input.dat записаны строки текста. Вывести в файл output.dat

в столбик длины этих строк.

#include

#include

main()

{

char s[80];

FILE *fin, *fout;

fin = fopen ( "input.dat", "r" );

fout = fopen ( "output.dat", "w" );

while ( NULL != fgets(s, 80, fin) ) // читаемстроку s

{

fprintf(fout, "%d\n", strlen(s)); // выводим ее длину в файл

}

fclose ( fin );

fclose ( fout );

}

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

Сравнение строк – strcmp

Для сравнения двух строк используют функцию strcmp (от английских слов string comparison– сравнение строк). Функция возвращает ноль, если строки равны (то есть «разность»между ними равна нулю) и ненулевое значение, если строки различны. Сравнение происходитпо кодам символов, поэтому функция различает строчные и заглавные буквы – они имеют разныекоды.

#include

#include

main()

{

char s1[] = "Вася",

s2[] = "Петя";

if ( 0 == strcmp(s1,s2) )

printf("Строки %s и %s одинаковы", s1, s2);

else printf("Строки %s и %s разные", s1, s2);

}

Если строки не равны, функция возвращает «разность» между первой и второй строкой, то есть разность кодов первых различных символов. Эти числа можно использовать для сортировки строк – если «разность» отрицательна, значит первая строка «меньше» второй, то есть стоит за ней в алфавитном порядке. В таблице показано несколько примеров (код буквы 'A' равен 65,

код буквы 'B' 66, код буквы 'C' 67).


Задача. Ввести две строки и вывести их в алфавитном порядке.

#include

#include

main()

{

char s1[80], s2[80];

printf ("Введитепервуюстроку");

gets(s1);

printf ("Введите вторую строку");

gets(s2);

if ( strcmp(s1,s2) <= 0 )

printf("%s\n%s", s1, s2);

else printf("%s\n%s", s2, s1);

}

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

#include

#include

main()

{

char s1[80], s2[80];

printf ("Введитепервуюстроку");

gets(s1);

printf ("Введите вторую строку");

gets(s2);

if ( 0 == strncmp(s1, s2, 2) )

printf("Первыедвасимвола %s и %s одинаковы", s1, s2);

else

printf("Первые два символа %s и %s разные", s1, s2);

}

Один из примеров использования функции strcmp – проверка пароля. Составим про-

грамму, которая спрашивает пароль и, если пароль введен неверно, заканчивает работу, а если верно – выполняет какую-нибудь задачу.

Пример. Составить программу, которая определяет, сколько цифр в символьной строке. Программа должна работать только при вводе пароля «куку».

#include

#include

main()

{

char pass[] = "куку", // правильный пароль

s[80]; // вспомогательная строка

int i, count = 0;

printf ("Введитепароль ");

gets(s);

if ( strcmp ( pass, s ) != 0 )

{

printf ( "Неверный пароль" );

return 1; // выход по ошибке, код ошибки 1

}

printf ("Введитестроку");

gets(s);

i = 0;

while ( s[i] != '\0' ) {

if ( s[i] >= '0' && s[i] <= '9' )

count ++;

}

printf("\nНашли %d цифр", count);

}

В этой программе использован тот факт, что коды цифр расположены в таблице символов последовательно от '0' до '9'. Поэтому можно использовать двойное неравенство, а не сравнивать текущий символ s[i] с каждой из цифр. Обратите внимание на разницу между символами

'\0' (символ с кодом 0, признак конца строки) и '0' (символ с кодом 48, цифра 0). Переменная

count работает как счетчик.

Копирование строк

Часто надо записать новое значение в строку или скопировать информацию из одной

строки в другую. Функции копирования принадлежат к числу «опасных» – они могут вызвать серьезную ошибку, если произойдет выход за границы массива. Это бывает в том случае, если строка, в которую копируется информация, имеет недостаточный размер (под нее выделено мало место в памяти).

В копировании участвуют две строки, они называются «источник» (строка, откуда копируется информация) и «приемник» (куда она записывается или добавляется).

При копировании строк надо проверить, чтобы для строки-приемника было выделено достаточно места в памяти

Простое копирование выполняет функция strcpy. Она принимает два аргумента: сначала

строка-приемник, потом – источник (порядок важен!).

char s1[50], s2[10];

gets(s1);

strcpy ( s2, s1); // s2 (приемник) <- s1 (источник)

puts ( s2 );

Этот фрагмент программы является «опасным» с точки зрения выхода за границы строки. В строку s1 можно безопасно записать не более 49 символов (плюс завершающий ноль). Поэтому если с клавиатуры будет введена строка длиннее 49 символов, при записи ее в память произой-

дет выход за границы строки s1. Строка s2 может принять не более 9 символов, поэтому при

большем размере s1 произойдет выход за границы строки s2.

Поскольку реально функции передается адрес начала строки, можно заставить функцию

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

пирует строку s2 в область памяти строки s1,которая начинается с ее 6-ого символа, оставив

без изменения первые пять:

strcpy ( s1+5, s2 );

При этом надо следить, чтобы не выйти за границу массива. Кроме того, если до выполнения

этой операции в строке s1 меньше 5 символов, фокус не удастся.

Еще одна функция позволяет скопировать только заданное количество символов, она на-

зывается strncpy и принимает в третьем параметре количество символов, которые надо ско-

пировать. Важно помнить, что эта функция НЕ записывает завершающий нуль, а только ко-

пирует символы (в отличие от нее strcpy всегда копирует завершающий нуль). Функция

strncpy особенно полезна тогда, когда надо по частям собрать строку из кусочков.

#include

#include

main()

{

char s1[] = "Ку-ку", s2[10];

strncpy ( s2, s1, 2 ); // скопировать 2 символа из s1 в s2

puts ( s2 ); // ошибка! нет последнего '\0'

s2[2] = '\0'; // добавляем символ окончания строки

puts (s2); // вывод

}

Проблемы при копировании строк

При копировании стандартные функции strcpy и strncpy поступают так: определяют

количество символов, которые надо скопировать и затем переписывают их, начиная с первого символа до последнего. Если области источника и приемника не перекрываются, то все проходит нормально. Но попробуем «раздвинуть» строку, например для того, чтобы вставить что-то в ее середину. Пусть в строке s1 записано имя и фамилия человека, а в строке s2 его отчество.Надо получить в строке s1 полностью имя, фамилию и отчество.

#include

#include

main()

{

int n;

char s1[80] = "Иван Рождественский",

s2[] = "Петрович ";

n = strlen(s2); // длина второй строки

strcpy (s1+5, s1+5+n); // пытаемся раздвинуть на n символов

strncpy(s1+5, s2, n); // вставляем отчество в середину

puts ( s1 );

}

При первом вызове strcpy мы хотели скопировать конец строки, начиная с символа с номером 5 (он шестой с начала, так как нумерация идет с нуля) вправо на n символов (объявленная__длина массива символов – 80 – позволяет это сделать). Однако из-за того, что копирование выполнялось с начала блока данных, скопировав на новое место первый символ фамилии ('Р') функция стерла букву 'н' (справа на 9 символов) и т.д. В результате получили

s1 = "Иван РождествеРождествеРождес"

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

s1 = "Иван Петрович РождествеРождес"

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

void strcpy1 ( char s1[], char s2[] )

{

int n = strlen(s2);

while ( n >= 0 )

{

s1[n] = s2[n];

n --;

}

}

Заметьте, что завершающий нуль строки s2 также копируется. Если использовать в нашем

примере эту функцию вместо strcpy, то получим желаемый результат.

Возникает вопрос: можно ли сделать функцию, которая всегда правильно копирует? Конечно, можно, хотя это и не просто. Для этого в самой функции надо использовать вспомогательную строку (ее называют буфером) и в ней формировать результат так, чтобы в процессе копирования не портилась исходная строка. Когда результат в буфере готов, его останется скопировать в то место, где была исходная строка и удалить память, выделенную под буфер. Попробуйте написать такую функцию, если известно, что ее будут использовать для копирования строк длиной не более 80 символов.

Объединение строк

Еще одна функция – strcat (от string concatenation – сцепка строк) позволяет добавить

строку источник в конец строки-приемника (завершающий нуль записывается автоматически).

Надо только помнить, что приемник должен иметь достаточный размер, чтобы вместить обе исходных строки. Функция strcat автоматически добавляет в конец строки-результата завершающий символ '\0'.

#include

#include

main()

{

char s1[80] = "Могу, ",

s2[] = "хочу, ", s3[] = "надо!";

strcat ( s1, s2 ); // дописать s2 вконец s1

puts ( s1 ); // "Могу, хочу, "

strcat ( s1, s3 ); // дописать s3 вконец s1

puts ( s1 ); // "Могу, хочу, надо!"

}

Заметьте, что если бы строка s1 была объявлена как s1[] (или с длиной меньше 18), произошел бы выход за границы массива с печальными последствиями.

Задача. Ввести с клавиатуры имя файла. Изменить его расширение на ".exe".

Алгоритм решения:

1. Найти в имени файла точку '.'или признак конца строки '\0'.

2. Если нашли точку, скопировать начиная с этого места новое расширение ".exe" (используем функцию strcpy).

3. Если нашли конец строки (точки нет), добавить в конец расширение ".exe" (используемфункциюstrcat).

#include

#include

main()

{

char s[80];

int n; // номерсимвола '.'

printf("Введитеимяфайла ");

gets ( s );

n = 0;

while ( (s[n] != '.') // ищем первую точку

&& (s[n] != '\0') ) // или конец строки

n ++;

if ( s[n] == '.' ) // если нашли точку, то...

strcpy ( s+n, ".exe" ); // меняем расширение,

else strcat ( s, ".exe" ); // иначедобавляем

puts ( s );

}

Задача. Ввести с клавиатуры фамилию и имя в одну строку (допустим, "Иванов Вася"). Построить в памяти строку «Привет, Вася Иванов!».

Алгоритм решения этой задачи можно записать так:



В программе используется важное свойство массивов в языке Си (в том числе и символьных строк, которые являются массивами символов):

Если массив называется s, то запись s+i обозначает адрес элемента s[i] (так же, как и
1   2   3   4   5   6   7   8   9   ...   12


написать администратору сайта