In deze hoofdstukuitdaging ga je een versie van het klassieke wc-programma maken, dat zijn oorsprong vindt in de eerste versie van AT&T Unix. Dit programma toont het aantal regels, woorden en bytes van tekst die van STDIN of één of meer bestanden afkomstig zijn. Ik gebruik het vaak om het aantal regels te tellen die door een ander proces worden geretourneerd. Dit is wat je leert in dit hoofdstuk:
-
Gebruik de functie
Iterator::all -
Maak een module voor eenheidstests
-
Simuleer een bestandshandle voor testen
-
Formatteer en print voorwaardelijk een waarde
-
Compileer een module voor testen met voorwaardelijke compilatie
-
Verdeel een tekstregel in woorden, bytes en karakters
Hoe werkt wc?
Om te begrijpen wat er van je verwacht wordt, begin ik met uitleggen hoe het wc-programma werkt. Dit komt uit de BSD-manpagina van wc. De basisfunctionaliteit van het programma is als volgt:
-
Het wc-programma toont het aantal regels, woorden en bytes die in elk ingevoerd bestand staan, of standaardinvoer (indien geen bestand is opgegeven).
-
Een regel wordt gedefinieerd als een reeks tekens, begrensd door een lijn-einde teken (zoals
\n). -
Een woord wordt gedefinieerd als een reeks tekens, begrensd door witruimtes. Witruimtes omvatten spaties, tabs en nieuwe regels.
Er zijn verschillende opties die je kunt gebruiken:
-
-c: Geeft het aantal bytes in elk invoerbestand weer.
-
-l: Geeft het aantal regels weer.
-
-m: Geeft het aantal karakters weer (voor systemen die meerdere bytes per karakter gebruiken).
-
-w: Geeft het aantal woorden weer.
Standaard geeft het programma de drie waarden weer: regels, woorden en bytes.
Voorbeelden van wc in actie
Stel je voor dat je drie verschillende tekstbestanden hebt:
-
empty.txt: een leeg bestand.
-
fox.txt: een bestand met één regel tekst en verschillende spaties en een tab.
-
atlamal.txt: een bestand met het eerste vers van "Atlamál hin groenlenzku", een Oudnoords gedicht.
Als je wc uitvoert op een leeg bestand, wordt het volgende geretourneerd:
Dit betekent dat er 0 regels, 0 woorden en 0 bytes in het bestand staan.
Voor een bestand als fox.txt, dat een enkele regel bevat met verschillende tussenruimtes, krijg je:
Dit betekent dat er 1 regel, 9 woorden en 48 bytes in het bestand staan.
Bijvoorbeeld, een bestand als atlamal.txt bevat veel Unicode-tekens die meerdere bytes vereisen. Als je het commando wc uitvoert, krijg je:
Dit geeft aan dat er 4 regels, 29 woorden en 177 bytes zijn.
Je kunt ook specifieke informatie opvragen met de verschillende vlaggen. Wil je bijvoorbeeld alleen het aantal regels, gebruik dan de -l optie:
Op dezelfde manier kun je de andere statistieken opvragen, zoals het aantal woorden (-w) of het aantal bytes (-c).
Implementatie in Rust
In Rust kun je een dergelijk wc-programma maken door gebruik te maken van de kracht van iterators en filehandles. Het idee is om elke invoerlijn te lezen, deze op te splitsen in woorden, bytes of karakters en vervolgens de nodige tellers bij te houden. De BufRead::read_line methode kan worden gebruikt om regels te lezen en de Iterator::all functie helpt je om te controleren of een iteratie volledig is.
Daarnaast moet je de mogelijkheid implementeren om een bestandshandle te "faken" voor testen. Dit kan door het gebruik van een MockFile of door een module te maken die alleen beschikbaar is tijdens het testen met #[cfg(test)].
Wat is belangrijk om te begrijpen?
Bij het bouwen van je wc-programma is het belangrijk om te begrijpen dat de verschillende bestandsindelingen en invoerformaten (zoals UTF-8) de manier waarop je bytes en karakters telt kunnen beïnvloeden. De manier waarop tekens worden opgeslagen in geheugen is essentieel voor het correct berekenen van de bestandsgrootte en het aantal karakters.
Verder moet je goed begrijpen hoe de as-operator werkt in Rust bij typeconversies, vooral wanneer je met verschillende typen (zoals u64 naar usize) werkt. Dit kan verwarrend zijn, maar met wat oefening en geduld zal het uiteindelijk logisch worden.
Je zult ook de kracht van de clap-bibliotheek waarderen voor het beheren van commandoregelargumenten. Het maakt het gemakkelijk om optionele parameters in te voeren die de uitvoer van je programma kunnen aanpassen, zoals het kiezen tussen het weergeven van bytes, regels of woorden.
Een belangrijk concept om op te merken is hoe het programma omgaat met bestandspaden en het verschil tussen lezen van standaardinvoer en het openen van fysieke bestanden. Door dit goed te begrijpen, kun je je programma zowel flexibel als robuust maken.
Hoe om te gaan met voorwaardelijke compilatie en bestandsverwerking in Rust
In de testdirectory "tests/expected" vinden we paren van bestanden voor elke test. Dit betekent dat de test "name_a" twee mogelijke uitvoerbestanden heeft: een voor Unix en een voor Windows. Dit is te zien in de volgende bestandspaden:
De test "name_a" is gedefinieerd als volgt:
De functie run maakt gebruik van de functie format_file_name om de juiste bestandsnaam te creëren. Hierbij wordt gebruik gemaakt van voorwaardelijke compilatie om te bepalen welke versie van de functie gecompileerd wordt, afhankelijk van het besturingssysteem. Dit wordt bereikt door de annotaties #[cfg(windows)] en #[cfg(not(windows))].
Voor Windows wordt de volgende versie van de functie gebruikt om ".windows" toe te voegen aan de verwachte bestandsnaam:
Wanneer de code niet op Windows wordt gecompileerd, wordt deze versie gebruikt, waarbij de originele bestandsnaam wordt teruggegeven:
Het gebruik van std::borrow::Cow in deze functies zorgt ervoor dat de string op Unix-systemen niet gekopieerd wordt, terwijl op Windows de gewijzigde bestandsnaam als een nieuwe eigen string wordt geretourneerd. Dit is een belangrijk aspect, omdat het helpt om onnodige geheugenallocaties te voorkomen op Unix-achtige systemen.
Verder wordt de test unreadable_dir geïllustreerd, die alleen op niet-Windows-platformen draait:
Deze test maakt een directory aan, stelt de bestandspermissies in zodat de directory niet leesbaar is, en voert een commando uit. Het resultaat wordt gecontroleerd om te verifiëren dat de foutmelding correct wordt weergegeven wanneer geprobeerd wordt de directory te benaderen.
Naast het implementeren van dergelijke testcases, kunnen we verder gaan door andere handige functies toe te voegen, zoals de -max_depth en -min_depth opties, die bepalen hoe diep het programma in de directorystructuur moet zoeken. De WalkDir::min_depth en WalkDir::max_depth opties kunnen hierbij van pas komen.
Een andere interessante uitbreiding zou zijn om bestanden te vinden op basis van hun grootte. Het find-programma maakt gebruik van een specifieke syntaxis om bestanden te zoeken die kleiner, groter of exact gelijk zijn aan een opgegeven grootte:
Waarbij n een numerieke waarde is en de eenheid een van de volgende kan zijn: kilobytes (k), megabytes (M), gigabytes (G), terabytes (T), of petabytes (P).
Het find-programma kan ook actie ondernemen op de resultaten, bijvoorbeeld met de optie -delete, waarmee lege bestanden kunnen worden verwijderd:
Een andere uitdaging zou kunnen zijn om een optie -count toe te voegen die aangeeft hoeveel items er zijn gevonden, zoals we eerder hebben gedaan met uniq -c. Dit zou een interessante toevoeging zijn aan het programma.
Daarnaast kan een implementatie van het programma tree nuttig zijn. Dit programma doorzoekt een pad naar bestendirectory's en maakt een visuele weergave van de structuur van bestanden en mappen. De optie -d toont alleen de directories, terwijl de -P optie het mogelijk maakt om alleen bestanden met een bepaald patroon weer te geven. Een voorbeeld:
Tot slot kan het nuttig zijn om je versie van het programma te vergelijken met fd, een andere Rust-implementatie van een vervanger voor find, om te zien hoe anderen dezelfde problemen hebben opgelost.
De belangrijkste les hier is het belang van voorwaardelijke compilatie en het gebruik van de juiste datastructuren om platformonafhankelijke code te schrijven. Rust biedt krachtige hulpmiddelen, zoals #[cfg] en Cow, waarmee je platformspecifieke implementaties kunt maken zonder de leesbaarheid en efficiëntie van je code te verliezen.
Hoe een moderne gebruikerservaring te creëren met dynamische thema’s, meldingen en integraties in een Python-webapplicatie
Hoe de Robots de Oude Wereld Hervormden: Een Blik op de Reconstructie van Timbuctoo en Andere Steden
Hoe kan Amerika’s immigratiebeleid de toekomst van de ‘Dreamers’ beïnvloeden?
Hoe Zelfstandig Ondernemen en Vastgoed de Weg naar Succes Kunnen Leiden: Het Verhaal van Barden

Deutsch
Francais
Nederlands
Svenska
Norsk
Dansk
Suomi
Espanol
Italiano
Portugues
Magyar
Polski
Cestina
Русский