Как в s/fscanf() гарантировать, что вся строка формата соответствует всему вводу?

Семейство функций *scanf() возвращает количество успешно сопоставленных и назначенных «входных элементов» (то есть спецификаций преобразования):

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

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

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

// read the response to a CSI 14 t request
int r = sscanf(input, "\033[4;%d;%dt", &resp_y, &resp_x);

Рассмотрим четыре потенциальных входа для этого вызова sscanf:

  1. "\033[4;100;200t"
  2. "\033[4;100;200"
  3. "\033[4;100;200foobar"
  4. "\033[4;100;200tfoobar"

Насколько я понимаю, в этом случае возвращаемое значение (r) будет установлено равным 2 во всех трёх случаях. Однако действительны только входные данные (1), тогда как входные данные (2) и (3) недействительны, а входные данные (4) содержат конечные данные.


Каков самый чистый/правильный способ отличить (1) от (2), (3) и от (4), т. е. гарантировать, что вся строка формата успешно сопоставляется со всем вводом?

Меня интересуют ответы на этот вопрос как для sscanf(), так и для fscanf().

sscanf (и его братья и сестры) — очень простой инструмент для анализа и проверки строк. Если вам нужна более продвинутая проверка, вам нужно создать собственный синтаксический анализатор для анализа конкретной строки.
Some programmer dude 08.08.2024 16:19

В вашем заголовке упоминается scanf, но в вашем примере используется sscanf. То, что представляет собой «весь ввод», для них различается, в том числе, хотите ли вы для scanf считать весь ввод всем потоком до конца файла или только одной целой строкой, введенной пользователем, или чем-то еще. Для sscanf вы можете использовать %n в конце, чтобы получить количество прочитанных символов и сравнить его с длиной строки. Для scanf вы можете использовать getchar позже, чтобы увидеть, является ли следующий символ новой строкой или возвращается ли EOF, по желанию, и, при желании, использовать ungetc, чтобы вернуть символ обратно.

Eric Postpischil 08.08.2024 16:20

Итак, отредактируйте вопрос, чтобы уточнить, запрашиваете ли вы sscanf из строки или scanf из потока и что вы считаете «полным вводом».

Eric Postpischil 08.08.2024 16:21

@EricPostpischil Второе слово вопроса указывает на то, что они спрашивают о семействе scanf, а не об одной конкретной функции.

Barmar 08.08.2024 17:00

@Barmar: (a) Семья *scanf обсуждается в качестве справочной/вводной информации. Здесь не говорится, что они спрашивают обо всей семье. (б) Для sscanf и scanf проблема остается иной. Вопрос следует сузить. В самом списке ОП необходимо уточнить, хотят ли они спросить об одном, о другом или вообще об обоих.

Eric Postpischil 08.08.2024 17:03

Как упомянул @Someprogrammerdude, *scanf — неправильный инструмент. Но, возможно, вы можете просто заменить t в конце строки формата на %c, а затем проверить возвращаемое значение и убедиться, что оно скопировало t в цель. (Тогда окончание строки 200toobar также будет совпадать, поэтому вам нужно добавить больше логики, и в конце концов вы все равно перестанете использовать scanf.)

William Pursell 08.08.2024 17:42

@EricPostpischil Хороший улов. Мой непосредственный вопрос касался sscanf(), но мне также был бы интересен ответ на аналогичный вопрос для fscanf().

intelfx 08.08.2024 18:14
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
7
57
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

«Самый чистый/правильный способ отличить» такие входные данные — избегать семейства функций scanf.

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

Ответ принят как подходящий

Использовать:

int n = 0;
int r = sscanf(input, "\033[4;%d;%dt%n", &resp_y, &resp_x, &n);
if (r != 2 || n == 0)
{
    …scanning failed…
}

Обратите внимание, что присвоение n не считается «конверсией», поэтому оно не учитывается в возвращаемом значении.

Если вы хотите убедиться, что с sscanf() использовалась вся строка, вы можете проверить:

if (r == 2 && n != 0 && input[n] == '\0')
{
   …whole string was scanned…
}

Этот метод с использованием %n можно использовать с любой функцией семейства scanf() . Хотя я дал ссылку на документацию POSIX, стандарт C говорит то же самое. Если вы читаете из файла (fscanf() или scanf(), а не sscanf()), вы, вероятно, не захотите проверять значение n больше, чем показано в первом фрагменте. Если вы хотите проверить наличие новой строки (или другого пробела), вы не сможете сделать это так легко с помощью scanf() — см. Каков эффект конечных пробелов в строке формата scanf()?.

Разве так не должно быть n != strlen(input)?

Barmar 08.08.2024 18:14

Спасибо, я лишь смутно знал о %n и не думал использовать его таким образом. Однако меня также интересует часть вопроса «весь ввод».

intelfx 08.08.2024 18:23

@Barmar: это зависит от того, чего ты хочешь. Если t отсутствует, %n не будет обрабатываться, поэтому для него останется 0. Если вы не хотите, чтобы с sscanf() и др. ничего не оставалось, то n != strlen(input) проверит, что все символы в строке обработаны. Конечно, вы также можете использовать if (input[n] != '\0'). С файловыми функциями scanf() вы, вероятно, не захотите выполнять такую ​​проверку длины; после сканирования в файле могло быть больше данных.

Jonathan Leffler 08.08.2024 18:27

Это то, чего ОП говорит, что они хотят. Предполагается, что пример №3 не удался, потому что в конце входных данных стоит foobar.

Barmar 08.08.2024 18:34

Ах, кажется, я неправильно понял, потому что не увидел t в их строке формата.

Barmar 08.08.2024 18:35

@Barmar Тоже хороший улов, меня также интересует обнаружение конечных данных (согласно заголовку). Я отредактировал вопрос и этот ответ, чтобы добавить свое понимание эквивалента fscanf().

intelfx 08.08.2024 18:38

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

Jonathan Leffler 08.08.2024 18:40

@JonathanLeffler Не могли бы вы уточнить, что именно вы подразумеваете под «проверкой конечных пробелов»?

intelfx 08.08.2024 18:43

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

Jonathan Leffler 08.08.2024 18:45

@JonathanLeffler Я прочитал связанный вопрос, прежде чем задать. Вопрос остается в силе. Понятно, что если мы говорим о «всем вводе», то мы не имеем дело с интерактивным вводом.

intelfx 08.08.2024 18:52

Другие вопросы по теме