Семейство функций *scanf() возвращает количество успешно сопоставленных и назначенных «входных элементов» (то есть спецификаций преобразования):
ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
В случае успеха эти функции возвращают количество успешно сопоставленных и назначенных входных элементов; оно может быть меньше предусмотренного или даже равно нулю в случае неудачного раннего сопоставления.
Теперь рассмотрим строку формата, которая содержит некоторые спецификации преобразования, чередующиеся с некоторыми литеральными символами и заканчивающуюся одним или несколькими литеральными символами:
// read the response to a CSI 14 t request
int r = sscanf(input, "\033[4;%d;%dt", &resp_y, &resp_x);
Рассмотрим четыре потенциальных входа для этого вызова sscanf:
"\033[4;100;200t""\033[4;100;200""\033[4;100;200foobar""\033[4;100;200tfoobar"Насколько я понимаю, в этом случае возвращаемое значение (r) будет установлено равным 2 во всех трёх случаях. Однако действительны только входные данные (1), тогда как входные данные (2) и (3) недействительны, а входные данные (4) содержат конечные данные.
Каков самый чистый/правильный способ отличить (1) от (2), (3) и от (4), т. е. гарантировать, что вся строка формата успешно сопоставляется со всем вводом?
Меня интересуют ответы на этот вопрос как для sscanf(), так и для fscanf().
В вашем заголовке упоминается scanf, но в вашем примере используется sscanf. То, что представляет собой «весь ввод», для них различается, в том числе, хотите ли вы для scanf считать весь ввод всем потоком до конца файла или только одной целой строкой, введенной пользователем, или чем-то еще. Для sscanf вы можете использовать %n в конце, чтобы получить количество прочитанных символов и сравнить его с длиной строки. Для scanf вы можете использовать getchar позже, чтобы увидеть, является ли следующий символ новой строкой или возвращается ли EOF, по желанию, и, при желании, использовать ungetc, чтобы вернуть символ обратно.
Итак, отредактируйте вопрос, чтобы уточнить, запрашиваете ли вы sscanf из строки или scanf из потока и что вы считаете «полным вводом».
@EricPostpischil Второе слово вопроса указывает на то, что они спрашивают о семействе scanf, а не об одной конкретной функции.
@Barmar: (a) Семья *scanf обсуждается в качестве справочной/вводной информации. Здесь не говорится, что они спрашивают обо всей семье. (б) Для sscanf и scanf проблема остается иной. Вопрос следует сузить. В самом списке ОП необходимо уточнить, хотят ли они спросить об одном, о другом или вообще об обоих.
Как упомянул @Someprogrammerdude, *scanf — неправильный инструмент. Но, возможно, вы можете просто заменить t в конце строки формата на %c, а затем проверить возвращаемое значение и убедиться, что оно скопировало t в цель. (Тогда окончание строки 200toobar также будет совпадать, поэтому вам нужно добавить больше логики, и в конце концов вы все равно перестанете использовать scanf.)
@EricPostpischil Хороший улов. Мой непосредственный вопрос касался sscanf(), но мне также был бы интересен ответ на аналогичный вопрос для fscanf().





«Самый чистый/правильный способ отличить» такие входные данные — избегать семейства функций 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)?
Спасибо, я лишь смутно знал о %n и не думал использовать его таким образом. Однако меня также интересует часть вопроса «весь ввод».
@Barmar: это зависит от того, чего ты хочешь. Если t отсутствует, %n не будет обрабатываться, поэтому для него останется 0. Если вы не хотите, чтобы с sscanf() и др. ничего не оставалось, то n != strlen(input) проверит, что все символы в строке обработаны. Конечно, вы также можете использовать if (input[n] != '\0'). С файловыми функциями scanf() вы, вероятно, не захотите выполнять такую проверку длины; после сканирования в файле могло быть больше данных.
Это то, чего ОП говорит, что они хотят. Предполагается, что пример №3 не удался, потому что в конце входных данных стоит foobar.
Ах, кажется, я неправильно понял, потому что не увидел t в их строке формата.
@Barmar Тоже хороший улов, меня также интересует обнаружение конечных данных (согласно заголовку). Я отредактировал вопрос и этот ответ, чтобы добавить свое понимание эквивалента fscanf().
@intelfx: Я откатил ваши изменения, потому что они отменили мои изменения, направленные на те же проблемы. Я не планирую снова редактировать какое-то время, поэтому, если вы все еще хотите включить некоторую часть своего редактирования в мой текущий ответ, вы можете сделать это безопасно.
@JonathanLeffler Не могли бы вы уточнить, что именно вы подразумеваете под «проверкой конечных пробелов»?
@intelfx: см. связанный вопрос (о концевых пробелах). Помещать пробел, табуляцию или новую строку в конце scanf() — в отличие от sscanf() — строки формата — плохая идея (особенно, если ввод поступает от пользователя, печатающего на терминале). С sscanf() это менее проблематично, но все равно не то, что обычно хочется. Семейство функций scanf() сложно использовать правильно — есть некоторая мудрость избегать их, как предлагается в комментариях к вопросу. См. также Руководство для начинающих по использованию scanf().
@JonathanLeffler Я прочитал связанный вопрос, прежде чем задать. Вопрос остается в силе. Понятно, что если мы говорим о «всем вводе», то мы не имеем дело с интерактивным вводом.
sscanf(и его братья и сестры) — очень простой инструмент для анализа и проверки строк. Если вам нужна более продвинутая проверка, вам нужно создать собственный синтаксический анализатор для анализа конкретной строки.