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.