Scanf в с при ошибке

This is homework, so you might be required to work under certain (arbitrary) restrictions. However, the phrase «scanf error handling» is something of an oxymoron in the C programming language.

The best way to do this is to read in a line/other suitable chunk and parse it with C string functions. You can do it in one line of scanf but there are many drawbacks:

  1. You can guard against buffer overflows, but if a buffer isn’t large enough you can’t recover.
  2. You can specify specific character ranges for your strings, but it starts to look a little regexy, and the behavior of the "%[" format of scanf isn’t mandated in the standard.
  3. You can check for a third name, but the code looks unintuitive — it doesn’t look like you only want two names, it looks like you want three. scanf also gives you very little control over how you handle whitespace.

EDIT: I initially thought from your question that the names were contained in brackets (a la "[Bruce] [Wayne]") but it now appears that was merely your convention for denoting a placeholder.

Anyway, despite my intense dislike of scanf, it has its uses. The biggest killer (for me) is the inability to distinguish between line endings and simple space separation. To fix that, you can call fgets to read the data into a buffer, then call sscanf on the buffer. This gives you both a) safer reading (scanf messes with the ability of other more straightforward functions to read from a buffer) and b) the benefits of scanf formats.

If you have to use scanf, your format basically be this:

" %s %s %s"

With the third being undesirable. As @Constantinius’s answer shows, you’d need to read data into three buffers, and check whether or not the third passed. However, if you’re reading multiple consecutive lines of this data, then the first entry of the next line would satisfy the third slot, falsely giving you an error. I highly recommend using fgets and sscanf or ditching the sscanf for more precise manual parsing in this case.

Here’s a link to the fgets man page if you missed the one I snuck in earlier. If you decide to ditch sscanf, here are some other functions to look into: strchr (or strspn, or strcspn) to find how long the name is, strcpy or memcpy (but please not strncpy, it’s not what you think it is) to copy data into the buffers.

On a character input in the first scanf(), the second one doesn’t run. getchar() isn’t working either for Try Again input. It skips to take input for Would you like to play again? (Y/N)? It seems that your_choice is supposed to take the character and check it afterward but the character is actually being taken by ch. What is causing it to work like this and how to resolve the issue. I’ve tried re-initializing the variables but doesn’t work.

#include <stdio.h>

void choice(int);

int main() {
    char ch;
    int random, your_choice;

    do {
        srand(time(NULL));
        system("cls");
        printf("** 0 is for Rock **n");
        printf("** 1 is for Scissors **n");
        printf("** 2 is for Lizard **n");
        printf("** 3 is for Paper **n");
        printf("** 4 is for Spock **n");

        printf("nEnter your choice here:");
        scanf("%d", &your_choice);

        random = rand() % 5; //random number between 0 & 4
        if ((your_choice >= 0) && (your_choice <= 4)) {
            //choice printer omitted for this post

            if ((random == ((your_choice + 1) % 5)) || (random == ((your_choice + 2) % 5)))
                printf("nn... and you win!!!n");
            else if ((random == ((your_choice + 3) % 5)) || (random == ((your_choice + 4) % 5)))
                printf("nn... and you lose!!!n");
            else if (random == your_choice)
                printf("nnUnfortunately, it's a tie!n");
        } else
            printf("nWell, this is wrong! Try again with a number from 0 to 4!!n");

        printf("nWould you like to play again? (Y/N)?: ");
        scanf(" %c", &ch);

    } while (ch == 'y' || ch == 'Y');

    return 0;
}

asked Nov 11, 2016 at 15:23

nAiN's user avatar

nAiNnAiN

552 silver badges7 bronze badges

15

If the user enters characters that cannot be converted to a number, scanf("%d", &your_choice); returns 0 and your_choice is left unmodified, so it is uninitialized. The behavior is undefined.

You should test for this and skip the offending input this way:

    if (scanf("%d", &your_choice) != 1) {
        int c;
        /* read and ignore the rest of the line */
        while ((c = getchar()) != EOF && c != 'n')
            continue;
        if (c == EOF) {
            /* premature end of file */
            return 1;
        }
        your_choice = -1;
    }

Explanation:

  • scanf() returns the number of successful conversions. If the user types a number, it is converted and stored into your_choice and scanf() returns 1, if the user enters something that is not a number, such as AA, scanf() leaves the offending input in the standard input buffer and returns 0, finally if the end of file is reached (the user types ^Z enter in windows or ^D in unix), scanf() returns EOF.

  • if the input was not converted to a number, we enter the body of the if statement: input is consumed one byte at a time with getchar(), until either the end of file or a linefeed is read.

  • if getchar() returned EOF, we have read the entire input stream, no need to prompt the user for more input, you might want to output an error message before returning an error code.

  • otherwise, set your_choice to -1, an invalid value so the read of the code complains and prompts for further input.

Reading and discarding the offending input is necessary: if you do not do that, the next input statement scanf(" %c", &ch); would read the first character of the offending input instead of waiting for user input in response to the Would you like to play again? (Y/N)?: prompt. This is the explanation for the behavior you observe.

answered Nov 11, 2016 at 16:21

chqrlie's user avatar

chqrliechqrlie

127k10 gold badges113 silver badges182 bronze badges

4

При использовании scanf выдаёт ошибку:

char str;
printf("qwe");
scanf("%s", &str);
return 0;

Ошибка	2	error C4996: 'scanf': This function or variable may be unsafe. Consider using scanf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.	e:decanatconsoleapplication15consoleapplication15consoleapplication15.cpp	11	1	ConsoleApplication15

После добавления
#define _CRT_SECURE_NO_WARNINGS
ничего не меняется.
А при использовании scanf_s, программа запускается но после ввода значения выкидывает такую ошибку:
dadd1062d568496b8a2b5a511177299f.png

Zloy_Tip

1 / 1 / 0

Регистрация: 01.09.2020

Сообщений: 2

1

01.09.2020, 17:10. Показов 7194. Ответов 23

Метки scanf (Все метки)


Студворк — интернет-сервис помощи студентам

Здравствуйте, Уважаемые!
Начал изучать язык Си и буквально в начале книги уже наткнулся на неприятность.
При запуске программы, компилятор ругается на функцию scanf. проверил не один раз, с точки зрения синтаксиса написано все верно.
Мой код:

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
 
main() {
    int a, number;
 
    printf("Enter number int: ");
        scanf("%d", &a);
 
    if (number != 7) {
        printf("The variable number is not equal to 7");
    }
 
    printf("This is a C program n");
    printf("This is a C n program nn");
    printf("This nis na nC n program nn");
 
    return 0;
}

Компилятор пишет:
Ошибка C4996 ‘scanf’: This function or variable may be unsafe. Consider using scanf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details. Project2 C:UsersProfessionalDesktopProjectsProject2Project2Main.c 8

не пойму, что там не так…?
ниже на код не обращайте внимание, делаю не большие задачки все подряд просто в одном файле, смотрю как работает.



1



Annemesski

2439 / 1178 / 436

Регистрация: 08.11.2016

Сообщений: 3,262

01.09.2020, 17:16

2

Лучший ответ Сообщение было отмечено Zloy_Tip как решение

Решение

Zloy_Tip, закидоны VS-ки, не обращайте внимания или пропишите перед включением заголовков

C
1
#define _CRT_SECURE_NO_WARNINGS



3



Эксперт .NET

6318 / 3936 / 1578

Регистрация: 09.05.2015

Сообщений: 9,237

01.09.2020, 17:49

3

Написано же что функция scanf небезопасна, используйте вместо нее scanf_s, что тут непонятного?



0



3579 / 2249 / 406

Регистрация: 09.09.2017

Сообщений: 9,415

01.09.2020, 18:12

4

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



1



Эксперт .NET

6318 / 3936 / 1578

Регистрация: 09.05.2015

Сообщений: 9,237

01.09.2020, 18:29

5

Цитата
Сообщение от COKPOWEHEU
Посмотреть сообщение

А scanf_s нестандартна, что еще хуже.

Бред не несите… https://en.cppreference.com/w/c/io/fscanf

Цитата
Сообщение от COKPOWEHEU
Посмотреть сообщение

Правильный способ уже указал Annemesski: приказать компилятору следовать стандарту.

Не делайте так как сказал Annemesski, не подвергайте себя и других опасности!!!



0



96 / 69 / 27

Регистрация: 26.08.2020

Сообщений: 361

01.09.2020, 18:37

6

Я тебе советую использовать компилятор GCC.У Microsoft всегда сове видение,причем они не соответствуют стандарту языка!



0



из племени тумба-юбма

2412 / 1741 / 405

Регистрация: 29.11.2015

Сообщений: 8,448

Записей в блоге: 14

01.09.2020, 18:40

7

Someone007, а как тогда быть, если используется другая IDE, отличная от VS? На функции scanf_s будет выдавать ошибку.



0



Эксперт .NET

6318 / 3936 / 1578

Регистрация: 09.05.2015

Сообщений: 9,237

01.09.2020, 18:46

8

Цитата
Сообщение от мама Стифлера
Посмотреть сообщение

а как тогда быть, если используется другая IDE, отличная от VS? На функции scanf_s будет выдавать ошибку.

С чего вдруг? scanf_s это стандартная С функция…



0



3579 / 2249 / 406

Регистрация: 09.09.2017

Сообщений: 9,415

01.09.2020, 23:15

9

Цитата
Сообщение от Someone007
Посмотреть сообщение

Не делайте так как сказал Annemesski, не подвергайте себя и других опасности!!!

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

Цитата
Сообщение от Someone007
Посмотреть сообщение

С чего вдруг? scanf_s это стандартная С функция…

Вот это вы успешно проглядели?

As with all bounds-checked functions, scanf_s , fscanf_s, and sscanf_s are only guaranteed to be available if __STDC_LIB_EXT1__ is defined by the implementation and if the user defines __STDC_WANT_LIB_EXT1__ to the integer constant 1 before including stdio.h.

Иначе говоря, функции *_s опциональны, а вовсе не стандартны.



0



Эксперт .NET

6318 / 3936 / 1578

Регистрация: 09.05.2015

Сообщений: 9,237

02.09.2020, 00:16

10

Цитата
Сообщение от COKPOWEHEU
Посмотреть сообщение

Иначе говоря, функции *_s опциональны, а вовсе не стандартны.

То что они опциональны, не отменяет того факта что они часть стандарта.



0



Вездепух

Эксперт CЭксперт С++

10928 / 5920 / 1619

Регистрация: 18.10.2014

Сообщений: 14,881

02.09.2020, 00:30

11

Цитата
Сообщение от Someone007
Посмотреть сообщение

То что они опциональны, не отменяет того факта что они часть стандарта.

Он просто вбрасывает эту чушь во все темы, где упоминаются эти функции. Лучше просто не обращать на это внимания.

Функции группы _s стандартны. Этот вопрос закрыт и не обсуждается. Не тратьте время на бессмысленный флуд.

Добавлено через 2 минуты

Цитата
Сообщение от scanf
Посмотреть сообщение

У Microsoft всегда сове видение,причем они не соответствуют стандарту языка!

Не надо здесь пороть «пионэрскую» чушь.



0



Модератор

Эксперт PythonЭксперт JavaЭксперт CЭксперт С++

11896 / 7268 / 1721

Регистрация: 25.07.2009

Сообщений: 13,311

02.09.2020, 04:56

12

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

Цитата
Сообщение от Someone007
Посмотреть сообщение

Бред не несите

Цитата
Сообщение от TheCalligrapher
Посмотреть сообщение

Не надо здесь пороть «пионэрскую» чушь

И вообще, будьте оба немного сдержаннее. То, что не все разделяют вашу странную привязанность к M$ — не повод для хамства.



0



Вездепух

Эксперт CЭксперт С++

10928 / 5920 / 1619

Регистрация: 18.10.2014

Сообщений: 14,881

02.09.2020, 05:22

13

Цитата
Сообщение от easybudda
Посмотреть сообщение

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

А, какое это имеет значение? Если бы подобные соображения имели вес, то комитет по стандартизации никогда бы не включил эти функции в стандарт языка. (Скажете, что это следствие «странной привязанности к M$С» самого комитета?). С другой стороны, несмотря на то, что, например, функция strdup присутствует практически везде, стандартной она не является. Так что «поддержка компиляторами» и «стандартность» — это совершенно ортогональные понятия.

К тому же предыдущий оратор пытается навязать нам некую более широкую «логику», согласно которой опциональные свойства языка или стандартной библиотеки являются «нестандартными» (???). В эту категорию, кстати, попадают и VLA, типы вроде uint32_t, uintptr_t, errno_t и многое другое. И эта загадочная ложная «логика» упорно насаждается вышеупомянутым оратором в каждой ветке, где упоминаются функции группы _s.

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



0



3579 / 2249 / 406

Регистрация: 09.09.2017

Сообщений: 9,415

02.09.2020, 07:45

14

Цитата
Сообщение от TheCalligrapher
Посмотреть сообщение

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

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

Цитата
Сообщение от TheCalligrapher
Посмотреть сообщение

В эту категорию, кстати, попадают и VLA, типы вроде uint32_t, uintptr_t, errno_t и многое другое

Про VLA сказать не могу. Просто не интересовался, но слышал, что ее использование чаще мешает, поэтому и смысла пропагандировать нет.
А вот int*_t объективно предпочтительнее еще более нестандартных BYTE, DWORD, u8 и т.д. Ну и если назовете компилятор, который этого не поддерживает — будет полезно. В отличие от того бреда что уже наговорили.

Цитата
Сообщение от TheCalligrapher
Посмотреть сообщение

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

Пресекать надо бездумный формализм из серии «так сказано в стандарте, это ИстинаЪ, молитесь ей». Программирование- ремесло, а не религия. Поэтому каждое утверждение должно быть обосновано.
Так и тут: навязывать стандартизованные (если уж вашему формализму не по нраву «стандартные») типы вроде int16_t полезно. Потому что типы фиксированного размера нужны, а данный вариант, в отличие от других существующих, подкреплен стандартом Си и поддерживается большинством компиляторов. Иначе говоря не столь важно стандарт это или нет, на сегодня этот вариант лучше прочих.
И пресекать навязывание псевдо-безопасных функций вроде scanf_s тоже нужно. Потому что поддерживаются они одним-единственным компилятором (подумайте об этом: профессиональные разработчики даже не пытаются их реализовать). Кроме того, эти функции добавляют иллюзию безопасности, что, вообще-то, опаснее стандартного аналога, изначально постулируемой как требующего внимания. Реальные проблемы, к которым это приведет (помимо снижения безопасности, естественно): переход между архитектурами (в т.ч. на микроеонтроллеры и телефоны), проблемы с поиском помощи (желающий помочь банально не сможет воспроизвести ошибку).



1



из племени тумба-юбма

2412 / 1741 / 405

Регистрация: 29.11.2015

Сообщений: 8,448

Записей в блоге: 14

02.09.2020, 11:18

15

Ну тут пожалуй, я соглашусь с COKPOWEHEU. Если Майкрософт добились, чтоб их новые, «безопасные» функции для их же продукта были занесены в стандарт, еще не означает того, что данные функции можно называть стандартными. Проще сказать так, что для компилятора от Майкрософт, функции группы _s являются стандартными, для остальных компиляторов — нет. Но опять же говоря о безопасности, ведь знающий человек просто напишет код грамотно, тогда и никакого переполнения буфера не будет.

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

Не подумайте что я негативно отношусь к их продуктам, нет. Я негативно отношусь к их политике поведения на рынке.



1



easybudda

Модератор

Эксперт PythonЭксперт JavaЭксперт CЭксперт С++

11896 / 7268 / 1721

Регистрация: 25.07.2009

Сообщений: 13,311

02.09.2020, 13:55

16

Цитата
Сообщение от TheCalligrapher
Посмотреть сообщение

А, какое это имеет значение?

«Причём тут борщ, когда такие дела на кухне?!» (с)

Цитата
Сообщение от TheCalligrapher
Посмотреть сообщение

функция strdup присутствует практически везде, стандартной она не является

Ну она в POSIX входит. К тому же её нужность и полезность, как и многих других пришедших из BSD вещей, становится очевидной по мере использования.

Цитата
Сообщение от TheCalligrapher
Посмотреть сообщение

Скажете, что это следствие «странной привязанности к M$С» самого комитета?

Ну может мелкомягкие им просто денег занесли на «дальнейшее развитие»…

Цитата
Сообщение от TheCalligrapher
Посмотреть сообщение

VLA, типы вроде uint32_t, uintptr_t, errno_t

… входят в ANSI C 99, нормальную поддержку которого M$ сделать так и не удосужились. А в С 11 стали опцией скорее всего по той же причине, по которой были включены *_s функции.

Цитата
Сообщение от COKPOWEHEU
Посмотреть сообщение

Пресекать надо бездумный формализм из серии «так сказано в стандарте, это ИстинаЪ, молитесь ей». Программирование- ремесло, а не религия.

Цитата
Сообщение от COKPOWEHEU
Посмотреть сообщение

Про VLA сказать не могу. Просто не интересовался, но слышал, что ее использование чаще мешает, поэтому и смысла пропагандировать нет.

Я бы сказал, что использование VLA в чём-то сходно с использованием рекурсивных функций — если бездумно их пихать везде, где надо и не надо, обязательно нарвётесь на неприятности. А при разумном подходе может оказаться вполне удобным инструментом…

Zloy_Tip, короче, прийдётся выбирать между

Цитата
Сообщение от Annemesski
Посмотреть сообщение

закидоны VS-ки, не обращайте внимания или пропишите перед включением заголовков

C
1
#define _CRT_SECURE_NO_WARNINGS

и

Цитата
Сообщение от Someone007
Посмотреть сообщение

Написано же что функция scanf небезопасна, используйте вместо нее scanf_s, что тут непонятного?

По мне правильнее первое — как минимум, код будет переносимым и не привязанным к единственному компилятору. Если готовитесь стать адептом культа M$, смело выбирайте второе…



0



Вездепух

Эксперт CЭксперт С++

10928 / 5920 / 1619

Регистрация: 18.10.2014

Сообщений: 14,881

02.09.2020, 14:04

17

Цитата
Сообщение от easybudda
Посмотреть сообщение

… входят в ANSI C 99,

…. являются опциональными в С99 (кроме VLA), т.е, согласно «логике» вышеупомянутого оратора, нестандартными.

Цитата
Сообщение от easybudda
Посмотреть сообщение

нормальную поддержку которого M$ сделать так и не удосужились.

…нормальную поддержку которого MS реализовали уже давно.



0



3579 / 2249 / 406

Регистрация: 09.09.2017

Сообщений: 9,415

02.09.2020, 17:51

18

Цитата
Сообщение от easybudda
Посмотреть сообщение

VLA, типы вроде uint32_t, uintptr_t, errno_t
… входят в ANSI C 99, нормальную поддержку которого M$ сделать так и не удосужились. А в С 11 стали опцией скорее всего по той же причине, по которой были включены *_s функции.

Скорее, потому что строго 16-битного типа может быть не предусмотрено архитектурой. Или, скажем, строго 8-битного. Вроде бывают такие экзотические камни, хотя я их не видел.
При том что int_fast32_t и int_least32_t если не ошибаюсь, таки обязательны даже для них.

Цитата
Сообщение от TheCalligrapher
Посмотреть сообщение

…нормальную поддержку которого MS реализовали уже давно.

Но это не мешает им пихать свои DWORD и прочие рудименты во все щели.



0



Модератор

1791 / 889 / 164

Регистрация: 23.07.2018

Сообщений: 3,077

Записей в блоге: 3

02.09.2020, 18:33

19

Zloy_Tip, насколько я понимаю microsoft, это предупреждение компилятора, а не ошибка
https://docs.microsoft.com/en-… el-3-c4996

В учебных упражнениях проще всего не обращать внимание на это сообщение.
Или следовать совету поставщика компилятора:

Цитата
Сообщение от microsoft

Microsoft deprecated some CRT and C++ Standard Library functions and globals because more secure versions are available. Most of the deprecated functions allow unchecked read or write access to buffers. Their misuse can lead to serious security issues. The compiler issues a deprecation warning for these functions, and suggests the preferred function.

To fix this issue, we recommend you use the function or variable safe-version instead. Sometimes you can’t, for portability or backwards compatibility reasons. Carefully verify it’s not possible for a buffer overwrite or overread to occur in your code. Then, you can turn off the warning.

To turn off deprecation warnings for these functions in the CRT, define _CRT_SECURE_NO_WARNINGS.

Добавлено через 13 минут

Цитата
Сообщение от COKPOWEHEU
Посмотреть сообщение

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

Каких ещё профессиональных разработчиков Вы знаете, кроме Microsoft ?
https://docs.oracle.com/cd/E88… -s-3c.html



0



Вездепух

Эксперт CЭксперт С++

10928 / 5920 / 1619

Регистрация: 18.10.2014

Сообщений: 14,881

02.09.2020, 18:35

20

Цитата
Сообщение от politoto
Посмотреть сообщение

Zloy_Tip, насколько я понимаю microsoft, это предупреждение компилятора, а не ошибка

Тема уже 100500 раз разбиралась здесь. Если в установках проекта включены «SDL checks», то это — именно ошибка, а не предупреждение компилятора. А «SDL checks» в нынешних версиях MSVC включены по умолчанию. Поэтому большинство новичков натыкаются именно на Ошибка C4996, как и процитировано у ТС.



0



IT_Exp

Эксперт

87844 / 49110 / 22898

Регистрация: 17.06.2006

Сообщений: 92,604

02.09.2020, 18:35

Помогаю со студенческими работами здесь

Линкер ругается на функцию
// laba7evteev.cpp: определяет можно ли из слов первой строки составить вторую
//

#include…

Ругается на функцию в функции
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;time.h&gt;
#include &lt;math.h&gt;
#define A 10…

Компилятор ругается на функцию strncat
#include &lt;iostream&gt;
#include &lt;string&gt;
using namespace std;

long p,x,i,j,k,d,l;
char t,s;…

В main объявить переменную, потом в другой функции ее инициализировать, вызывая функцию scanf
Помогите разобраться с задачей. Нужно в main объявить переменную, потом в другой функции ее…

Антивирус ругается на функцию отправки почты
Антивирус — Kaspersky Internet Security 2012.
ОС — Windows 7 x64.
IDE -…

Почему компилятор ругается на математическую функцию?
#include &quot;stdafx.h&quot;
#include &lt;iostream&gt;
#include &lt;string&gt;
#include &lt;cmath&gt;

using namespace…

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:

20

Содержание

#include <stdio.h>
int scanf(const char *format, ...);

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

В версии C99 к параметру format применен квалификатор restrict.

Управляющая строка, задаваемая параметром format, состоит из символов трех категорий:

  • спецификаторов формата;
  • пробельных символов;
  • символов, отличных от пробельных.

Спецификации формата начинаются знаком % и сообщают функции scanf() тип данного, которое будет прочитано. Спецификации формата приведены в таблице 13.3. Например, по спецификации %s будет прочитана строка, а по спецификации %d — целое значение. Строка форматирования читается слева направо, и спецификации формата сопоставляются аргументам в порядке их перечисления в списке аргументов.

Таблица 13.3. Спецификации формата функции scanf()

Код Назначение
%a Читает значение с плавающей точкой (только C99)
%A Аналогично коду %a (только C99)
%c Читает один символ
%d Читает десятичное целое
%i Читает целое в любом формате (десятичное, восьмеричное или шестнадцатеричное)
%e Читает число с плавающей точкой
%E Аналогично коду %e
%f Читает число с плавающей точкой
%F Аналогично коду %f (только С99)
%g Читает число с плавающей точкой
%G Аналогично коду %g
%o Читает восьмеричное число
%s Читает строку
%x Читает шестнадцатеричное число
%X Аналогично коду %x
%p Читает указатель
%n Принимает целое значение, равное количеству прочитанных до сих пор символов
%u Читает десятичное целое без знака
%[] Просматривает набор символов
%% Читает знак процента

По умолчанию спецификации a, f, e и g заставляют функцию scanf() присваивать данные переменным типа float. Если перед одной из этих спецификаций поставить модификатор l, функция scanf() присвоит прочитанные данные переменной типа double. Использование же модификатора L означает, что полученное значение присвоится переменной типа long double.

Современные компиляторы, поддерживающие добавленные в 1995 году средства работы с двухбайтовыми символами, позволяют к спецификации c применить модификатор l; тогда будет считаться, что соответствующий указатель указывает на двухбайтовый символ (т.е. на данное типа whcar_t). Модификатор l также можно использовать с кодом формата s; тогда будет считаться, что соответствующий указатель указывает на строку двухбайтовых символов. Кроме того, модификатор l можно использовать для того, чтобы указать, что набор сканируемых символов состоит из двухбайтовых символов.

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

Если в строке форматирования встретился символ, отличный от разделителя, то функция scanf() прочитает и отбросит его. Например, если в строке форматирования встретится %d, %d, то функция scanf() сначала прочитает целое значение, затем прочитает и отбросит запятую и, наконец, прочитает еще одно целое. Если заданный символ не найден, функция scanf() завершает работу.

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

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

scanf("%d%d", &r, &c);

примет значения, введенные как 10 20, но откажется от последовательности символов 10, 20.

Символ *, стоящий после знака % и перед кодом формата, прочитает данные заданного типа, но запретит их присваивание. Следовательно, оператор

scanf("%d%*c%d", &x, &y); 

при вводе данных в виде 10/20 поместит значение 10 в переменную x, отбросит знак деления и присвоит значение 20 переменной у.

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

scanf("%20s", address); 

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

Хотя пробелы, символы табуляции и разделители строк используются в качестве разделителей полей, при чтении одиночного символа они читаются подобно любому другому символу. Например, если входной поток состоит из символов x у, то оператор

scanf("%c%c%c", &a, &b, &c);

поместит символ x в переменную а, пробел — в переменную b, а символ у — в переменную с.

Помните, что любые символы управляющей строки (включая пробелы, символы табуляции и новой строки), не являющиеся спецификациями формата, используются для установки соответствия и отбрасывания символов из входного потока. Любой соответствующий им символ отбрасывается. Например, если поток ввода выглядит, как 10t20, оператор

scanf("%dt%d", &x, &y);

присвоит переменной x значение 10, а переменной у — значение 20. Символ t отбрасывается, так как он присутствует в управляющей строке.

Функция scanf() поддерживает спецификатор формата общего назначения, называемый набором сканируемых символов (scanset). В этом случае определяется набор символов, которые могут быть прочитаны функцией scanf() и присвоены соответствующему массиву символов. Для определения такого набора символы, подлежащие сканированию, необходимо заключить в квадратные скобки. Открывающая квадратная скобка должна следовать сразу за знаком процента. Например, следующий набор сканируемых символов указывает на то, что необходимо читать только символы A, B и C.

%[ABC]

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

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

Во многих реализациях допускается задавать диапазон с помощью дефиса. Например, функция scanf(), встречая набор сканируемых символов в виде %[A-z], будет читать символы, попадающие в диапазон от А до Z.

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

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

Модификаторы формата, добавленные к функции scanf() Стандартом C99

В версии C99 для использования в функции scanf() добавлены модификаторы формата hh, ll, j, z и t. Модификатор hh можно применять к спецификациям d, i, о, u, x и n. Он означает, что соответствующий аргумент является указателем на значение типа signed char или unsigned char. Модификатор ll также можно применять к спецификациям d, i, о, u, x и n. Он означает, что соответствующий аргумент является указателем на значение типа signed long long int или unsigned long long int.

Moдификaтop фopмaтa j, кoтopый пpимeняeтcя к cпeцификaциям d, i, о, u, х и n означает, что соответствующий аргумент является указателем на значение типа intmax_t или uintmax_t. Эти типы объявлены в заголовке <stdint.h> и служат для хранения целых максимально возможной разрядности.

Модификатор формата z, который применяется к спецификациям d, i, о, u, х и n означает, что соответствующий аргумент является указателем на объект типа size_t. Этот тип объявлен в заголовке <stddef.h> и служит для хранения результата операции sizeof.

Модификатор формата t, который применяется к спецификациям d, i, о, u, х и n означает, что соответствующий аргумент является указателем на объект типа ptrdiff_t. Этот тип объявлен в заголовке <stddef.h> и служит для хранения значения разности между двумя указателями.

Пример

Действие данных операторов scanf() объясняется в комментариях.

#include <stdio.h>

int main(void)
{
  char str[80], str2[80];
  int i;

  /* читается строка и целое значение */
  scanf("%s%d", str, &i);

  /* в переменную str считывается не более 79 символов */
  scanf("%79s", str);

  /* целое, расположенное между двумя строками, пропускается */
  scanf("%s%*d%s", str, str2);

  return 0;
}

Зависимые функции

printf() fscanf()

Содержание | Глава 13


#include <stdio.h>
int scanf(const char *format, ...);

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

В версии C99 к параметру format применен квалификатор restrict.

Управляющая строка, задаваемая параметром format, состоит из символов трех категорий:

  • спецификаторов формата;
  • пробельных символов;
  • символов, отличных от пробельных.

Спецификации формата начинаются знаком % и сообщают функции scanf() тип данного, которое будет прочитано. Спецификации формата приведены в таблице 13.3. Например, по спецификации %s будет прочитана строка, а по спецификации %d — целое значение. Строка форматирования читается слева направо, и спецификации формата сопоставляются аргументам в порядке их перечисления в списке аргументов.

Таблица 13.3. Спецификации формата функции scanf()

Код Назначение
%a Читает значение с плавающей точкой (только C99)
%A Аналогично коду %a (только C99)
%c Читает один символ
%d Читает десятичное целое
%i Читает целое в любом формате (десятичное, восьмеричное или шестнадцатеричное)
%e Читает число с плавающей точкой
%E Аналогично коду %e
%f Читает число с плавающей точкой
%F Аналогично коду %f (только С99)
%g Читает число с плавающей точкой
%G Аналогично коду %g
%o Читает восьмеричное число
%s Читает строку
%x Читает шестнадцатеричное число
%X Аналогично коду %x
%p Читает указатель
%n Принимает целое значение, равное количеству прочитанных до сих пор символов
%u Читает десятичное целое без знака
%[] Просматривает набор символов
%% Читает знак процента

По умолчанию спецификации a, f, e и g заставляют функцию scanf() присваивать данные переменным типа float. Если перед одной из этих спецификаций поставить модификатор l, функция scanf() присвоит прочитанные данные переменной типа double. Использование же модификатора L означает, что полученное значение присвоится переменной типа long double.

Современные компиляторы, поддерживающие добавленные в 1995 году средства работы с двухбайтовыми символами, позволяют к спецификации c применить модификатор l; тогда будет считаться, что соответствующий указатель указывает на двухбайтовый символ (т.е. на данное типа whcar_t). Модификатор l также можно использовать с кодом формата s; тогда будет считаться, что соответствующий указатель указывает на строку двухбайтовых символов. Кроме того, модификатор l можно использовать для того, чтобы указать, что набор сканируемых символов состоит из двухбайтовых символов.

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

Если в строке форматирования встретился символ, отличный от разделителя, то функция scanf() прочитает и отбросит его. Например, если в строке форматирования встретится %d, %d, то функция scanf() сначала прочитает целое значение, затем прочитает и отбросит запятую и, наконец, прочитает еще одно целое. Если заданный символ не найден, функция scanf() завершает работу.

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

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

scanf("%d%d", &r, &c);

примет значения, введенные как 10 20, но откажется от последовательности символов 10, 20.

Символ *, стоящий после знака % и перед кодом формата, прочитает данные заданного типа, но запретит их присваивание. Следовательно, оператор

scanf("%d%*c%d", &x, &y);

при вводе данных в виде 10/20 поместит значение 10 в переменную x, отбросит знак деления и присвоит значение 20 переменной у.

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

scanf("%20s", address);

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

Хотя пробелы, символы табуляции и разделители строк используются в качестве разделителей полей, при чтении одиночного символа они читаются подобно любому другому символу. Например, если входной поток состоит из символов x у, то оператор

scanf("%c%c%c", &a, &b, &c);

поместит символ x в переменную а, пробел — в переменную b, а символ у — в переменную с.

Помните, что любые символы управляющей строки (включая пробелы, символы табуляции и новой строки), не являющиеся спецификациями формата, используются для установки соответствия и отбрасывания символов из входного потока. Любой соответствующий им символ отбрасывается. Например, если поток ввода выглядит, как 10t20, оператор

scanf("%dt%d", &x, &y);

присвоит переменной x значение 10, а переменной у — значение 20. Символ t отбрасывается, так как он присутствует в управляющей строке.

Функция scanf() поддерживает спецификатор формата общего назначения, называемый набором сканируемых символов (scanset). В этом случае определяется набор символов, которые могут быть прочитаны функцией scanf() и присвоены соответствующему массиву символов. Для определения такого набора символы, подлежащие сканированию, необходимо заключить в квадратные скобки. Открывающая квадратная скобка должна следовать сразу за знаком процента. Например, следующий набор сканируемых символов указывает на то, что необходимо читать только символы A, B и C.

%[ABC]

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

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

Во многих реализациях допускается задавать диапазон с помощью дефиса. Например, функция scanf(), встречая набор сканируемых символов в виде %[A-z], будет читать символы, попадающие в диапазон от А до Z.

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

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

Модификаторы формата, добавленные к функции scanf() Стандартом C99

В версии C99 для использования в функции scanf() добавлены модификаторы формата hh, ll, j, z и t. Модификатор hh можно применять к спецификациям d, i, о, u, x и n. Он означает, что соответствующий аргумент является указателем на значение типа signed char или unsigned char. Модификатор ll также можно применять к спецификациям d, i, о, u, x и n. Он означает, что соответствующий аргумент является указателем на значение типа signed long long int или unsigned long long int.

Moдификaтop фopмaтa j, кoтopый пpимeняeтcя к cпeцификaциям d, i, о, u, х и n означает, что соответствующий аргумент является указателем на значение типа intmax_t или uintmax_t. Эти типы объявлены в заголовке <stdint.h> и служат для хранения целых максимально возможной разрядности.

Модификатор формата z, который применяется к спецификациям d, i, о, u, х и n означает, что соответствующий аргумент является указателем на объект типа size_t. Этот тип объявлен в заголовке <stddef.h> и служит для хранения результата операции sizeof.

Модификатор формата t, который применяется к спецификациям d, i, о, u, х и n означает, что соответствующий аргумент является указателем на объект типа ptrdiff_t. Этот тип объявлен в заголовке <stddef.h> и служит для хранения значения разности между двумя указателями.

Пример

Действие данных операторов scanf() объясняется в комментариях.

#include <stdio.h>
int main(void)
{
  char str[80], str2[80];
  int i;
  /* читается строка и целое значение */
  scanf("%s%d", str, &i);
  /* в переменную str считывается не более 79 символов */
  scanf("%79s", str);
  /* целое, расположенное между двумя строками, пропускается */
  scanf("%s%*d%s", str, str2);
  return 0;
}

Зависимые функции

printf() fscanf()


Содержание | Глава 13

Other Alias

fscanf, sscanf, vscanf, vsscanf, vfscanf

ОБЗОР

#include <stdio.h>
int scanf(const char *format, …);
int fscanf(FILE *stream, const char *format, …);
int sscanf(const char *str, const char *format, …);


#include <stdarg.h>
int vscanf(const char *format, va_list ap);
int vsscanf(const char *str, const char *format, va_list ap);
int vfscanf(FILE *stream, const char *format, va_list ap);

Требования макроса тестирования свойств для glibc
(см. feature_test_macros(7)):

vscanf(), vsscanf(), vfscanf():

_XOPEN_SOURCE >= 600 || _ISOC99_SOURCE || _POSIX_C_SOURCE >= 200112L;

или cc -std=c99

ОПИСАНИЕ

Группа функций scanf() считывает вводимую информацию в соответствии с
форматом format так, как описано ниже. В формате могут указываться
определители преобразования (conversion specifications); результаты
каждого преобразования, если они производились, сохраняются по адресам
параметров указателей, передаваемых после format. Каждый параметр
указатель должен быть того же типа, что и значение, получаемое в
результате преобразования данных в соответствии с форматом.

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

Функция scanf() считывает информацию из стандартного потока ввода
stdin; fscanf() считывает информацию из потока, на который указывает
stream, а sscanf() считывает информацию из символьной строки, на
которую указывает str.

Функция vfscanf() является аналогом vfprintf(3) и читает информацию из
потока, на который указывает указатель stream, используя список
указателей переменной длины (смотрите stdarg(3)). Функция vscanf()
считывает список параметров переменной длины из стандартного ввода, а
функция vsscanf() считывает его из строки. Эти функции являются аналогами
функций vprintf(3) и vsprintf(3), соответственно.

Строка format состоит из последовательности инструкций (directives),
которые описывают порядок обработки входных символов. Если обработка
инструкции завершается с ошибкой, то чтение прекращается и scanf()
завершает работу. «Отказом» может быть: ошибка ввода, то есть
недоступность входных символов или ошибка совпадения, то есть получены
неподходящие данные (смотрите далее).

Инструкцией может быть:

  • Последовательность пробельных символов (пробел, табуляция, символ новой
    строки и т. д.; смотрите isspace(3)). Эта инструкция совпадает с любым
    количеством пустого места, включая отсутствие данных.
  • Обычный символ (т. е., отличный от пробельного или «%»). Такой символ должен
    точно совпадать со следующим символом входных данных.
  • Определитель преобразования, который начинается с символа «%»
    (процент). Последовательность символов ввода преобразуется в соответствии с
    определителем, а результат помещается в соответствующий параметр
    указатель. Если следующий элемент ввода не соответствует определителю
    преобразования, то преобразование завершается с ошибкой — ошибкой
    совпадения
    .

Каждый определитель преобразования в format начинается с символа «%»
или последовательности символов «%n$» (смотрите о разнице далее) за
которым следует:

  • Необязательный символ подавления назначения «*»: scanf() читает данные
    как предписано определителем преобразования, но отбрасывает
    их. Соответствующий параметр указатель необязателен, и этот определитель
    не учитывается в счётчике успешных назначений, возвращаемом scanf().
  • Необязательный символ «m». Используется в строковых преобразованиях (%s,
    %c, %[) и освобождает вызывающего от необходимости выделять
    соответствующий буфер для хранения входных данных: вместо этого scanf()
    выделяет буфер достаточного размера и присваивает адрес этого буфера
    соответствующему параметру указателю, который должен быть указателем на
    переменную char * (эту переменную не нужно инициализировать перед
    вызовом). Вызывающий должен вызвать free(3) для этого буфера, как только
    он станет ненужным.
  • Необязательное целое десятичное число, которое задаёт максимальную ширину
    поля
    . Чтение символов прерывается по достижении этого максимума или при
    нахождении несовпадающего символа, неважно что случится раньше. В
    большинстве преобразований начальные пробельные символы отбрасываются
    (исключения приведены далее), и эти отброшенные символы не учитываются в
    максимальной ширине поля. В преобразованных строках ввода сохраняется
    конечный байт null (») для отметки конца ввода; в максимальной
    ширине поля он также не учитывается.
  • Необязательный символ модификатора типа. Например, модификатор типа l
    используется в преобразованиях целых чисел, например с помощью %d, для
    указания того, что соответствующий параметр указатель ссылается на
    long int, а не на int.
  • Определитель преобразования, который задаёт тип входного преобразования.

Определители преобразования в format бывают двух видов: начинающиеся с
«%» и начинающиеся с «%n$». Эти два вида не должны использоваться
одновременно в строке format, за исключением случая, когда строка,
содержащая определители «%n$», может включать %% и %*. Если в
format содержатся определители «%», то они задаются в порядке появления
параметров указателей, указанных после. В форме «%n$» (есть в
POSIX.1-2001, но отсутствует в C99), n — это десятичное целое, которое
задаёт в какое место должен быть помещён ввод, то есть указывает на
расположение n-го параметра указателя, передаваемого после format.

Преобразования

Следующие символы модификаторов типа (type modifier characters) могут
появляться в определении преобразования:

h
Обозначает, что преобразование будет одним из d, i, o, u, x,
X или n и следующий указатель является указателем на short int или
на unsigned short int (но не int).
hh
Как h, но следующий указатель — указатель на signed char или
unsigned char.
j
Как h, но следующий указатель — указатель на intmax_t или
uintmax_t. Этот модификатор появился в C99.
l
Обозначает, что преобразование будет одним из d, i, o, u, x,
X или n и следующий указатель является указателем на long int или
unsigned long int (но не int), или что преобразование будет одним из
e, f или g и следующий указатель является указателем на double
(но не float). Указание двух символов l эквивалентно L. Если
используется с %c или %s, то соответствующий параметр считается
указателем на широкий символ или строку широких символов, соответственно.
L
Обозначает, что преобразование будет одним из e, f или g и
следующий указатель является указателем на long double или преобразование
будет одним из d, i, o, u или x и следующий указатель
является указателем на long long.
q
Эквивалентен L. Данный определитель отсутствует в ANSI C.
t
Как h, но следующий указатель — указатель на ptrdiff_t. Этот
модификатор появился в C99.
z
Как h, но следующий указатель — указатель на size_t. Этот модификатор
появился в C99.

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

%
Совпадает с литерой «%». То есть %% в строке формата соответствует
одиночному символу данных «%». Преобразование не выполняется (но начальные
пробельные символы отбрасываются) и назначения не происходит.
d
Совпадает с необязательным знаковым десятичным целым; следующий указатель
должен быть указателем на int.
D
Эквивалентно ld; оставлено только для обратной совместимости (замечание:
есть только в libc4. В libc5 и glibc %D просто игнорируется, что приводит
к непонятным ошибкам в старых программах).
i
Совпадает с необязательным знаковым целым; следующий указатель должен быть
указателем на int. Целое считывается как шестнадцатеричное число, если
начинается с 0x или 0X, как восьмеричное, если начинается с 0 и как
десятичное в остальных случаях. Используются только символы, подходящие для
работы с выбранным основанием системы счисления.
o
Совпадает с необязательным беззнаковым восьмеричным целым; следующий
указатель должен быть указателем на unsigned int.
u
Совпадает с необязательным беззнаковым десятичным целым; следующий указатель
должен быть указателем на unsigned int.
x
Совпадает с необязательным беззнаковым шестнадцатеричным целым; следующий
указатель должен быть указателем на unsigned int.
X
Эквивалентно x.
f
Совпадает с необязательным знаковым числом с плавающей запятой; следующий
указатель должен быть указателем на float.
e
Эквивалентно f.
g
Эквивалентно f.
E
Эквивалентно f.
a
(C99) Эквивалентно f.
s
Совпадает с последовательностью непробельных символов; следующий указатель
должен указывать на первый элемент массива символов достаточной длины для
сохранения входной последовательности и завершающего байта null
(»), который добавляется автоматически. Входная строка обрывается
при появлении пробельного символа или достижении максимальной ширины поля,
неважно что случится раньше.
c
Совпадает с последовательностью символов, чья длина задаётся максимальной
шириной поля
(по умолчанию 1); следующий указатель должен быть указателем
на char, и должно быть достаточно места для всех символов (завершающий
байт null не добавляется). Обычный пропуск начальных пробелов не
выполняется. Чтобы пропустить пробелы, явно укажите их в формате.
[
Совпадает с непустой последовательностью символов из задаваемого набора
допустимых символов; следующий указатель должен быть указателем на char и
должно быть достаточно места для всех символов в строке плюс завершающий
байт null. Обычный пропуск начальных пробелов не выполняется. Строка будет
состоять (или нет) из символов определённого набора; набор задаётся
указанием символов между символом открывающей скобки [ и закрывающей
скобки ]. Набором определяются исключающиеся символы, если первым
символом после открывающей скобки является символ диакритического знака
(^). Чтобы включить закрывающую скобку в набор, укажите её первым
символом после открывающей скобки или диакритического знака; в любой другой
позиции она закрывает набор. Символ переноса также является
специализированным; если он указывается между двумя символами, то в набор
добавляются все лежащие в промежутке символы. Чтобы добавить в набор символ
переноса укажите его последним, перед конечной закрывающей
скобкой. Например, [^]0-9-] означает, что «все символы, кроме закрывающей
скобки, цифр от 0 до 9 и переноса». Строка обрывается при появлении символа
не из набора (или, при указании символа диакритического знака, из) или при
достижении ширины поля.
p
Совпадает со значением указателя (как выводится при %p в printf(3));
следующий указатель должен быть указателем на void.
n
Ничего не ожидается; вместо этого количество символов, использованных к
настоящему времени из ввода, сохраняется по следующему указателю, который
должен быть указателем на int. Это не преобразование и не
увеличивает счётчик, возвращаемый функцией. Назначение может подавляться при
указании символа подавления назначения *, но влияние этого на
возвращаемое значение не определено. Поэтому преобразования %*n лучше не
использовать.

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ

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

Если конец входных данных был достигнут раньше, чем произошло хотя бы одно
совпадение или при ошибке совпадения возвращается значение EOF. Значение
EOF также возвращается при ошибке чтения; в этом случае для потока
устанавливается индикатор ошибки (смотрите ferror(3)), а в errno
указывается номер ошибки.

ОШИБКИ

EAGAIN
Файловый дескриптор stream помечен как неблокирующий, а чтение вызвало бы
блокировку.
EBADF
Неправильный файловый дескриптор для stream или он не открыт на чтение.
EILSEQ
Из входной байтовой последовательности невозможно создать корректный символ.
EINTR
Операция чтения была прервана сигналом; смотрите signal(7).
EINVAL
Недостаточно параметров или format равен NULL.
ENOMEM
Не хватает памяти.
ERANGE
Результат преобразования целого превысил бы размер, который можно хранить в
соответствующем целочисленном типе.

АТРИБУТЫ

Описание терминов данного раздела смотрите в attributes(7).

Интерфейс Атрибут Значение
scanf(),
fscanf(),

sscanf(),
vscanf(),

vsscanf(),
vfscanf()

безвредность в нитях безвредно (MT-Safe locale)

СООТВЕТСТВИЕ СТАНДАРТАМ

Функции fscanf(), scanf() и sscanf() соответствуют C89, C99 и
POSIX.1-2001. В этих стандартах не определена ошибка ERANGE.

Определитель q в 4.4BSD используется для long long, а определители
ll или L используются в GNU для преобразования целых чисел.

Версия Linux этих функций основана на библиотеке GNU libio. Более
точное описание функций можно найти в документации в формате info на
GNU libc (glibc-1.08).

ЗАМЕЧАНИЯ

Модификатор выделения-назначения «a»

Первоначально, в библиотеке GNU C поддерживалось динамическое выделение
памяти для входных строк при указании символа a (нестандартное
расширение) (это свойство существует до glibc 2.0). То есть можно указать
scanf() выделить буфер под входную строку, передав в указателе только
указатель на буфер *buf:


    char *buf;

    scanf(«%as», &buf);

Использование буквы a для этой цели проблематично, так как a также
используется в стандарте ISO C как синоним f (ввод данных с плавающей
запятой). В POSIX.1-2008 для назначения с выделением определён модификатор
m (смотрите в ОПИСАНИЕ выше).

Заметим, что модификатор a недоступен, если программа скомпилирована
посредством gcc -std=c99 или gcc -D_ISOC99_SOURCE (если не определён
_GNU_SOURCE); в этом случае a рассматривается как определитель чисел с
плавающей запятой (смотрите выше).

Поддержка модификатора m была добавлена в glibc начиная с версии 2.7, и в
новых программах нужно использовать этот модификатор вместо a.

Стандартизированный в POSIX модификатор m имеет дополнительные
преимущества над a:

*
Он может также применяться к определителям преобразования %c (например,
%3mc).
*
Исчезает неоднозначность с определителем преобразования чисел с плавающей
запятой %a (не подвержен влиянию gcc -std=c99).

ДЕФЕКТЫ

Все функции полностью соответствуют C89, но предоставляют дополнительные
определители q и a, а также дополнительные возможности определителей
L и l. Последнее может считаться дефектом, так как это изменяет
поведение определителей, заданное в C89.

Некоторые комбинации модификаторов типов и определителей преобразования,
определённые в ANSI C, не имеют смысла (например, %Ld). Хотя они могут
иметь хорошо описанное поведение в Linux, это не обязательно так на других
архитектурах. Поэтому, обычно, лучше использовать модификаторы, не
определённые в ANSI C, то есть использовать q вместо L в комбинации с
преобразованием d, i, o, u, x и X или ll.

Работа q отличается от работы в 4.4BSD, так как может использоваться при
преобразовании вещественных числе подобно L.

ПРИМЕР

Чтобы использовать определитель динамического выделения при преобразовании,
укажите m в качестве модификатора длины (в виде %ms или
%m[диапазон]). Вызывающий должен вызвать free(3) для
возвращённой строки как в следующем примере:

char *p;
int n;
errno = 0;
n = scanf("%m[a-z]", &p);
if (n == 1) {
    printf("чтение: %sn", p);
    free(p);
} else if (errno != 0) {
    perror("scanf");
} else {
    fprintf(stderr, "Нет совпадающих символовn");
}

Как показано в примере выше, необходимо вызывать free(3) только, если при
вызове scanf() была прочитана строка.

Понравилась статья? Поделить с друзьями:
  • Sc542 ошибка ricoh sp 3510
  • Sc542 ошибка ricoh sp 150w
  • Sc500 00 ricoh 301 ошибка
  • Sc396 ошибка ricoh sp c261sfnw
  • Sc396 ошибка ricoh sp c260