Conserver chaque ligne du reçu

CBeginner
Pratiquer maintenant

Introduction

Sunny Snacks a besoin d'un reçu qui conserve chaque achat, même lorsqu'un client rachète le même en-cas. Vous exécuterez d'abord un exemple à un article, puis compléterez un programme C qui stocke des achats séparés et imprime chaque ligne, son prix unitaire, son sous-total et le total complet.

Vous devez connaître les variables, conditions, boucles, tableaux et fonctions en C, ainsi que la saisie de base dans un terminal et les chemins. Ce laboratoire présente les structures et les détails de sortie de texte nécessaires. L'environnement du navigateur fournit déjà un compilateur et le terminal Workspace. Les montants sont des centimes entiers synthétiques, pas un système de paiement ou de comptabilité. Le temps de lecture et de pratique est une estimation de l'auteur ; il n'a pas été mesuré auprès de débutants.

Lire un reçu à un article

Dans cette étape, vous exécuterez un petit exemple complet et remplacerez un achat d'un article par un achat de deux articles.

Ouvrez l'onglet Workspace. Le terminal démarre dans /home/labex/project/receipts. cd change de répertoire ; pwd affiche le répertoire actuel et ls liste les fichiers. Ces commandes permettent aussi de corriger une erreur de chemin ou de nom.

cd /home/labex/project/receipts
pwd
ls

sample.c est un exemple complet à un article. till.c est le programme incomplet à plusieurs articles de l'étape suivante. Le catalogue, l'analyseur et le pilote de saisie sont des fichiers préparés ; ne les modifiez pas. cat affiche un fichier texte sans le modifier :

cat sample.c

La variable quantity de l'exemple vaut initialement 1. unit_cents vaut 125 : une unité monétaire et 25 centimes. La multiplication calcule un sous-total en centimes. Conservez cette unité pendant tout le calcul. La division entière par 100 donne la partie entière ; % 100 donne le reste. Dans printf, %d affiche un entier, %s une chaîne C terminée, %02d complète la partie fractionnaire à deux chiffres et \n termine la ligne. Ainsi, 250 centimes s'affichent comme 2.50, sans calcul en virgule flottante.

Compilez le programme. gcc est le compilateur C ; -std=c11 sélectionne C11, -Wall -Wextra active des avertissements et -o sample nomme l'exécutable. ./sample exécute ce fichier depuis le répertoire actuel.

gcc -std=c11 -Wall -Wextra sample.c -o sample
./sample

Le résultat normal complet est :

SUNNY SNACKS
ID | ITEM | QTY | UNIT | SUBTOTAL
101 | Oat bar | 1 | 1.25 | 1.25
TOTAL | 1.25

ID signifie identifiant du produit : 101 désigne cette entrée du catalogue. UNIT est le prix d'un article ; SUBTOTAL est la quantité de cet achat multipliée par son prix unitaire. Les barres verticales séparent les colonnes lisibles. Le TOTAL final additionne les sous-totaux de tous les achats ; ici, il n'y en a qu'un.

Changez la quantité en 2. La commande sed suivante remplace l'ancien texte exact par le nouveau ; -i enregistre la modification. Les apostrophes gardent le remplacement dans un seul argument du shell.

sed -i 's/int quantity = 1;/int quantity = 2;/' sample.c

Recompilez après modification : changer le code source ne met pas automatiquement à jour un ancien exécutable. L'opérateur du shell > redirige la sortie standard vers un fichier. Il l'ouvre ou le tronque avant le démarrage du programme C ; utilisez donc le nouveau nom de sortie indiqué.

gcc -std=c11 -Wall -Wextra sample.c -o sample
./sample > sample-receipt.txt
cat sample-receipt.txt

La quantité vaut maintenant 2 et le sous-total comme le total valent 2.50 ; le prix unitaire reste 1.25. Vérifiez à la main : 125 × 2 = 250 centimes. Conservez sample.c, l'exécutable sample et sample-receipt.txt.

Si un fichier manque, revenez avec le cd appris et vérifiez son nom avec ls. Si le compilateur signale une erreur, corrigez la ligne indiquée et recompilez avant l'exécution ; un ancien exécutable peut subsister après une compilation échouée.

Conserver et imprimer chaque achat

Dans cette étape, vous conserverez les achats comme des enregistrements séparés et produirez un reçu complet de trois lignes.

Commencez dans Workspace, dans /home/labex/project/receipts. Lisez le catalogue et le programme incomplet :

cd /home/labex/project/receipts
cat catalog.h
cat till.c

Le fichier d'en-tête catalog.h fournit le nom du comptoir, le maximum de six achats, la quantité maximale de 20 et quatre entrées du catalogue. #include "catalog.h" incorpore ces définitions lors de la compilation. #define nomme une constante ou un initialiseur ; CATALOG_ROWS fournit les lignes qui initialisent le tableau des produits. Une barre oblique inversée (\) en fin de ligne d'une définition du catalogue prolonge le même #define sur la ligne physique suivante. Gardez catalog.h, parse.h, driver.h et read-line.h inchangés dans ce laboratoire.

Une structure regroupe des champs liés dans un enregistrement. Product regroupe un id entier, un tableau de 32 caractères pour le nom et un prix entier cents. typedef permet d'utiliser Product comme nom court de ce type d'enregistrement. Une chaîne C se termine par le caractère de valeur zéro \0 ; le tableau du nom doit donc réserver une place au terminateur en plus des caractères visibles. Les noms fournis tiennent dans ce tableau ; cette tâche ne traite pas des noms arbitraires.

Purchase est un second type d'enregistrement. Il conserve la position du produit dans le tableau et la quantité de cet achat. Dans basket[line_count].quantity, les crochets sélectionnent un achat et le point sélectionne son champ quantité. Les positions commencent à zéro. Le tableau global static et line_count sont initialisés à zéro. sizeof products / sizeof products[0] calcule le nombre d'enregistrements complets de produits dans le tableau.

La boucle fournie find_product recherche par identifiant de produit et renvoie sa position dans le tableau, ou -1 s'il n'existe pas. Les deux produits nommés Water ont des identifiants et prix différents : 103 coûte 75 centimes et 104 est gratuit. Le nom seul n'identifie pas un produit. Le catalogue contient quatre produits, des identifiants de 1 à 999 et des prix de 0 à 9999 centimes. Le même programme doit aussi fonctionner lorsqu'il est recompilé avec un autre catalogue fourni de quatre lignes dans ces limites.

Complétez approve_purchase en vérifiant l'identifiant, la quantité et la capacité restante avant d'écrire dans le tableau. Ensuite seulement, enregistrez la position du produit et la quantité dans basket[line_count], puis incrémentez le compteur. La quantité est comprise entre 1 et 20 ; un prix nul est valide. Ne fusionnez pas les identifiants répétés : un achat ultérieur constitue une autre ligne. Six achats au prix et à la quantité maximaux autorisés totalisent au plus 6 × 20 × 9999 = 1,199,880 centimes, ce qui tient dans un entier signé de 32 bits.

puts affiche une chaîne terminée et ajoute un saut de ligne. printf utilise des marqueurs de format, comme %s pour une chaîne terminée par NUL et %d pour un entier. print_receipt parcourt tous les achats approuvés. Product product = ... copie cet enregistrement du catalogue dans une variable locale. Calculez chaque sous-total entier, ajoutez-le au total et imprimez les lignes complètes dans l'ordre entre le titre et l'en-tête et un unique TOTAL final. Utilisez les cinq champs séparés par des barres verticales ; les espaces autour des champs et les lignes vides peuvent varier, mais un reçu avec des enregistrements supplémentaires ou des champs manquants n'est pas complet.

Remplacez till.c par le code complet suivant. cat > till.c remplace le fichier ; <<'EOF' commence un here-document. Le délimiteur entre apostrophes copie littéralement le texte C jusqu'à la ligne contenant uniquement EOF. Copiez tout le bloc, y compris cette dernière ligne.

cat > till.c <<'EOF'
#include <stdio.h>
#include "catalog.h"
#include "parse.h"

typedef struct {
    int id;
    char name[32];
    int cents;
} Product;

typedef struct {
    int product_index;
    int quantity;
} Purchase;

static const Product products[] = CATALOG_ROWS;
static const int product_count = sizeof products / sizeof products[0];
static Purchase basket[MAX_LINES];
static int line_count;

static int find_product(int id) {
    for (int i = 0; i < product_count; i++)
        if (products[i].id == id) return i;
    return -1;
}

static int approve_purchase(int id, int quantity) {
    int index = find_product(id);
    if (index < 0 || quantity < 1 || quantity > MAX_QUANTITY || line_count >= MAX_LINES)
        return 0;
    basket[line_count].product_index = index;
    basket[line_count].quantity = quantity;
    line_count++;
    return 1;
}

static void print_receipt(void) {
    int total = 0;
    puts(SHOP_TITLE);
    puts("ID | ITEM | QTY | UNIT | SUBTOTAL");
    for (int i = 0; i < line_count; i++) {
        Product product = products[basket[i].product_index];
        int quantity = basket[i].quantity;
        int subtotal = product.cents * quantity;
        total += subtotal;
        printf("%d | %s | %d | %d.%02d | %d.%02d\n",
               product.id, product.name, quantity,
               product.cents / 100, product.cents % 100,
               subtotal / 100, subtotal % 100);
    }
    printf("TOTAL | %d.%02d\n", total / 100, total % 100);
}

#include "driver.h"
EOF

parse.h et driver.h fournissent la boucle de saisie déjà opérationnelle. Le pilote demande un identifiant et une quantité, puis appelle vos fonctions d'approbation et d'impression. Leurs noms et signatures existants sont des points d'intégration ; conservez-les tels quels. Les noms internes des types d'enregistrements et des variables locales ne sont pas l'objet de l'évaluation. Le laboratoire suivant expliquera la validation de la saisie.

Compilez le programme complet :

gcc -std=c11 -Wall -Wextra till.c -o till

Le pilote envoie les invites et messages sur l'erreur standard, qui reste visible dans le terminal. Seul le reçu terminé va sur la sortie standard. Exécutez-le avec un nouveau fichier de sortie :

./till > receipt.txt

Le programme attend une saisie. Tapez chaque valeur du tableau à l'invite correspondante, puis appuyez sur Entrée. Ne les tapez pas comme des commandes du shell. done est la commande exacte de finalisation, en minuscules, et ne fonctionne qu'à l'invite Product ID.

Saisie de Product ID Saisie de Quantity Résultat
101 2 Added line 1
102 1 Added line 2
101 1 Added line 3, en conservant l'achat précédent de 101
done Aucune quantité demandée Terminer le reçu et revenir au shell

Vous voyez toujours les invites et les messages Added line, car le pilote les écrit sur l'erreur standard, un flux séparé. Seul le reçu complet va sur la sortie standard, que > envoie dans receipt.txt. Quand l'invite du shell revient, lisez le reçu réellement enregistré :

cat receipt.txt

Contenu attendu :

SUNNY SNACKS
ID | ITEM | QTY | UNIT | SUBTOTAL
101 | Oat bar | 2 | 1.25 | 2.50
102 | Fruit cup | 1 | 2.30 | 2.30
101 | Oat bar | 1 | 1.25 | 1.25
TOTAL | 6.05

Les deux achats d'Oat bar restent séparés. Additionnez leurs sous-totaux avec celui de Fruit cup : 250 + 230 + 125 = 605 centimes. Un total apparemment correct sans toutes ces lignes ferait perdre les détails de la transaction du client.

Les nombres utilisent des chiffres décimaux, un signe facultatif et des espaces ASCII ou tabulations facultatifs au début et à la fin ; les zéros initiaux sont autorisés. Les identifiants doivent exister dans le catalogue, les quantités être dans 1..20 et chaque ligne contenir au maximum 63 octets avant Entrée. Le rejet d'un identifiant ou d'une quantité abandonne le candidat en attente et revient à Product ID sans modifier les achats approuvés. Le lecteur fourni consomme une ligne trop longue au lieu de la diviser en réponses ultérieures. Au plus six achats sont approuvés. Un panier vide suivi de done produit un reçu vide valide avec un total de 0.00.

Pour annuler au lieu de terminer, appuyez sur Ctrl+D à une invite de saisie vide. Ce signal de fin de fichier, ou EOF pendant une ligne inachevée, annule avec le statut 2 et sans reçu complet. Il n'exporte ni ne conserve la transaction inachevée. La redirection a déjà ouvert le fichier de sortie ; utilisez un autre nom si vous testez une annulation volontaire. Saisir done comme quantité est une quantité textuelle invalide, pas une finalisation.

Si le reçu ne contient que le dernier achat, vérifiez que vous enregistrez dans basket[line_count] et incrémentez le compteur après chaque approbation. Enregistrez, recompilez et exécutez une nouvelle transaction avec les mêmes achats requis. Chaque exécution commence avec un panier vide ; elle ne recharge pas le texte d'un ancien reçu. Conservez till.c, l'exécutable till, les fichiers fournis et le receipt.txt complet. Le contrôle du cours exécute aussi des copies inchangées du code avec une détection des dépassements de limites à l'exécution dans un répertoire jetable. Il peut détecter les erreurs exercées par ces entrées ; il ne prouve pas une sûreté mémoire complète et ne répare pas vos fichiers.

Résumé

Vous avez transformé un exemple à un article en un reçu complet à plusieurs lignes. Vous avez utilisé des structures, un tableau d'achats séparés, des identifiants stables du catalogue et des calculs en centimes entiers, puis enregistré et lu le résultat réel. Le laboratoire suivant porte sur la validation des entrées avant qu'un achat puisse rejoindre ce tableau.