2016-12-19 4 views
7

По названию Я пытаюсь понять точное поведение Ctrl + D/Ctrl + Z в цикле в то время как с get (который я должен использовать). Код я тестирование заключается в следующем:различное поведение Ctrl-D (Unix) и Ctrl-Z (Windows)

#include <stdio.h> 
#include <stdlib.h> 

int main() 
{ 

    char str[80]; 

    while(printf("Insert string: ") && gets(str) != NULL) { 

     puts(str); 
    } 

    return 0; 
} 

Если мой вклад просто Ctrl + D (или Ctrl + Z на Windows) gets возвращает NULL и программа завершается корректно. Неясная ситуация - когда я вставляю что-то вроде house^D^D (Unix) или house^Z^Z\n (Windows).

  1. В первом случае моя интерпретация является getchar (или что-то подобное внутри функции gets) ожидает для чтения(), чтобы получить вход, первый Ctrl + D переполнить буфер, который не является пустым (следовательно, не EOF), то второй раз read() называется EOF.
  2. Во втором случае я заметил, что первые Ctrl + Z вставляется в буфер, а все, что следует, просто игнорируется. Следовательно, мое понимание - это первый вызов read(), вставленный house^Z, и отброшенное все остальное, возвращающее 5 (количество прочитанных символов). (Я говорю 5, потому что иначе я думаю, что простой Ctrl + Z должен возвращать 1 без запуска EOF). Затем программа ждет больше ввода от пользователя, следовательно, второй вызов read().

Я хотел бы знать, что я получу правильно и не так, как он работает, и какая часть его просто зависит от реализации, если таковая имеется.


Кроме того, я заметил, что в обоих Unix и Windows, даже после того, как EOF активизирован, он, кажется, возвращаются к ложным в следующем gets() вызов, и я не понимаю, почему это происходит и в какой строке кода.

Я бы очень признателен за любую помощь.


(12/20/2016) я сильно отредактировал мой вопрос, чтобы избежать путаницы

+0

это выражение: '|| ! feof (stdin)) 'не имеет никакого полезного эффекта. Главным образом потому, что функция: 'feof()' проверяет, пытался ли код прочесть предыдущий EOF, и это выражение проверяется только при обнаружении EOF, поэтому всегда будет false. Поскольку эта функция НЕ делает то, что ожидается (обычно), настоятельно рекомендуем никогда не использовать ее. – user3629249

+0

это выражение: '* curr ++' может/будет проблемой. Проблема связана с приоритетом операторов в C. Предложите: '* curr = (char) c; curr ++; ' – user3629249

+0

Я использовал! feof (stdin), чтобы проверить, не возникла ли ошибка чтения вместо eof. Я просто попытался подражать функции gets (...), чтобы лучше изолировать источник моего сомнения (то есть getchar()) –

ответ

1

клавиш CTRL-D и CTRL-Z «конец файла» показатели служат той же цели на Unix и Windows, систем, но реализованы совершенно по-разному.

В системах Unix (включая клоны Unix, такие как Linux) CTRL-D, официально описанный как символ конца файла, на самом деле является символом-разделителем. Это почти то же самое, что и символ конца строки (обычно возврат каретки или CTRL-M), который используется для разграничения строк. Оба символа сообщают операционной системе, что линия ввода завершена, и чтобы она была доступна программе. Единственное отличие состоит в том, что с символом конца строки символ конца строки (CTRL-J) вставляется в конец входного буфера, чтобы отметить конец строки, в то время как символ конца файла ничего не вставлен ,

Это означает, что при вводе house^D^D на Unix системный вызов сначала возвращает буфер длиной 5 с 5 символами house. Когда read вызывается снова, чтобы получить больше ввода, он затем возвращает буфер длиной 0 без символов в нем. Поскольку нулевая длина, считанная в нормальном файле, указывает, что конец файла был достигнут, библиотечная функция gets также интерпретирует это как конец файла и прекращает чтение ввода. Однако, поскольку он заполняет буфер 5 символами, он не возвращает NULL, чтобы указать, что он достиг конца файла. И поскольку на самом деле он фактически не дошел до конца файла, поскольку терминальные устройства не являются фактически файлами, дальнейшие вызовы gets после этого будут делать дальнейшие звонки read, которые возвратят любые последующие символы, которые пользователь набирает.

В Windows CTRL-Z обрабатывается по-разному. Самое большое различие заключается в том, что он не обрабатывается специально операционной системой вообще. Когда вы вводите house^Z^Z^M на Windows, только символ возврата каретки получает специальную обработку. Как и в Unix, возврат каретки делает типизированную линию доступной для программы, хотя в этом случае возврат каретки и подача строки добавляются в буфер для маркировки конца строки. Поэтому результатом является то, что функция ReadFile возвращает 9-байтовый буфер с 9 символами house^Z^Z^M^J.

Это на самом деле сама программа, в частности библиотека времени выполнения C, которая специально относится к CTRL-Z. В случае библиотеки времени выполнения Microsoft C, когда он видит символ CTRL-Z в буфере, возвращаемом ReadFile, он рассматривает его как маркер конца файла и игнорирует все остальное после него. Используя пример в предыдущем абзаце, gets заканчивает вызов ReadFile, чтобы получить больше ввода, потому что тот факт, что его видел символ CTRL-Z, не запоминается при чтении с консоли (или на другом устройстве), и он еще не видел конца -of-line (который был проигнорирован). Если вы снова нажмете клавишу Enter, gets вернется с буфером, заполненным 7 байтами house^Z\0 (добавив 0 байт, чтобы указать конец строки). По умолчанию он делает то же самое при чтении из обычных файлов, если в файле появляется символ CTRL-Z, он и все после его игнорирования. Это для обратной совместимости с CP/M, которая поддерживает только файлы длиной, которые были кратными 128, и использовала CTRL-Z, чтобы отметить, где должны были закончить текстовые файлы.

Обратите внимание, что описанное выше поведение Unix и Windows - это обычная обработка по умолчанию для ввода пользователем. Обработка Unix для CTRL-D происходит только при чтении с терминала в каноническом режиме, и можно изменить символ «конец файла» на что-то другое. В Windows операционная система никогда не обрабатывает CTRL-Z специально, но независимо от того, выполняется ли библиотека выполнения C или нет, зависит от того, читается ли поток FILE в текстовом или двоичном режиме. Вот почему в переносных программах вы должны всегда включать символ b в строку режима при открытии двоичных файлов (например, fopen("foo.gif", "rb")).

+0

Прежде всего, я хочу поблагодарить вас за углубленный анализ различных реализаций. Однако я заметил, что CTRL-Z обрабатывается несколько иначе в Windows, в частности, если я набираю 'house^Z^Z^Z \ n', игнорируются только второй и третий«^Z ». –

+0

Это означает, что 'puts (str)' выводит на экран строку «house^Z» (где^Z печатается как символ не ASCII). Моя интерпретация заключается в том, что вызов 'getchar()' внутри 'gets' возвращает EOF, только если' ReadFile': a) не имеет символа для чтения (конец файла); b) встречает '^ Z' в качестве первого символа в буфере , Если '^ Z' находится посреди буфера, он просто запускает закрытие буфера, но затем' getchar() 'все еще возвращает«^Z ». PS: Я имею в виду реализацию 'get' как' getS' выше 'user3856986'. –

+0

На самом деле все после первого^Z, которое игнорируется, по крайней мере, с реализацией библиотеки времени выполнения Microsoft C. Возможны другие реализации, например, один «user3856986», хотя это не соответствует, поэтому я не уверен, действительно ли он используется в любой реальной библиотеке времени выполнения C, не говоря уже о Windows. Как точно обрабатывается^Z, будет зависеть реализация и, возможно, другие детали, подобные режиму буферизации потока. –