Comment arrêter les codes couleurs ANSI chambouler l'alignement printf?
-
03-10-2019 - |
Question
J'ai découvert ce en utilisant printf rubis, mais il applique également à la printf de C.
Si vous incluez les codes d'échappement couleur ANSI dans une chaîne de sortie, il salit l'alignement.
Ruby:
ruby-1.9.2-head > printf "%20s\n%20s\n", "\033[32mGreen\033[0m", "Green"
Green # 6 spaces to the left of this one
Green # correctly padded to 20 chars
=> nil
Le même ligne dans un programme C produit la même sortie.
Y at-il de toute façon à obtenir printf (ou autre chose) à la sortie align et ne pas ajouter des espaces pour les caractères non-imprimés?
Est-ce un bug, ou est-il une bonne raison?
Mise à jour: Depuis printf ne peut pas se fier aux données d'alignement quand il y a des codes ANSI et de grands caractères, est-il une meilleure façon pratique de faire la queue des données tabulaires de couleur dans la console en rubis
La solution
Je suis en désaccord avec votre interprétation des « 9 espaces après le vert vert ». J'utilise Perl plutôt que Ruby, mais si j'utilise une modification de votre déclaration, l'impression d'un symbole de la conduite après la chaîne, je reçois:
perl -e 'printf "%20s|\n%20s|\n", "\033[32mGreen\033[0m", "Green";'
Green|
Green|
Cela me montre que la déclaration de 14 caractères printf()
compté dans la chaîne, il préfixé 6 places pour produire 20 caractères alignés à droite. Cependant, le terminal avalé 9 de ces caractères, les interpréter comme des changements de couleur. Ainsi, la sortie est apparu 9 caractères plus courte que vous le voulais. Cependant, la printf()
n'a pas imprimer 9 blancs après la première « verte ».
En ce qui concerne les meilleures pratiques pour la sortie alignée (avec colorisation), je pense que vous aurez besoin d'avoir chaque champ de taille et aligné entouré par un simple champs '% s de qui traitent de la colorisation:
printf "%s%20.20s%s|%s%-10d%s|%s%12.12s%s|\n",
co_green, column_1_data, co_plain,
co_blue, column_2_data, co_plain,
co_red, column_3_data, co_plain;
Si, de toute évidence, les variables co_XXXX
(constantes?) Contiennent les séquences d'échappement pour passer à la couleur du nom (et co_plain
peut-être mieux comme co_black
). S'il se avère que vous n'avez pas besoin colorisation sur un domaine, vous pouvez utiliser la chaîne vide à la place des variables co_XXXX
(ou appeler co_empty
).
Autres conseils
Il est pas un bug: il n'y a pas ruby ??moyen devrait savoir (au moins dans printf, ce serait une autre histoire pour quelque chose comme malédictions) que son stdout va un terminal qui comprend VT100 séquences d'échappement
.Si vous n'êtes pas ajuster les couleurs d'arrière-plan, quelque chose comme cela pourrait être une meilleure idée:
GREEN = "\033[32m"
NORMAL = "\033[0m"
printf "%s%20s%s\n", GREEN, "Green", NORMAL
champ printf
largeur spécificateurs ne sont pas utiles pour aligner des données tabulaires, des éléments d'interface, etc. Outre la question des caractères de contrôle que vous avez déjà découvert, il y a aussi sans chasse et caractères à double largeur de votre programme devra traiter si vous ne voulez pas les choses à limiter les codages de caractères hérités (que beaucoup d'utilisateurs considèrent déconseillés).
Si vous insistez sur l'utilisation printf
cette façon, vous avez probablement besoin de faire quelque chose comme:
printf("%*s\n%*s\n", bytestopad("\033[32mGreen\033[0m", 20), "\033[32mGreen\033[0m", bytestopad("Green", 20), "Green");
où bytestopad(s,n)
est une fonction que vous écrivez qui calcule le nombre total d'octets sont nécessaires (chaîne, plus les espaces rembourrage) pour entraîner la s
chaîne prenant des colonnes terminales de n
. Cela impliquerait l'analyse syntaxique échappe et le traitement de caractères multi-octets et en utilisant une installation (comme la fonction wcwidth
Posix) pour rechercher le nombre de colonnes du terminal chaque Takes. Notez l'utilisation de *
en place d'un champ constant largeur dans la chaîne de format printf
. Cela vous permet de passer un argument int
à printf
pour des largeurs de champ d'exécution variable.
Je séparer des séquences d'échappement de texte réel pour éviter toute la question.
# in Ruby
printf "%s%20s\n%s%20s\n", "\033[32m", "Green", "\033[0m", "Green"
ou
/* In C */
printf("%s%20s\n%s%20s\n", "\033[32m", "Green", "\033[0m", "Green");
Étant donné que les séquences d'échappement ANSI ne font pas partie de l'une ou Ruby C ne pense qu'ils ont besoin de traiter ces caractères spéciaux, et à juste titre.
Si vous allez faire beaucoup de choses de couleur du terminal, alors vous devriez regarder dans malédictions et ncurses qui fournissent des fonctions pour faire la couleur change qui travaillent pour différents types de terminaux. Ils offrent également des fonctionnalités beaucoup plus, comme les fenêtres en mode texte, touches de fonction, et parfois même l'interaction de la souris.
Voici une solution je suis venu récemment. Cela vous permet d'utiliser color("my string", :red)
dans un communiqué de printf
. Je aime utiliser la même mise en forme chaîne pour les en-têtes et les données - SEC. Cela permet. De plus, j'utilise le joyau de l'arc en ciel pour générer les codes de couleur; ce n'est pas parfait, mais fait le travail. Le hachage CPAD
contient deux valeurs pour chaque couleur, correspondant au rembourrage gauche et à droite, respectivement. Bien entendu, cette solution devrait être étendue pour faciliter d'autres couleurs et des modificateurs tels que gras et souligné.
CPAD = {
:default => [0, 2],
:green => [0, 3],
:yellow => [0, 2],
:red => [0, 1],
}
def color(text, color)
"%*s%s%*s" % [CPAD[color][0], '', text.color(color), CPAD[color][1], '']
end
Exemple:
puts "%-10s %-10s %-10s %-10s" % [
color('apple', :red),
color('pear', :green),
color('banana', :yellow)
color('kiwi', :default)
]