#c #scanf
#c #сканф
Вопрос:
Я писал программу на языке Си для чтения содержимого файла. Код выглядит следующим образом:
#include<stdio.h>
void main()
{
char line[90];
while(scanf("y[^n]n",line)==1)
printf("%s",line);
}
Приведенный выше код считывает содержимое файла и отображает его на экране.
Но
while(scanf("y[^n]",line)==1) and while(scanf("y[^n]s",line)==1) or while(scanf("y[^n]sn",line)==1)
не работает.(Они отображают только первую строку)
Кто-нибудь может объяснить?
Комментарии:
1. Первый предназначен для приема ввода типа ‘string’
2. @шекхар суман… все они принимают строки
3.
y[^n]
является строкой формата, не нуждается в последующей фиксацииs
. Вторая новая строкаy[^n]n
буквально переходит на следующую строку4. Я думаю, что мини-регулярное
[^n]
выражение заменяет спецификаторs
формата. Остальные будут совпадать, если у вас есть литерал s в конце строки.
Ответ №1:
В формате "y[^n]"
указаны все символы максимум до 79 символов, которые не 'n'
являются.
Когда вы используете,
scanf("y[^n]s",line)
возвращаемое значение не должно быть 1, поскольку спецификатор s
формата in ожидает литерал s
после завершения чтения всех символов, которые не 'n'
являются. Другими словами, scanf
сообщает о сбое.
Когда вы используете,
scanf("y[^n]n",line)
это удается, потому что он находит литерал 'n'
в конце.
Разница между
scanf("y[^n]",line)
и
scanf("y[^n]n",line)
заключается в том, что 'n'
в первом случае он остается во входном потоке, в то время как во втором случае он потребляется. Во втором случае не только 'n'
используется, но и любая последовательность пробелов, начинающихся с the 'n'
, также используется (спасибо @MattMcNabb за дополнительные разъяснения).
Если вы хотите scanf
потреблять только 'n'
то, что нужно, используйте:
scanf("y[^n]%*c",line)
Комментарии:
1. Обратите внимание
n
, что за пределами квадратных скобок соответствует любой последовательности пробелов , а не только одной новой строке. Эта версия также будет использовать любые начальные пробелы в следующей строке.2. «возвращаемое значение не должно быть 1» вводит в заблуждение. При
scanf("y[^n]s",line)
этом, если отсканировано от 1 до 79char
line
,scanf()
будет возвращено 1. Совпадение или's'
несоответствие литерала не влияет наscanf()
результат.3. Аналогично
scanf("y[^n]n",line)
возвращает 1, если он сканирует что-либоline
. Нахождение (или не нахождение) следующего"n"' does not change the return value of
scanf(). The
» n»‘ влияет на использование пробелов после сканированияline
.4. Уверен, что вы хотите
scanf("y[^n]*c",line)
—>scanf("y[^n]%*c",line)
5. Спасибо, отличные глаза. Исправил ответ. Спасибо, что дали мне знать.
Ответ №2:
Поскольку сам формат один и тот же, ключом к решению этой проблемы, как вы, конечно, можете сказать, является конечный символ после строки формата (или отсутствие этого символа)
- Когда вы ставите
'n'
в конце, вы указываетеscanf
прочитать и проигнорировать символ конца строки после прочтения строки. Это работает нормально, потому'n'
что это то, что завершает строку в соответствии с вашим форматом (т.е.y[^n]
). - Когда вы ставите знак
's'
в конце, вы сообщаетеscanf
, что ожидаете увидеть литералs
после строки, которая заканчивается на'n'
. Это ошибка, потомуscanf
что будет считываться строка до'n'
символа* и исключая его. - Когда вы не ставите
'n'
в конце, первое чтение будет успешным, но все последующие будут натыкаться на'n'
, и сразу прекращать чтение.
Обратите внимание, что работающий код может привести к неожиданным результатам для входных строк, которые превышают 79 символов. Эти строки будут разделены на 79 символов, а их остаток будет представлен как начало следующей строки.
* Существует маловероятная ситуация, когда это чтение будет успешным, а именно, когда чтение завершается из-за достижения предела 79, а затем обнаружения буквы s
на 80-м пробеле.
Комментарии:
1. Я думаю, что проблема с длиной строки может быть исправлена с помощью
while ( scanf("y[^n]%*[^n]", line) ) { getchar(); printf("%sn", line); }
2. @MattMcNabb Ах, я понимаю вашу точку зрения — вы правы, это решило бы проблему с разделением длинных строк. Спасибо!
3. @Matt McNabb Примечание:
scanf("y[^n]%*[^n]"
застревает, если первоеchar
есть'n'
. Он также потребляет несколько'n'
, которые могут / не могут быть в порядке.4. Предлагаю отредактировать: »
'n'
в конце вы указываете scanf читать и игнорировать все последовательные символы конца строки (и любые другие пробелы) после чтения строки»5. @chux Он не использует несколько новых строк, и
n
в конце это было бы плохо, так как тогда он использует другие пробелы в начале следующей строки. Однако вы правы, чтоscanf
возвращает0
, если[]
соответствует пустой строке.
Ответ №3:
Много тонких различий:
1) "y[^n]"
указывает scanf()
сканировать и сохранять от 1 до 79 char
за исключением 'n'
. Если при первой char
попытке сканирования n
ничего не будет сохранено, и сканирование прекратится. В примерах OP возвращается значение 0. Иначе a ''
добавляется к месту назначения line
. Возвращается 1, если больше ничего нет.
2) "y[^n]n"
выполняет 1) и в случае успеха продолжает искать любые последовательные пробелы, а не только 'n'
. Обычно это будет включать a, 'n'
а затем все начальные пробелы следующей строки. Он вернет 1 независимо от того, найдет ли он пробел.
3) "y[^n]s"
выполняет 1 и в случае успеха продолжает искать один s
. Он вернет 1 независимо от того, найдет ли он a s
. Вероятно, это не желание OP — отбросьте 's'
.
4) "y[^n]sn"
выполняет 3) и в случае успеха ведет себя как 2) продолжает искать любые последовательные пробелы, а не только 'n'
. Он вернет 1 независимо от того, найдет ли он пробел.
5) Утверждение 3 while()
довольно сложное, взятое в целом. Он застревает, пытаясь прочитать a 'n'
в начале 1 из 3 scanf()
.
Лучшим подходом, если код необходимо использовать scanf()
для чтения строки, является
while (scanf(" y[^n]%*c", line) ==1)
. Это приведет к удалению начального пробела, а затем к чтению строки до 'n'
, перебрасывая ее 'n'
через "%*c"
.
Лучший подход — это использовать fgets(line, sizeof line, stdin)
.
Ответ №4:
Вполне возможно, что проблема связана с коротким замыканием логических значений. ie. когда компилятор определяет, что у него достаточно информации для оценки всего выражения, ему не нужно проверять другие логические значения (и он не оценивает их).
т. е. false и call_foo() всегда принимают значение false , поэтому call_foo() никогда не вызывается.
Это также может быть связано с тем, что когда вы читаете строку в scanf (первое из 3 логических выражений), другие scanf будут считывать следующие строки, а не те же строки, потому что первый scanf уже извлек данные из буфера.