
string
Le texte : découper, chercher, changer la casse, mettre en forme des nombres, couper en morceaux et nettoyer. Tout texte a aussi ces fonctions comme méthodes : s:upper().
La bibliothèque string travaille sur le texte : noms d'objets, lignes tapées par un joueur, lignes d'un fichier, messages d'un autre ordinateur. Chaque fonction renvoie un nouveau texte : un texte, lui, ne change jamais.
Écriture en méthode. Toute valeur texte possède aussi ces fonctions comme méthodes : nom:upper() vaut string.upper(nom), et ligne:sub(1, 3) vaut string.sub(ligne, 1, 3). Ça marche sur toute expression qui donne un texte, read():trim() compris. Un texte écrit directement dans le code demande des parenthèses : ("fer"):upper(), car "fer":upper() ne compile pas.
local objet = "minecraft:iron_ingot"
local deux_points = objet:find(":")
print(objet:sub(deux_points + 1))
print(#objet, objet:upper())
print(("laiton"):rep(2, "+"))iron_ingot 20 MINECRAFT:IRON_INGOT laiton+laiton
Les positions commencent à 1 pour le premier caractère. Une position négative compte depuis la fin : -1 est le dernier caractère, -2 celui d'avant. Chaque caractère compte pour un, lettres accentuées comprises. Un texte ne s'indexe pas comme une table : s[1] s'arrête sur attempt to index a string value with a number key, utilisez s:sub(1, 1).
Opérateurs. .. colle deux textes et transforme les nombres en texte tout seul ("x" .. 3 donne "x3"), mais refuse nil, true et les tables (attempt to concatenate a nil value) : passez par tostring. #s est la longueur. == compare les caractères, et < compare les textes selon les codes des caractères : "Zinc" < "apple" (les majuscules passent avant) et "10" < "9" (chiffre par chiffre). Le calcul ne transforme jamais un texte en nombre : "10" + 1 est une erreur, utilisez tonumber.
Écrire un texte. Entre "..." ou '...', avec les séquences \n (nouvelle ligne), \t (tabulation), \\, \", \', \r, \0 et \xNN (un caractère par son code hexadécimal). Un long texte peut s'étaler sur plusieurs lignes entre [[ et ]], où rien n'est interprété.
Un texte contient au plus 65536 caractères. Les dépasser, avec .., string.rep, string.format ou table.concat, arrête le programme avec string too long. Toutes les fonctions de cette page qui prennent un texte acceptent aussi un nombre, converti comme le fait tostring : string.len(1234) vaut 4.
string.len(s) | Le nombre de caractères d'un texte. |
string.upper(s) | Met le texte en majuscules. |
string.lower(s) | Met le texte en minuscules. |
string.sub(s, i [, j]) | La partie du texte de la position i à la position j, toutes deux comprises. |
string.find(s, text [, init]) | La position d'un texte dans un autre, ou nil. |
string.starts(s, prefix) | Vrai si le texte commence par prefix. |
string.ends(s, suffix) | Vrai si le texte finit par suffix. |
string.rep(s, n [, sep]) | Le texte répété n fois, avec un séparateur facultatif entre les copies. |
string.reverse(s) | Le texte à l'envers. |
string.format(fmt, ...) | Construit un texte à partir d'un modèle et de valeurs : "%d objets", "%.2f", "%5s"... |
string.split(s [, sep]) | Coupe le texte en une liste de morceaux, à chaque séparateur. |
string.trim(s) | Enlève les espaces, tabulations et retours à la ligne aux deux bouts du texte. |
string.byte(s [, i]) | Le code d'un caractère du texte (le premier par défaut). |
string.char(...) | Un texte à partir de codes de caractères. |
Longueur et casse
Le nombre de caractères d'un texte.
sstring- un texte
- number
- son nombre de caractères
#s donne la même chose et s'écrit plus vite. Un usage courant : centrer un titre à l'écran.
print(string.len("Andésite"), #"Andésite", #"")
local titre = "FERME À FER"
local largeur = term.get_size().w
print(string.rep(" ", math.floor((largeur - #titre) / 2)) .. titre)8 8 0
FERME À FERVoir aussi string.sub()
Met le texte en majuscules.
sstring- un texte
- string
- le même texte en majuscules
Les lettres accentuées suivent : é devient É. Les autres caractères ne bougent pas.
local nom = "boitier de laiton"
print(nom:upper())
print(string.upper("Portail 3 : ouvert"))
print(("dépôt"):upper())BOITIER DE LAITON PORTAIL 3 : OUVERT DÉPÔT
Voir aussi string.lower()
Met le texte en minuscules.
sstring- un texte
- string
- le même texte en minuscules
La comparaison de textes est exacte : "Oui" ~= "oui". Passez ce qu'un joueur a tapé en minuscules avant de le comparer, pour que OUI, Oui et oui marchent tous :
local function est_oui(reponse)
local r = reponse:trim():lower()
return r == "oui" or r == "o"
end
print(est_oui("OUI"), est_oui(" o "), est_oui("non"))true true false
Dans un programme : write("Ouvrir le portail ? ") if est_oui(read()) then ... end.
Voir aussi string.upper() string.trim()
Découper et chercher
La partie du texte de la position i à la position j, toutes deux comprises.
sstring- un texte
inumber- la première position gardée (négative : depuis la fin)
jnumber facultatif- la dernière position gardée, comprise (
-1, la fin, si elle est omise)
- string
- les caractères de
iàj
local s = "Presse mécanique"
print(s:sub(1, 6))
print(s:sub(8))
print(s:sub(-4))
print(s:sub(-4, -3))Presse mécanique ique iq
Elle n'échoue jamais sur des positions hors du texte : un début avant le premier caractère part du premier, une fin après le dernier caractère s'arrête au dernier, et un début après la fin donne "". Les positions doivent être des entiers (bad argument #2 to 'sub' (number has no integer representation)).
local s = "Scie"
print("[" .. s:sub(0, 2) .. "]", "[" .. s:sub(3, 99) .. "]", "[" .. s:sub(6) .. "]")[Sc] [ie] []
Pour faire tenir un nom dans une colonne, coupez-le et marquez la coupure :
local function ajuster(nom, largeur)
if #nom > largeur then
return nom:sub(1, largeur - 1) .. "~"
end
return nom
end
print(ajuster("Alliage d'andésite", 10))
print(ajuster("Zinc", 10))Alliage d~ Zinc
Voir aussi string.find() string.split()
La position d'un texte dans un autre, ou nil.
sstring- le texte où chercher
textstring- le texte cherché, pris tel quel
initnumber facultatif- la position où commence la recherche (négative : depuis la fin)
- number|nil
- la position où
textapparaît pour la première fois, ou nil
La recherche est simple : chaque caractère se représente lui-même. Les motifs de Lua n'existent pas en Brass, donc ., %, [ ou ( n'ont rien de spécial (en Lua, ("v1.2"):find(".") trouve n'importe quel caractère, ici il trouve le point). Il n'y a pas non plus de string.match, gmatch ni gsub : find, sub et split font le même travail. Un quatrième argument (le drapeau « plain » de Lua) est accepté et ignoré.
Seule la position de début revient. La correspondance se termine en debut + #text - 1. La recherche distingue majuscules et minuscules : passez les deux textes en minuscules pour l'ignorer.
print(("v1.2"):find("."))
print(("Lingot de fer"):find("fer"), ("Lingot de fer"):find("Fer"))
local id = "create:brass_ingot"
local deux_points = id:find(":")
print(id:sub(1, deux_points - 1), id:sub(deux_points + 1))3 11 nil create brass_ingot
init fait partir la recherche plus loin, ce qui permet de trouver toutes les occurrences. Un départ après la fin donne nil :
local journal = "bourrage presse; ok; bourrage scie; ok"
local nombre, depuis = 0, 1
while true do
local pos = journal:find("bourrage", depuis)
if pos == nil then
break
end
nombre = nombre + 1
depuis = pos + 1
end
print("bourrages : " .. nombre)bourrages : 2
Voir aussi string.sub() string.starts() string.split()
Vrai si le texte commence par prefix.
sstring- un texte
prefixstring- le début cherché
- boolean
- true si
scommence parprefix
Une extension de Brass (Lua n'en a pas). Exacte, sensible à la casse, et un préfixe vide est toujours trouvé. Pratique pour trier des identifiants d'objets par mod, ou des commandes par leur premier mot :
local objets = {"create:brass_ingot", "minecraft:iron_ingot", "create:zinc_ingot"}
for _, id in ipairs(objets) do
if id:starts("create:") then
print(id)
end
endcreate:brass_ingot create:zinc_ingot
Voir aussi string.ends() string.find()
Vrai si le texte finit par suffix.
sstring- un texte
suffixstring- la fin cherchée
- boolean
- true si
sfinit parsuffix
Une extension de Brass, comme string.starts. Utilisez-la pour des familles d'objets ou des noms de fichiers :
local ids = {"minecraft:iron_ingot", "minecraft:iron_nugget", "create:zinc_ingot"}
local lingots = 0
for _, id in ipairs(ids) do
if id:ends("_ingot") then
lingots = lingots + 1
end
end
print(lingots .. " sortes de lingots")
print(("rapport.log"):ends(".log"), ("rapport.log"):ends(""))2 sortes de lingots true true
Voir aussi string.starts()
Construire du texte
Le texte répété n fois, avec un séparateur facultatif entre les copies.
sstring- le texte à répéter
nnumber- combien de fois (0 ou moins donne
"") sepstring facultatif- un texte placé entre les copies
- string
- les copies, mises bout à bout
print(string.rep("=", 20))
print(("ab"):rep(3, ", "))
print("[" .. ("x"):rep(0) .. "]")==================== ab, ab, ab []
n doit être un entier. Un résultat de plus de 65536 caractères s'arrête sur string too long.
Une barre de progression en caractères : la partie pleine, puis la partie vide, toujours de la même largeur.
local function barre(valeur, max, largeur)
local plein = math.min(largeur, math.round(valeur / max * largeur))
return "[" .. string.rep("#", plein) .. string.rep("-", largeur - plein) .. "]"
end
print(barre(750, 1000, 20) .. " 75%")
print(barre(0, 1000, 20))
print(barre(1200, 1000, 20) .. " plein")[###############-----] 75% [--------------------] [####################] plein
Voir aussi string.format()
Le texte à l'envers.
sstring- un texte
- string
- les mêmes caractères, le dernier en premier
Une ligne de train notée avec une lettre par gare se parcourt à l'envers au retour :
local trajet = "MFUD" -- Mine, Fonderie, Usine, Dépôt
print("Aller : " .. trajet)
print("Retour : " .. trajet:reverse())Aller : MFUD Retour : DUFM
Voir aussi string.sub()
Construit un texte à partir d'un modèle et de valeurs : "%d objets", "%.2f", "%5s"...
fmtstring- le modèle, avec un code
%à la place de chaque valeur valuesany facultatif- une valeur par code, dans l'ordre
- string
- le modèle avec les valeurs écrites dedans
Chaque code % du modèle est remplacé par la valeur suivante, écrite comme le code le demande :
| code | prend | écrit | exemple |
|---|---|---|---|
%d ou %i | un entier | le nombre | 42 |
%f | un nombre | avec 6 décimales, ou .N décimales | 3.141593, %.2f → 3.14 |
%s | n'importe quelle valeur | la valeur, comme tostring | Fer, true, nil |
%x, %X | un entier | en hexadécimal | ff, FF |
%% | rien | un signe % | % |
Entre le % et la lettre, dans cet ordre, vous pouvez ajouter :
- des drapeaux :
-complète à droite (la valeur se cale à gauche),0complète les nombres avec des zéros,+affiche toujours le signe d'un nombre, une espace met une espace là où un nombre positif n'a pas de signe. - une largeur, d'un ou deux chiffres : le nombre minimum de caractères, complété par des espaces à gauche.
%5décrit42sous la forme42. - une précision, un point et des chiffres : le nombre de décimales pour
%f, le nombre maximum de caractères gardés pour%s(%.3scoupeAndésiteenAnd).%det%xl'ignorent.
Le séparateur décimal est toujours un point, jamais une virgule.
print(string.format("%d objets dans %d coffres", 1250, 20))
print(string.format("Vitesse : %.1f tr/min", 63.27))
print(string.format("[%5d] [%-5d] [%05d]", 42, 42, 42))
print(string.format("%+d / %+d", 5, -5))
print(string.format("%x %X 100%%", 255, 3054))
print(("%.3s"):format("Andésite"))1250 objets dans 20 coffres Vitesse : 63.3 tr/min [ 42] [42 ] [00042] +5 / -5 ff BEE 100% And
Des colonnes. Une largeur par colonne, - pour les textes pour qu'ils s'alignent à gauche, rien pour les nombres pour qu'ils s'alignent à droite :
local stock = {
{nom = "Lingot de fer", nombre = 1250},
{nom = "Lingot de cuivre", nombre = 87},
{nom = "Pépite de zinc", nombre = 4},
{nom = "Lingot de laiton", nombre = 320},
}
print(string.format("%-16s %6s", "Objet", "Nombre"))
print(string.rep("-", 23))
for _, ligne in ipairs(stock) do
print(string.format("%-16s %6d", ligne.nom, ligne.nombre))
endObjet Nombre ----------------------- Lingot de fer 1250 Lingot de cuivre 87 Pépite de zinc 4 Lingot de laiton 320
Une horloge. os.day_time() compte les ticks de la journée Minecraft, 0 correspondant à 6 h du matin. %02d écrit deux chiffres, avec un zéro devant si besoin :
local function horloge(ticks)
local t = (ticks + 6000) % 24000
local h = math.floor(t / 1000)
local m = math.floor(t % 1000 * 60 / 1000)
return string.format("%02d:%02d", h, m)
end
print(horloge(os.day_time()))
print(horloge(0), horloge(6500), horloge(18000))
local secondes = 754
print(string.format("encore %d:%02d", secondes // 60, secondes % 60))12:00 06:00 12:30 00:00 encore 12:34
Quand elle échoue. string.format est stricte, et le programme s'arrête :
| message | cause |
|---|---|
bad argument #2 to 'format' (number has no integer representation) | %d ou %x avec un nombre à virgule : arrondissez-le d'abord (math.floor, math.round) |
bad argument #2 to 'format' (number expected, got string) | %d, %f ou %x avec un texte, même "42" : convertissez-le avec tonumber |
bad argument #3 to 'format' (no value) | moins de valeurs que de codes |
invalid conversion '%e' to 'format' | un code qui n'existe pas en Brass (%c, %e, %g, %o, %q...), une largeur de trois chiffres, une précision au-delà de 99, un % seul à la fin (écrivez %%), ou des drapeaux qui ne vont pas avec le code : - ou 0 sans largeur, + avec %s ou %x |
Les valeurs en trop sont ignorées. Avec %f, l'infini et « pas un nombre » s'écrivent Infinity et NaN (%s écrit inf et nan), et avec %x un nombre négatif sort sur 16 chiffres hexadécimaux (-1 donne ffffffffffffffff).
Voir aussi tostring() string.rep()
Couper et nettoyer
Coupe le texte en une liste de morceaux, à chaque séparateur.
sstring- le texte à couper
sepstring facultatif- le séparateur, pris tel quel (une seule espace
" "s'il est omis)
- table
- les morceaux, une liste de textes
Une extension de Brass qui remplace les motifs de Lua dans la plupart des cas. Le séparateur peut faire plusieurs caractères (", "). Les morceaux gardent tout ce qui se trouve entre deux séparateurs, donc :
- deux séparateurs d'affilée donnent un morceau vide, et un séparateur au début ou à la fin donne un morceau vide à cet endroit ;
- un texte sans le séparateur donne une liste avec le texte entier, et un texte vide donne
{""}; - un séparateur vide
""coupe entre chaque caractère.
local morceaux = ("fer,or,,cuivre"):split(",")
print(#morceaux, morceaux[3] == "", morceaux[4])
local lettres = ("ABC"):split("")
print(#lettres, lettres[1], lettres[3])
print(#("a b"):split(), #(""):split(","))4 true cuivre 3 A C 2 1
Avec table.concat, il remplace un morceau de texte par un autre partout :
print(table.concat(("lingot_de_fer"):split("_"), " "))lingot de fer
Lire les commandes tapées par le joueur. Sans séparateur, split coupe à chaque espace, donc deux espaces d'affilée donnent un mot vide : nettoyez la ligne et sautez les mots vides.
local function mots(ligne)
local liste = {}
for _, m in ipairs(ligne:trim():split(" ")) do
if m ~= "" then
table.insert(liste, m)
end
end
return liste
end
local function executer(ligne)
local m = mots(ligne)
local commande = m[1]
if commande == nil then
return
end
if commande == "vitesse" then
local rpm = tonumber(m[2])
if rpm == nil then
print("usage : vitesse <tr/min>")
else
print("vitesse réglée à " .. rpm .. " tr/min")
end
elseif commande == "ouvrir" or commande == "fermer" then
print(commande .. " le portail " .. (m[2] or "1"))
else
print("commande inconnue : " .. commande)
end
end
executer("vitesse 128")
executer(" ouvrir 3 ")
executer("vitesse rapide")
executer("")
executer("sauter")vitesse réglée à 128 tr/min ouvrir le portail 3 usage : vitesse <tr/min> commande inconnue : sauter
Dans un vrai programme, les lignes viennent du clavier : while true do write("> ") executer(read()) end.
Voir aussi string.trim() table.concat()
Enlève les espaces, tabulations et retours à la ligne aux deux bouts du texte.
sstring- un texte
- string
- le texte sans espaces à ses extrémités
Les espaces à l'intérieur restent. Nettoyez ce qu'un joueur a tapé et les lignes d'un fichier (un fichier écrit sur un autre système peut finir ses lignes par un \r invisible).
local tape = " Lingot de fer \n"
print("[" .. tape:trim() .. "]")[Lingot de fer]
Voir aussi string.split()
Codes des caractères
Chaque caractère a un numéro, son code : A vaut 65, a 97, 0 48, é 233 (le code Unicode). Les codes servent à passer des lettres aux nombres et inversement.
Le code d'un caractère du texte (le premier par défaut).
sstring- un texte
inumber facultatif- la position du caractère (1 si elle est omise, négative : depuis la fin)
- number|nil
- le code de ce caractère, nil après la fin
Lua peut renvoyer plusieurs codes d'un coup. Brass en renvoie un : appelez-la une fois par position.
print(string.byte("A"), ("abc"):byte(2), ("abc"):byte(-1))
print(("abc"):byte(10))65 98 99 nil
Les étagères d'un mur de stockage s'appellent A1, B4, C12 : la lettre donne la colonne.
local function case(code)
local colonne = code:upper():byte(1) - string.byte("A") + 1
local rangee = tonumber(code:sub(2))
return {colonne = colonne, rangee = rangee}
end
local c = case("c12")
print(c.colonne, c.rangee)3 12
Voir aussi string.char()
Un texte à partir de codes de caractères.
codesnumber- un ou plusieurs codes de caractères, de 0 à 65535
- string
- le texte formé par ces caractères
string.char() sans code donne "". Un code hors de 0 à 65535 s'arrête sur bad argument #1 to 'char' (value out of range), un code à virgule sur bad argument #1 to 'char' (number has no integer representation) (le numéro est la position du code fautif). Les codes sous 32 sont des caractères de contrôle : l'écran les affiche comme ?.
print(string.char(72, 105))
for i = 1, 5 do
write(string.char(64 + i) .. " ")
end
print()
print(string.char(("A"):byte() + 1))Hi A B C D E B
Voir aussi string.byte()
Motifs courants
Un tableau de stock. string.format pour les colonnes, string.rep pour les barres, les couleurs de term :
local coffres = {
{nom = "Lingot de fer", nombre = 3120, capacite = 4096},
{nom = "Lingot de cuivre", nombre = 860, capacite = 4096},
{nom = "Lingot de zinc", nombre = 3950, capacite = 4096},
{nom = "Lingot de laiton", nombre = 140, capacite = 2048},
}
term.clear()
term.set_cursor(1, 1)
term.set_fg(term.colors.yellow)
print(string.format("%-16s %6s %s", "OBJET", "NOMBRE", "NIVEAU"))
for _, c in ipairs(coffres) do
local taux = c.nombre / c.capacite
local plein = math.round(taux * 20)
term.set_fg(term.colors.white)
write(string.format("%-16s %6d ", c.nom, c.nombre))
if taux >= 0.9 then
term.set_fg(term.colors.red) -- presque plein : l'entrée va s'arrêter
elseif taux < 0.1 then
term.set_fg(term.colors.orange) -- presque vide
else
term.set_fg(term.colors.lime)
end
print(string.rep("#", plein) .. string.rep(".", 20 - plein) .. string.format(" %3d%%", math.round(taux * 100)))
end
**Lire des lignes cle=valeur.** split coupe le fichier en lignes, find et sub coupent chaque ligne au =, trim nettoie les deux côtés, tonumber refait des nombres. L'exemple de pcall de Fonctions globales construit un chargeur de réglages complet sur cette base.
local texte = "vitesse = 64\ncote = back\n# un commentaire\n"
local config = {}
for _, ligne in ipairs(texte:split("\n")) do
local egal = ligne:find("=")
if egal and not ligne:starts("#") then
config[ligne:sub(1, egal - 1):trim()] = ligne:sub(egal + 1):trim()
end
end
print(config.vitesse, config.cote, tonumber(config.vitesse) * 2)64 back 128