Aligner texte console

Contenu du snippet

Un problème récurrent que je vois sur le forum: comment aligner correctement le texte en mode console ?
A l'origine, c'est une question du forum: http://www.cppfrance.com/forum/sujet-AFFICHAGE-PROGRAMME_1357462.aspx

Voici un petit code qui affiche la table de multiplication d'un nombre donné, tout en adaptant la taille des colonnes.
Certes le code n'est pas compliqué, mais tout l'intérêt réside dans la technique d'alignement utilisé.

Explication:

std::setiosflags(std::ios::left) : Permet de justifier le texte vers la gauche dans un espace.
std::resetiosflags(std::ios::left) : Annule la justification vers la gauche dans un espace.
std::setfill(Caractère) : Définit le caractère de remplissage (par défaut c'est le caractère espace ' ').
std::setw(Taille) : Permet de créer un espace d'une taille fixe. Si on crée un espace de 3, alors ce qu'on écrira dedans feras forcement 3 caractères. Si c'est trop court, le texte est tronqué, si c'est trop long l'espace supplémentaire est comblé par le caractère de remplissage. L'espace supplémentaire est comblé par la gauche ou par la droite, suivant la valeur donnée à std::setiosflag.

La technique pour aligner les colonnes est la suivantes:
- On calcule le nombre le plus grand (nb * nb) que l'on convertit en string afin d'obtenir la longueur de la chaîne obtenue. C'est la taille maximale d'une colonne.
- Ensuite lors de l'affichage, on écrit chacun de nos nombres dans un espace de la taille du nombre maximale, tout en les justifiant sur la gauche (Par défaut les nombres sont justifiés à droite).
- Pour créer la ligne de séparation, inutile de faire une boucle, il suffit juste de donner un espace de la taille du tableau en mettant le caractère de rembourrage à '-'. On remet bien évidemment le caractère de rembourrage à ' ' une fois celui-ci affiché.

Source / Exemple :

#include <iostream>
#include <sstream>
#include <iomanip>

namespace
{
  /*!
  ** Convert a string to a choosen type
  **
  ** @param t The dst type
  ** @param s The string to convert
  ** @param f The stream option
  **
  ** @return The dst type
  */
  template <class T>
  inline bool
  fromString(T& t,
             const std::string& s,
             std::ios_base& (*f)(std::ios_base&) = std::dec)
  {
    std::istringstream iss(s);

    return !(iss >> f >> t).fail();
  }

  /*!
  ** Compute the best width possible getting the max size of the max number
  **
  ** @param nb The number
  ** @return Best width possible
  */
  inline int computeBestWidth(int nb)
  {
    std::ostringstream ss;

    ss << (nb * nb);

    return ss.str().length();
  }

  /*!
  ** Help below methods to draw a line
  **
  ** @param buff The buff to fill
  ** @param nb The max number
  ** @param line The current line
  ** @param width The width of each cell
  */
  inline void drawHelper(std::ostringstream& buff, int line, int nb, int width)
  {
    buff << std::setiosflags(std::ios::left);

    if (line == -1)
    {
      buff << std::setw(width) << '*';
      line = 1;
    }
    else
      buff << std::setw(width) << line;
    buff << "| ";
    for (int i = 1; i <= nb; ++i)
      buff << std::setw(width) << (i * line);
    buff << 'n';

    buff << std::resetiosflags(std::ios::left);
  }

  /*!
  ** Draw the header
  **
  ** @param buff The buff to fill
  ** @param nb The max number
  ** @param width The width of each cell
  */
  inline void drawHeader(std::ostringstream& buff, int nb, int width)
  {
    drawHelper(buff, -1, nb, width);
  }

  /*!
  ** Draw the line separator
  **
  ** @param buff The buff to fill
  ** @param nb The max number
  ** @param width The width of each cell
  */
  inline void drawLineSeparator(std::ostringstream& buff, int nb, int width)
  {
    buff << std::setfill('-') << std::setw(width * nb + width + 3)
         << 'n'
         << std::setfill(' ');
  }

  /*!
  ** Draw a line
  **
  ** @param buff The buff to fill
  ** @param nb The max number
  ** @param line The current line
  ** @param width The width of each cell
  */
  inline void drawLine(std::ostringstream& buff, int line, int nb, int width)
  {
    drawHelper(buff, line, nb, width);
  }

  /*!
  ** Draw all lines
  **
  ** @param buff The buff to fill
  ** @param nb The max number
  ** @param width The width of each cell
  */
  inline void drawBody(std::ostringstream& buff, int nb, int width)
  {
    for (int i = 1; i <= nb; ++i)
      drawLine(buff, i, nb, width);
  }

  /*!
  ** Draw a muliplication table
  **
  ** @param nb The max number
  */
  inline void displayComputeTable(int nb)
  {
    std::ostringstream buff;
    const int width = computeBestWidth(nb) + 1;

    drawHeader(buff, nb, width);
    drawLineSeparator(buff, nb, width);
    drawBody(buff, nb, width);

    std::cout << buff.str() << std::endl;
  }
}

/*!
** Given a valid number, this program draw a multiplication table
**
** @param argc Number of arguments
** @param argv List of arguments
** @return 1 if too few arguments, 2 if invalid argument, 0 else
*/
int main(int argc, char** argv)
{
  if (argc <= 1)
  {
    std::cerr << "Usage: " << argv[0] << " number" << std::endl;
    return 1;
  }

  int nb = 0;
  if (!fromString<int>(nb, argv[1]) || nb < 1)
  {
    std::cerr << "Invalid number: " << argv[1] << std::endl;
    return 2;
  }

  displayComputeTable(nb);

  return 0;
}

Conclusion :

La STL regorge d'outil souvent peu connu, mais tellement pratique !

A noter que l'on pourrait facilement faire en sorte que les colonnes ait des tailles différentes, mais adaptées en fonction de ce qu'elles contiennent.
Il suffit de calculer un tableau contenant les meilleures tailles pour chacune des colonnes, et de l'appliquer à ces colonnes de manière individuelles.

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.