O número de matrícula dos alunos de uma universidade é composto por quatro números no padrão aaaa.s.cc.iii (onde: aaaa
ano de ingresso;
semestre de ingresso, que pode ser 1 ou 2 ; cc
número do curso, que pode ser qualquer valor; e iii = identificador do discente, também qualquer valor). (a) Defina um tipo (typedef) denominado matricula que seja composto de um registro (struct) contendo em cada campo um dos números que formam o número de matrícula. (b) Crie um programa que peça para o usuário digitar um número de matrícula no formato padronizado e verifique se os números digitados são aceitáveis. Caso o número de matrícula esteja com problemas, exiba mensagens identificando qual (ou quais) dos números digitados apresenta(m) problemas; caso contrário o número de matrícula deve ser atribuído a uma variável do tipo matricula (que você definiu no item a) e deve ser exibido na tela o número de matrícula o usuário, a partir da variável tipo matricula criada.
Passo 1
O enunciado pede duas coisas:
- Definir um tipo (typedef) chamado
matriculaque seja composto de um registro (struct) contendo, em cada campo, um dos números que formam o número de matrícula no formato. - Escrever um programa que peça ao usuário para digitar um número de matrícula no formato
, verifique se os números digitados são aceitáveis e: - se houver problema(s), exiba mensagens indicando qual(is) campo(s) estão inválidos;
- se estiver tudo certo, atribua o valor a uma variável do tipo
matriculae exiba na tela a matrícula a partir dessa variável.
Informações relevantes dadas no enunciado:
- O formato é
, onde: -
= ano de ingresso (quatro dígitos), -
= semestre de ingresso (pode ser ou ), -
= número do curso (o enunciado diz “pode ser qualquer valor” — interpretarei como um número inteiro com uma ou mais casas), -
= identificador do discente (também “qualquer valor” — interpretarei como um número inteiro com uma ou mais casas).
-
- O programa deve verificar a aceitabilidade (validação) de cada campo e informar erros específicos.
Passo 2
Decisão de método (explicando pedagogicamente):
- Para validar uma string no formato
de forma robusta em C, o melhor caminho para um programa de prova é: - Ler a linha inteira como string (com
fgets) para evitar problemas com espaços e quebras de linha. - Separar a string nos quatro tokens usando o caractere
'.'como separador (funçãostrtok), porque a estrutura do formato usa pontos fixos. - Para cada token, verificar que todos os caracteres são dígitos (usar
isdigit) e checar comprimentos esperados: exigir que o token do ano tenha exatamente 4 dígitos, o token do semestre exatamente 1 dígito, e os tokens de curso e identificador tenham pelo menos 1 dígito (como o enunciado permitiu “qualquer valor”). - Converter os tokens para inteiros com
strtol(ouatoi) e, em seguida, validar semanticamente (por exemplo, semestre deve ser 1 ou 2; ano razoável — aqui vamos exigir que seja um número de 4 dígitos entree para evitar entradas estranhas). - Se tudo estiver OK, atribuir aos campos da
structe imprimir a matrícula a partir da struct.
- Ler a linha inteira como string (com
- Por que esse método? Porque é simples, fácil de implementar sem bibliotecas externas e confiável para provas: não depende de expressões regulares, nem de comportamento complexo de
scanf. Além disso, é fácil explicar e reproduzir sem calculadora.
Passo 3
Definição do tipo
matricula (a parte (a) pedida). Explicação do que cada campo representa e por que escolhi esses tipos:- Em C, podemos representar os quatro números como inteiros (
int) dentro de umastruct. Cada campo dastructconterá um dos números:ano,semestre,curso,id. - Usei
intporque são números inteiros e isso facilita operações e comparações (por exemplo, testar sesemestre == 1ou2). Na impressão, formataremosanocom 4 dígitos (%04d) para manter o formato. - Aqui está a definição (este trecho corresponde à resposta do item (a)):
typedef struct {
int ano; /* ano de ingresso (ex.: 2023) */
int semestre; /* semestre de ingresso (1 ou 2) */
int curso; /* número do curso (qualquer inteiro não negativo) */
int id; /* identificador do discente (qualquer inteiro não negativo) */
} matricula;
Passo 4
Como ler e quebrar a string (detalhes passo a passo e mini-aula):
- Ler a linha inteira:
fgets(buffer, sizeof(buffer), stdin);— isso lê até a nova linha e evita overflow se usarmos um buffer com tamanho razoável (ex.: 100). - Remover o
\nfinal (se existir): testar o último caractere e substituir por'\0'. - Separar pelos pontos: usar
strtok(buffer, ".")para obter o primeiro token, depoisstrtok(NULL, ".")para os seguintes. Assim obtemos até 4 tokens; se houver mais ou menos tokens, a entrada não respeita o formato. - Por que não usar
scanf("%d.%d.%d.%d", ...)? Porquescanfaceita espaços e sinais e pode aceitar entradas ambíguas; além disso, se quisermos detectar qual campo tem problema e validar tamanhos (ex.: ano com exatamente 4 dígitos), é muito mais direto tokenizar e inspecionar caractere a caractere.
Passo 5
Validação de cada token (mini-aula detalhada):
- Verificar que cada token exista (ou seja, foram encontrados exatamente 4 tokens).
- Verificar que cada caractere de um token é dígito: iterar sobre o token e usar
isdigit((unsigned char) token[i]). Isso impede que apareçam letras ou sinais. - Regras específicas:
- Token 1 (ano): deve ter comprimento exatamente 4 e todos dígitos. Depois de converter para inteiro, é razoável exigir
para evitar anos com zeros à esquerda inválidos. - Token 2 (semestre): deve ter comprimento exatamente 1, ser dígito e convertido deve ser
ou . - Token 3 (curso): pelo enunciado “qualquer valor”, exigimos pelo menos 1 dígito e que a conversão resulte em inteiro não-negativo. (Você pode exigir tamanho fixo se o professor especificar, mas aqui mantemos flexível.)
- Token 4 (id): idem ao curso — pelo menos 1 dígito e inteiro não-negativo.
- Token 1 (ano): deve ter comprimento exatamente 4 e todos dígitos. Depois de converter para inteiro, é razoável exigir
- Conversão: usar
strtol(token, &endptr, 10)e checar se*endptr == '\0'(garante que toda a string foi convertida). Também é útil checar sinais (não permitimos-), então o primeiro caractere não deve ser'-'. - Se alguma verificação falhar, armazenar uma mensagem de erro específica (por exemplo: “Ano inválido: deve ter 4 dígitos” ou “Semestre inválido: deve ser 1 ou 2”).
Passo 6
Construção do programa e explicação do código principal (linha a linha, com comentários):
- Cabeçalhos necessários:
#include <stdio.h>,#include <stdlib.h>,#include <string.h>,#include <ctype.h>. - Variáveis: buffer de leitura (ex.:
char linha[100];), ponteiros para tokens, flags/variáveis para armazenar erros e números convertidos. - Fluxo:
- Imprimir mensagem pedindo a matrícula.
- Ler com
fgets. - Remover
\n. - Usar
strtokpara separar e obter tokens. - Para cada token, testar comprimento e
isdigit. - Converter com
strtol. - Verificar regras semânticas (semestre
ou , ano no intervalo). - Se tudo válido, criar uma variável
matricula m;e atribuirm.ano = ano_num; m.semestre = sem_num; m.curso = curso_num; m.id = id_num;. - Exibir a matrícula a partir de
musandoprintf("%04d.%d.%d.%d\n", m.ano, m.semestre, m.curso, m.id);.
- Observação sobre impressão:
printf("%04d", m.ano)garante queanoapareça com 4 dígitos (ex.:2023). Os camposcursoeidsão impressos como inteiros; se for necessário preservar zeros à esquerda originais, o struct teria que guardar strings. Aqui, imprimo como inteiros porque o enunciado pediu os números em cada campo.
Passo 7
Código completo do programa (implementação do item (b)). Leia com atenção os comentários — eu expliquei cada parte acima e, na prova, você pode reproduzir este padrão:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
typedef struct {
int ano;
int semestre;
int curso;
int id;
} matricula;
int so_digitos(const char *s) {
if (s == NULL || *s == '\0') return 0;
for (size_t i = 0; s[i] != '\0'; ++i) {
if (!isdigit((unsigned char)s[i])) return 0;
}
return 1;
}
int main(void) {
char linha[200];
char *token;
char *rest;
char *tokens[4] = {NULL, NULL, NULL, NULL};
int i = 0;
int erros = 0;
printf("Digite a matrícula no formato aaaa.s.cc.iii : ");
if (!fgets(linha, sizeof(linha), stdin)) {
printf("Erro ao ler entrada.\n");
return 1;
}
/* remover newline */
rest = strchr(linha, '\n');
if (rest) *rest = '\0';
/* tokenizar por '.' */
token = strtok(linha, ".");
while (token != NULL && i < 4) {
tokens[i++] = token;
token = strtok(NULL, ".");
}
if (i != 4) {
printf("Formato inválido: deve conter exatamente 4 campos separados por '.'\n");
return 1;
}
/* Validar ano (tokens[0]) */
if (strlen(tokens[0]) != 4 || !so_digitos(tokens[0])) {
printf("Erro: 'ano' inválido. Deve ter exatamente 4 dígitos.\n");
erros++;
}
/* Validar semestre (tokens[1]) */
if (strlen(tokens[1]) != 1 || !so_digitos(tokens[1])) {
printf("Erro: 'semestre' inválido. Deve ser 1 dígito (1 ou 2).\n");
erros++;
}
/* Validar curso (tokens[2]) */
if (strlen(tokens[2]) < 1 || !so_digitos(tokens[2])) {
printf("Erro: 'curso' inválido. Deve conter pelo menos 1 dígito e apenas dígitos.\n");
erros++;
}
/* Validar id (tokens[3]) */
if (strlen(tokens[3]) < 1 || !so_digitos(tokens[3])) {
printf("Erro: 'identificador' inválido. Deve conter pelo menos 1 dígito e apenas dígitos.\n");
erros++;
}
if (erros > 0) {
/* já imprimimos mensagens específicas */
return 1;
}
/* Converter para inteiros com strtol e validar semanticamente */
char *endptr;
long ano_l = strtol(tokens[0], &endptr, 10);
if (*endptr != '\0' || ano_l < 1000 || ano_l > 9999) {
printf("Erro: 'ano' semanticamente inválido (espera-se 4 dígitos, ex: 2023).\n");
return 1;
}
long semestre_l = strtol(tokens[1], &endptr, 10);
if (*endptr != '\0' || (semestre_l != 1 && semestre_l != 2)) {
printf("Erro: 'semestre' inválido. Só são permitidos 1 ou 2.\n");
return 1;
}
long curso_l = strtol(tokens[2], &endptr, 10);
if (*endptr != '\0' || curso_l < 0) {
printf("Erro: 'curso' inválido.\n");
return 1;
}
long id_l = strtol(tokens[3], &endptr, 10);
if (*endptr != '\0' || id_l < 0) {
printf("Erro: 'identificador' inválido.\n");
return 1;
}
/* Atribuir à struct matricula e imprimir */
matricula m;
m.ano = (int) ano_l;
m.semestre = (int) semestre_l;
m.curso = (int) curso_l;
m.id = (int) id_l;
printf("Matrícula válida atribuída à variável do tipo 'matricula'.\n");
printf("Matrícula (a partir da struct): %04d.%d.%d.%d\n", m.ano, m.semestre, m.curso, m.id);
return 0;
}
Passo 8
Dicas e observações para a prova (como usar esse método rapidamente):
- Na prova, escreva primeiro a
struct— isso garante que você cumpra o item (a). - Depois, no
main, implemente leitura comfgetse tokenização comstrtok. Esses três passos (ler, separar, validar) são suficientes para resolver a maior parte dos exercícios parecidos. - Se o enunciado da prova exigir manter zeros à esquerda em
ccouiii, guarde esses campos como strings nastructem vez de inteiros (ex.:char curso[10]; char id[10];) para depois imprimi-los exatamente como digitados. - Sempre verifique e explique cada validação — o corretor costuma valorizar mensagens de erro claras.
Resposta
(a)
typedef struct {
int ano; /* ano de ingresso (ex.: 2023) */
int semestre; /* semestre de ingresso (1 ou 2) */
int curso; /* número do curso (qualquer inteiro não negativo) */
int id; /* identificador do discente (qualquer inteiro não negativo) */
} matricula;
(b)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
typedef struct {
int ano;
int semestre;
int curso;
int id;
} matricula;
int so_digitos(const char *s) {
if (s == NULL || *s == '\0') return 0;
for (size_t i = 0; s[i] != '\0'; ++i) {
if (!isdigit((unsigned char)s[i])) return 0;
}
return 1;
}
int main(void) {
char linha[200];
char *token;
char *rest;
char *tokens[4] = {NULL, NULL, NULL, NULL};
int i = 0;
int erros = 0;
printf("Digite a matrícula no formato aaaa.s.cc.iii : ");
if (!fgets(linha, sizeof(linha), stdin)) {
printf("Erro ao ler entrada.\n");
return 1;
}
/* remover newline */
rest = strchr(linha, '\n');
if (rest) *rest = '\0';
/* tokenizar por '.' */
token = strtok(linha, ".");
while (token != NULL && i < 4) {
tokens[i++] = token;
token = strtok(NULL, ".");
}
if (i != 4) {
printf("Formato inválido: deve conter exatamente 4 campos separados por '.'\n");
return 1;
}
/* Validar ano (tokens[0]) */
if (strlen(tokens[0]) != 4 || !so_digitos(tokens[0])) {
printf("Erro: 'ano' inválido. Deve ter exatamente 4 dígitos.\n");
erros++;
}
/* Validar semestre (tokens[1]) */
if (strlen(tokens[1]) != 1 || !so_digitos(tokens[1])) {
printf("Erro: 'semestre' inválido. Deve ser 1 dígito (1 ou 2).\n");
erros++;
}
/* Validar curso (tokens[2]) */
if (strlen(tokens[2]) < 1 || !so_digitos(tokens[2])) {
printf("Erro: 'curso' inválido. Deve conter pelo menos 1 dígito e apenas dígitos.\n");
erros++;
}
/* Validar id (tokens[3]) */
if (strlen(tokens[3]) < 1 || !so_digitos(tokens[3])) {
printf("Erro: 'identificador' inválido. Deve conter pelo menos 1 dígito e apenas dígitos.\n");
erros++;
}
if (erros > 0) {
/* já imprimimos mensagens específicas */
return 1;
}
/* Converter para inteiros com strtol e validar semanticamente */
char *endptr;
long ano_l = strtol(tokens[0], &endptr, 10);
if (*endptr != '\0' || ano_l < 1000 || ano_l > 9999) {
printf("Erro: 'ano' semanticamente inválido (espera-se 4 dígitos, ex: 2023).\n");
return 1;
}
long semestre_l = strtol(tokens[1], &endptr, 10);
if (*endptr != '\0' || (semestre_l != 1 && semestre_l != 2)) {
printf("Erro: 'semestre' inválido. Só são permitidos 1 ou 2.\n");
return 1;
}
long curso_l = strtol(tokens[2], &endptr, 10);
if (*endptr != '\0' || curso_l < 0) {
printf("Erro: 'curso' inválido.\n");
return 1;
}
long id_l = strtol(tokens[3], &endptr, 10);
if (*endptr != '\0' || id_l < 0) {
printf("Erro: 'identificador' inválido.\n");
return 1;
}
/* Atribuir à struct matricula e imprimir */
matricula m;
m.ano = (int) ano_l;
m.semestre = (int) semestre_l;
m.curso = (int) curso_l;
m.id = (int) id_l;
printf("Matrícula válida atribuída à variável do tipo 'matricula'.\n");
printf("Matrícula (a partir da struct): %04d.%d.%d.%d\n", m.ano, m.semestre, m.curso, m.id);
return 0;
}