Entrada e Saída

Durante uma competição de programação é uma grande vantagem não perder tempo pensando em como ler a entrada ou escrever a saída. O competidor deve dominar o uso das funções de entrada e saída de forma a manipular os tipos de dados mais comuns sem maiores dificuldades.

Entrada

Antes de mais nada é importante ler bem a descrição da entrada para não ter dúvidas de seu formato. Deve-se verificar se é dado o número de casos de teste ou se os casos de teste terminam com um caso de valor especial ou com o fim do arquivo (EOF).

Para reforçar a intuição sobre a leitura, imagine que a entrada seja um vetor enorme onde há um ponteiro que indica o ponto a partir do qual o próximo item deve ser lido. Ao se solicitar a leitura de um item, dependendo do caso, esse ponteiro pode se mover até chegar ao primeiro caractere do item antes de começar a leitura propriamente dita. Após a leitura esse ponteiro sempre estará apontando para o caractere imediatamente após o último caractere do item.

Funções

Assumiremos o uso da entrada padrão para leitura. Se for necessário ler de um arquivo, utilize a função freopen("arquivo", "r", stdin) e a partir daí pode-se realizar as leituras como se fossem da entrada padrão.

Em C a principal função para entrada é a scanf (em <stdio.h>) e em C++ temos a stream cin (em <iostream>). Em geral utilizar cin é mais simples e enxuto, pois não precisamos dizer o tipo que estamos lendo, no entanto dependendo do compilador e da otimização, cin pode ser muito ineficiente em problemas com limite apertado de tempo. Neste caso e quando a entrada for meio complicada é preferível utilizar a função scanf, que é um recurso mais eficiente e poderoso.

std::cin

Para ler um item, basta fazer cin >> v; onde v é a variável a ser lida. O tipo da variável define como será a leitura. Pode-se agrupar variáveis: cin >> v1 >> v2;, onde v1 é lida antes de v2. Algumas vezes é necessário utilizar estruturas como v = cin.get(). Para testar fim de arquivo utiliza-se cin.eof(), que retorna verdadeiro ou falso.

scanf()

Para ler um item deve-se passar uma string que represanta o formato da entrada e os endereços das variáveis de destino. Essa string do formato pode possuir caracteres normais que se espera na entrada e combinações especiais usando o caractere % para representar os valores a serem lidos. Por exemplo, scanf("%c%d", &l, &i) lê um caractere (%c) seguido de um número (%d) e guarda nas variáveis l ei, que devem ser dos tipos char e int, respectivamente. As expressões dos dados são sempre um % seguido de uma letra que indica o tipo a ser lido. Entre esses dois pode haver um número que indica o máximo de caracteres a serem usados para montar o valor a ser lido. Dessa forma scanf("%3s%4d", s, &i) lê uma string (%s) de no máximo 3 caracteres e um número com no máximo 4 dígitos (incluindo o sinal) e guarda em s e i, respectivamente. Pode-se colocar um * logo após o % para indicar que o valor a ser lido deve ser simplesmente descartado e, dessa forma, não se deve colocar uma variável para esse valor. Por exemplo, scanf("%d%*c%d", &i1, &i2) lê um número e guarda em i1, lê um caractere e descarta e lê outro número e guarda em i2. Um espaço na string de formatação significa que a função deve saltar para o próximo caracter não branco. Caso queira saltar apenas um espaço, utilize %*c. O valor de retorno da função scanf é o número de elementos lidos com sucesso. A função pára no primeira falha. Pode-se usar isso para testes de fim de arquivo, por exemplo: while(scanf("%d%d",&n,&m)==2). Existe um truque bem que pode ser útil que consiste em colocar um %n na string de formatação que faz com que a função coloque na variável correspondente quantos caracteres foram lidos até aquele ponto. Isso facilita o cálculo do tamanho dos itens lidos.

Tipos

Veremos a seguir como ler os principais tipos de dados.

Inteiros

scanf("%d", &i); 
cin >> i;

Onde i é uma variável inteira. Ambas as funções lêem um inteiro na notação decimal e pulam os espaços em branco da posição atual do ponteiro até o próximo caractere não branco antes de tentar a leitura. Assim se os próximos caracteres da entrada forem: '_','_','\t','2','5','g','f',..., onde '_' representa um espaço em branco, após a leitura a variável i conterá o valor 23 e os próximos caracteres da entrada serão: 'g','f',.... Para se ler um long int em C deve-se usar scanf("%ld", &i). Em C++ com cin basta que a variável seja um long int. Ambas as funções lêem dígitos para formar o número enquanto ele for válido. Se for necessário especificar o número máximo de dígitos, coloque-o após o %. Por exemplo, se os próximos caracteres da entrada forem: '7','1','0','2','5','g'..., a chamada scanf("%d", &i) colocaria o valor 71025 na variável i, sendo 'g' o próximo caracter a ser lido, enquanto scanf("%2d", &i), colocaria o valor 71, sendo '0' o próximo caractere a ser lido. Sinais (+ e -) entram na contagem desse campo.

Caracteres

scanf("%c", &c);  // Lê o próximo caractere
scanf("%13c", s);  // Lê 13 caracteres guardando-os no vetor s
scanf(" %c", &c);  // Pula caracteres brancos e lê o caractere seguinte
c = cin.get();  // Lê o próximo caractere
cin.get(c);   // Lê o próximo caractere
cin >> c;   // Pula caracteres brancos e lê o caractere seguinte

Onde c é um variável do tipo caractere. A expressão scanf("%13c", s) lê 13 caracteres para um vetor de char mas não coloca o '\0' no final! Pode-se substituir o 13 por qualquer outro inteiro positivo. A expressão cin.get() retorna um inteiro, sendo um cast do caractere lido ou EOF (que é um int) se fim de arquivo. A expressão cin.get(char&) guarda o caractere lido na variável dada e retorna uma referência ao cin, se foi lido normalmente, ou NULL = 0 = false se fim de arquivo. Dessa forma é possível fazer cin.get(c1).get(c2) ou if(cin.get(c)), por exemplo.

Linhas

gets(s);  // Lê uma linha excluindo o '\n', descarta o '\n' e coloca '\0' no final
fgets(s,tam,stdin); // Lê uma linha incluindo o '\n' e coloca '\0' no final
cin.getline(s,tam); // Lê uma linha excluindo o '\n', descarta o '\n' e coloca '\0' no final
cin.get(s,tam); // Lê uma linha excluindo o '\n', mantém o '\n' no buffer e coloca '\0' no final

A variável s deve ser um vetor de caracteres ou, para a leitura com cin, do tipo string. Repare que o método cin.get é sobrecarregado. As chamadas cin.getline e cin.get recebem como parâmetro o destino da linha e o tamanho máximo que ela pode ter menos 1 (reservado para o '\0'). Pode-se também, por exemplo, usar cin.getline(s,10000,'$') para ler da entrada até chegar a um caractere '$', descartando-o, ou até ler 10000 caracteres, o que acontecer primeiro. Manter o '\n' no buffer como faz cin.get(s,tam,'\n') normalmente é ruim, pois uma chamada seguinte do tipo cin.get(c) lerá o '\n' deixado no buffer, e não o primeiro caractere da linha seguinte. Da mesma forma colocar o '\n' no fim da linha como faz o fgets costuma ser um incômodo. Dessa forma é recomendável utilizar gets ou cin.getline nas competições de programação. Vale a pena notar que a função gets não é recomendável para o uso em programas regulares, pois uma entrada grande pode ultrapassar o espaço alocado para a linha e não há como limitar isso. No entanto isso não é um problema no nosso caso pois as entradas são sempre "bem comportadas".

Strings

scanf("%s", s) // Lê uma palavra incluindo brancos iniciais

scanf(" %s", s) // Lê uma palavra pulando brancos iniciais

cin >> s // Lê uma palavra pulando brancos iniciais

A variável s deve ser um vetor de caracteres ou, para a leitura com cin, do tipo string. Uma "palavra" seria um conjunto de caracteres não brancos consecutivos limitados por caracteres brancos em volta. No caso da leitura com scanf("%s", s) são incluídos brancos no início. Por exemplo, se no buffer de entrada temos: '_','_','t','e','s','t','e','\n', onde '_' representa espaço, a chamada scanf("%s", s) guardará em s a string "__teste", enquanto as chamadas scanf(" %s", s) e cin >> s guardarão em s a string "teste". Note que as três chamadas nunca retornam string vazia, a não ser em fim de arquivo.