Как считать строку в си
Перейти к содержимому

Как считать строку в си

Чтение строк

Функция scanf() может использоваться для чтения строк из потока ввода, для чего надо указать спецификатор формата %s. %s заставляет функцию scanf() читать символы, пока не встретится специальный символ. Прочитанные символы помещаются в массив символов, на который указывает соответствующий аргумент, и результат завершается нулевым символом. Применительно к scanf() специальные символы — это пробел, новая строка, табуляция, вертикальная табуляция или перевод формата. В противоположность gets(), которая читает строку, пока не встретится возврат каретки, функция scanf() читает строку до первого специального символа. Это означает, что нельзя использовать scanf() для чтения строки типа «this is a test», поскольку первый пробел завершит процесс ввода. Для изучения эффекта воздействия спецификатора %s опробуем данную программу, введя строку «hello there»:

#include
int main(void)
char str[80];
printf(«Enter a string: «);
scanf(«%s», str);
printf («Here’s your string: %s», str);
return 0;
>

Программа выдаст только часть «hello» строки.

Как считать строку в си

Мы можем работать со строками в С++ в так называемом С-стиле как с массивами символов, которые оканчиваются на нулевой байт ‘0’. Однако, что если такой символ не будет найден или в процессе манипуляций со строкой будет удален, то дальшейшие действия с такой строкой могут иметь недетерминированный результат. По этой причине строки в С-стиле считаются небезопасными, и рекомендуется для хранения строк в C++ использовать тип std::string из модуля .

Объект типа string содержит последовательность символов типа char, которая может быть пустой. Например, определение пустой строки:

std::string message;

Также можно инициализировать или присвоить переменной string конкретную строку:

std::string message ; // или так std::string message2 = "Hello METANIT.COM!"; std::string message3("Hello METANIT.COM!");

В данном случае переменная message получит копию строкового литерала «Hello METANIT.COM!». В своем внутреннем представлении переменная message будет хранить массив символов, который также заканчивается на нулевой байт. Однако реализация типа string и предлагаемые им возможности делают работу с этим типом более безопасной.

И можно инициализировать объект string другим объектом string:

std::string hello; std::string message ; // message = "hello world" // или так // std::string message (hello); // std::string message = hello;

Мы можем вывести подобную строку на консоль:

#include #include int main() < std::string message ; std::cout 

Получение и изменение символов строки

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

std::string hello ; char c ; // e hello[0]='M'; std::cout  

Поскольку объект string представляет последовательность символов, то эту последовательность можно перебрать с помощью цикла for. Например, подсчитаем, сколько раз в строке встречается буква "l":

#include #include int main() < unsigned count<>; // счетчик, сколько раз встречается символ std::string message< "Hello World">; for(const char c: message) < if(c == 'l') < count++; >> std::cout 

Чтение строки с консоли

Для считывания введенной строки с консоли, как и для считывания других значений, можно использовать объект std::cin :

#include #include int main() < std::string name; std::cout > name; std::cout

Input your name: Tom Your name: Tom

Однако если при данном способе ввода строка будет содержать подстроки, разделенные пробелом, то std::cin будет использовать только первую подстроку:

Input your name: Tom Smith Your name: Tom

Чтобы считать всю строку, применяется метод getline() :

#include #include int main()

Метод getline принимает два объекта - std::cin и переменную, в которую надо считать строку.

Input your name: Tom Smith Your name: Tom Smith

Функция scanf

Для считывания данных в языке C используется функция scanf. Ее использование похоже на функцию prinf - сначала задается форматная строка, потом передаются переменные, в которые необходимо записать результат. Например, для считывания двух целых чисел функция вызывается так:

Основное отличие функции scanf в том, что при считывании чисел (или значений типа char) ей необходимо передавать адреса переменных (в языке C все параметры передаются по значению, поэтому чтобы функция scanf могла модифицировать переменную, необходимо передать в функцию адрес этой переменной). Поэтому перед названиями переменных мы пишем знак амперсанда ("&").

В функции scanf могут быть явно записаны какие-то символы, кроме форматных строк. Например, вызов

можно использовать для считывания времени, заданного в виде hh:mm - функция считает число, затем символ двоеточия, затем опять число.

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

Особенности считывания чисел

Функция scanf корректно считывает целые числа, если они начинаются с символа 0, или со знака "+". То есть числа "+123" или "0123" будут корректно считаны по форматной строке "%d", никаких дополнительных параметров задавать не нужно.

Также при считывании чисел игнорируются пробелы перед числом. Это означает, что вызов

scanf("%d:%d", &a, &b) сможет корректно считать время, заданное в формате hh:mm при наличии пробела после двоеточия (такая запись успешно считает строки "12:34", "01:02", "01:␣23" или "␣01:␣23", поскольку дается указание считать число, затем сразу же двоеточие, затем - число, перед которым могут быть пробелы). Но такая запись не считает выражение, например, вида "01␣:␣23", поскольку после первого числа сразу должно идти двоеточие.

Чтобы считать записать вида "01␣:␣23" можно использовать форматную строку "%d :%d", причем пробел в форматной строке может означать и отсутствие пробелов.

Возможные форматные символы

Возможные форматные символы функции scanf для разных типов данных в целом соответствуют форматным символам для функции printf , но имеют меньше различных модификаторов.

Форматная
строка
Соответствующий ей тип
%hhd Считать число (десятичное) и записать его в переменную типа char (для unsigned char нужно использовать %hhu)
%hd short int (для unsigned short int нужно использовать %hu)
%d int (для unsigned int нужно использовать %u)
%ld long int (для unsigned long int нужно использовать %lu)
%lld long long int (для unsigned long long int нужно использовать %llu)
%f float
%lf double
%Lf long double
char. Считывается один символ, он может быть пробелом или символом конца строки.
%s Считывается последовательность непробельных символов (строка), записывается в C-строку (типа char * или char[])

Особенность считывание символов

Считывание одного символа "%c" считывает из потока ввода следующий символ, он может быть в том числе и пробельным символом, а также символом конца строки. Но если в форматной строке перед "%c" поставить пробел, то поскольку пробел в форматной строке обозначает последовательность пробельных символов любой длины, то в этом случае будет считан следующий непробельный символ.

Особенность считывания строк

При считывании строки результат записывается в С-строку, которая представляет собой массив символов (или указатель типа char * с выделенной памятью). Поскольку строка в языке C является адресом (указателем) в памяти, где хранится начало строки символов, то передавать в функцию scanf нужно имя переменной без указания амперсанда.

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

Иногда бывает полезно считать всю строку целиком вместе с пробелами до конца строки. Для этого используется функция gets . Например:

Функция gets считается опасной и не рекомендуется для использования, так как она не контролирует количество считанных символов и не учитывает длину передаваемой строки, что может привести к записи данных за пределами строки.

Вместо нее рекомендуется использование функции fgets , у которой три параметра - строка для записи результата, размер строки и файловый поток, из которого читаются данные. Например:

fgets(s, 101, stdin);

В данном случае мы использовали stdin для чтения со стандартного ввода.

Не следует забывать, что в языке C в конец строки добавляется нулевой символ для обозначения конца строки. То есть если необходимо считать строку, в которой может быть 4 символа, то для нее нужно создать массив char[5] , и функции fgets нужно передавать число, не меньшее 5.

Возвращаемое значение

Функция scanf возвращает значение, равное числу успешно считанных и записанных в переданные параметры значений, что можно использовать для анализа входных данных.

Например, пусть вызвали scanf("%d:%d", &a, &b) .

Тогда при вводе строки "12:34" функция scanf считает два числа, запишет их в переменные a и b и вернет значение 2. А при вводе "12 34" будет считано только одно число, поскольку после него должно идти двоеточие, то второе число считано не будет и функция scanf вернет значение 1.

Как считать строку в си

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

  • Стандартный поток ввода stdin
  • Стандартный поток вывода stdout
  • Стандартный поток вывода сообщений об ошибках stderr

Стандартный поток ввода stdin по умолчанию соответствует клавиатуре, а потоки stdout и stderr - экрану монитора.

Для управления вводом-выводом с помощью этих потоков используются ряд функций:

  • getchar() : ввод с клавиатуры одного символа
  • putchar() : вывод на консоль одного символа
  • fgets() : ввод одной строки
  • puts() / fputs() : вывод одной строки на консоль
  • scanf() : ввод с консоли с форматированием данных
  • sscanf() : ввод с из строки с форматированием данных
  • printf() : вывод с форматированием данных

Функции printf и scanf уже рассматривались ранее, поэтому посмотрим, как применять остальные функции.

Ввод и вывод символов

Для ввода и вывода символа применяются функции getchar() и putchar() . Но следует сказать, что на самом деле они полноценными функциями не являются, а определены как макросы в заголовочном файле stdio.h :

#define getchar() getc(stdin) #define putchar(c) putc((c), stdout)
Вывод символа

Для вывода отдельного символа на консоль предназначена функция putchar() со следующим прототипом:

int putchar(int c);

Выводимый символ в виде числового кода передается в putchar в качестве параметра, он же возвращается функцией.

#include int main(void) < char c = 'A'; putchar(c); // Выводим символ A >
Ввод символа

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

int getchar(void);

В качестве результата функция возвращает числовой код введенного символа.

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

И если буфер операционной системы не пуст, то при вызове функции getc() она получает очередной символ из буфера. Если же буфер пуст, то происходит чтение байта из потока ввода с помощью системной функции, название которой зависит от операционной системы.

При этом при нажатии клавиши Enter, в буфер также помещается код этой клавиши. То есть если мы введем один символ и нажмем на Enter, в буфере окажутся два числовых кода - введенного символа и клавиши Enter. И это надо учитывать при работе с функцией getchar. В частости, рассмотрим простой, но показательный пример:

#include int main(void) < printf("1"); getchar(); // ожидание ввода символа printf("2"); getchar(); // ожидание ввода символа printf("3"); return 0; >

Сначала на экран выводится цифра 1, после чего функция getchar ожидает ввода символа. Если после ввода символа мы нажмем Enter, то в буфер будет помещены два числовых кода - введеного символа и клавиши Enter. Поэтому при втором вызове getchar эта функция считывает байт из буфера - то есть числовой код клавиши Enter.

Например, введем при первом вызове функции getchar символ "a", а затем Enter:

1a 23

Но если при каждом вызове getchar мы будем только нажимать клавишу Enter, тогда в буфер будет заноситься только код этой клавиши, и соответственно программа будет работать, как и ожидалось:

1 2 3

Применим функции getchar и putchar для ввода и вывода символов с клавиатуры:

#include int main(void) < int c; while((c=getchar())!=EOF) < putchar(c); >return 0; >

Функция getchar() считывает числовой код символа, который потом выводится в функции putchar() . Для вывода из программы необходимо ввести комбинацию клавиш Ctrl+C.

Ввод и вывод строк

Вывод строк и puts

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

int putchar(char *s);

В качестве параметра передается указатель на строку, а возвращаемым результатом функции является последний выведенный символ.

При этом функция puts() будет выводить символы переданной строки, пока не дойдет до нулевого символа '\0'. Если же выводимый массив символов не содержит этого символа, то результат программы неопределен. Например:

#include int main(void) < puts("Hello World"); // выводим строковый литерал char* name = "Hello Metanit.com"; puts(name); // выводим значение переменной return 0; >
Hello World Hello Metanit.com
Вывод строк и fputs

Функция fputs() также записывает в поток вывода строку, то есть набор символов, который завершается символом '\0'. При записи строки нулевой символ '\0' не записывается. Она имеет следующий прототип:

int fputs(const char *s, FILE *stream);

Первый параметр функции - записываемая строка, а второй - указатель на поток вывода. В качестве результата функция возвращает неотрицательное целое число. При ошибках в процессе записи возвращается значение EOF .

Применим функцию для записи в стандартный поток вывода, то есть на консоль. В этом случае в качестве второго параметра надо передать значение stdout :

#include int main(void)

Ввод строк и fgets

Для ввода строки с клавиатуры применяется функция fgets() , которая имеет следующий прототип:

char *fgets(char *str, int count, FILE *stream);
  • char *str : строка, в которую производится считывание.
  • int count : сколько символов необходимо считывать.
  • FILE *stream : файловый поток, из которого производится считывание. В качестве потока ввода может выступать и консольный ввод.

Функция fgets() прекращает считывание, когда пользователь нажимает клавишу ENTER, то есть когда в поток добавляется символ перевода строки.

Рассмотрим считывание строки с консоли. Для этого в качестве третьего параметра в функцию передается значение stdin :

#include #define MAX 15 int main(void)

Здесь функция fgets считывает не более 15 символов в строку name, а В реальности функция предложит ввести 14 символов, так как последний символ зарезервирован для нулевого символа '\0'. Если будет введено больше символов, то fgets все равно считает не более 15 символов. Таким образом функция позволяет проверить количество считываемых символов и поэтому считается безопасной.

Пример работы программы:

Enter name: Tom Smith Your name: Tom Smith

Стоит отметить, что функция fgets() возвращает указатель char * - указатель на буфер, в который считаны данные. В реальности это тот же самый буфер, который передавался в качестве первого параметра, то есть в примере выше - это массив name. Однако этот результат может нам пригодится для проверки успешности выполнения функции - если считывание прошло неудачно, то функция возвращает NULL :

#include #define MAX 15 int main(void) < char name[MAX]; printf("Enter name: "); if(fgets(name, MAX, stdin) != NULL) // if(fgets(name, MAX, stdin)) - можно сократить < printf("Your name: %s\n", name); >else < printf("Critical Error. "); >return 0; >

Для эмуляции ошибки можно передать в функцию вторым параметром число 0.

fgets и scanf

При использовании функции fgets() после функции scanf() мы можем столкнуться с некорректным вводом:

#include #define N 32 int main(void) < int age; char name[N]; // считываем возраст в переменную age printf("Input age: "); scanf("%d", &age); // считываем строку в переменну. name printf("Input name: "); fgets(name, N, stdin); // проверяем ввод printf("Age: %d\n", age); printf("Name: %s\n", name); >

В данном случае сначала с помощью функции scanf() считываем число в переменную age. После этого считываем имя - строку с помощью функции fgets() в переменную name. Но посмотрим, какой будет результат работы программы:

Input age: 39 Input name: Age: 39Name:

В данном случае мы видим, что ввод имени с помощью fgets вроде как пропускается. По крайней мере мы не можем ввести имя, а программа сразу же выводит результат.

Все дело в том, что функция scanf() считывает именно то, что ей предписано - число в виде переменной типа int. Все остальные символы остаются в буфере, в который предварительно попадают введенные с клавиатуры символы. Так, после ввода возраста мы нажимаем на клавишу Enter, и в буфер попадает символ "\n", то есть перевод строки. И fgets считывает этот символ из буфера, после чего ввод имени завершается. Очевидно, это не то поведение, на которое мы рассчитывали.

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

Первый способ - считывание символа с помощью вызова scanf("%*c") :

#include #define N 32 int main(void) < int age; char name[N]; // считываем возраст в переменную age printf("Input age: "); scanf("%d", &age); scanf("%*c"); // вытаскиваем символ из буфера // считываем строку в переменну. name printf("Input name: "); fgets(name, N, stdin); // проверяем ввод printf("Age: %d\n", age); printf("Name: %s\n", name); >

Второй способ - мы можем считать символ с помощью getchar() :

#include #define N 32 int main(void) < int age; char name[N]; // считываем возраст в переменную age printf("Input age: "); scanf("%d", &age); getchar(); // вытаскиваем символ из буфера // считываем строку в переменну. name printf("Input name: "); fgets(name, N, stdin); // проверяем ввод printf("Age: %d\n", age); printf("Name: %s\n", name); >

Результат работы программы:

Input age: 39 Input name: Tom Age: 39 Name: Tom

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *