Texte & fichiers
Source: tutorial/03_text_and_files See in Playground
Lit un fichier texte ligne par ligne, garde les lignes qui correspondent à un motif d’expression régulière, et écrit un petit rapport résumant combien de lignes (non vides) ont été lues et combien ont correspondu.
Exécution
cd tutorial/03_text_and_files
melodium run Compo.toml --input_file sample.txt --pattern "Mélodium"Cet exemple lit sample.txt (fourni avec l’exemple), journalise chaque ligne correspondant au motif pattern, et écrit report.txt avec le total de lignes et de correspondances.
Optionnel : ajoutez --api-report et un jeton d’API (MELODIUM_API_TOKEN) pour voir la trace complète de cette exécution sur Cadence.CI .
Fonctionnement
Cet exemple n’utilise aucun modèle : la lecture de fichier, la correspondance d’expressions régulières et la composition de texte sont toutes des traitements sans état.
Flux de données
D’un fichier à un flux de lignes
readTextLocal diffuse le contenu brut du fichier par blocs. split avec delimiter="\n" puis flatten transforment cela en un flux de lignes : le même enchaînement “split puis flatten” sert à décoder n’importe quel flux délimité, pas seulement un fichier.
read: readTextLocal(path=input_file)
readFailed: logInfoMessage(label="read", message="could not read input file")
startup.trigger -> read.trigger
read.failed -> readFailed.trigger
splitLines: split(delimiter="\n", inclusive=false)
lines: flatten<string>()
trimmed: trim()
read.text -> splitLines.text,splitted -> lines.vector,value -> trimmed.textDécouper sur "\n" laisse un morceau vide après le dernier saut de ligne du fichier, d’où une petite chaîne exact + not + filter qui élimine les lignes vides avant toute autre étape :
isBlank: exact(pattern="")
notBlank: not<bool>()
nonBlank: filter<string>()
trimmed.trimmed -> isBlank.text
isBlank.matches -> notBlank.value
trimmed.trimmed -> nonBlank.value
notBlank.not -> nonBlank.selectC’est le schéma général pour “garder tout sauf X” : la même forme est réutilisée un peu plus loin pour ne garder que les lignes correspondant à un motif, simplement inversée.
Correspondance avec le motif
Chaque ligne est testée par rapport à pattern avec matches ; le flux de booléens obtenu pilote filter, dont la branche accepted est à la fois journalisée et comptée :
isMatch: matches(regex=pattern)
matching: filter<string>()
nonBlank.accepted -> isMatch.text
nonBlank.accepted -> matching.value
isMatch.matches -> matching.select
logMatches: logInfos(label="match")
matching.accepted -> logMatches.messagesAgréger un flux en une seule valeur
Les totaux sont calculés par un petit traitement local, finalCount<T>, réutilisé à la fois pour le nombre de lignes et le nombre de correspondances : count numérote chaque élément au fil du flux, et trigger .last ramène ce compteur courant à sa valeur finale une fois le flux terminé :
treatment finalCount<T>()
input items: Stream<T>
output total: Block<string>
{
index: count<T>()
asStr: toString<u128>()
lastStr: trigger<string>()
Self.items -> index.stream,count -> asStr.value,into -> lastStr.stream
lastStr.last -> Self.total
}Construction du rapport
Les deux totaux et le motif sont combinés en un seul StringMap avec blockEntry/blockInsert (des alias pour entry et insert), puis formatés dans le texte final du rapport avec format :
withLines: blockEntry(key="lines")
withMatches: blockInsert(key="matches")
withPattern: blockInsert(key="pattern")
lineTotal.total -> withLines.value
withLines.map -> withMatches.base
matchTotal.total -> withMatches.value
withMatches.map -> withPattern.base
patternBlock.emit -> withPattern.value
reportEntries: stream<StringMap>()
reportLine: format(format="Report for pattern \"{pattern}\": {lines} line(s) read, {matches} matching.")
write: writeTextLocal(path=output)
logDone: logInfoMessage(label="report", message="report written")
withPattern.map -> reportEntries.block,stream -> reportLine.entries,formatted -> write.text
write.finished -> logDone.triggerDépendances
[dependencies]
std = "0.10.3" # flux de base, journalisation, structures de données
fs = "0.10.3" # lecture/écriture de fichiers locaux
regex = "0.10.3" # expressions régulières