web-dev-qa-db-fra.com

Comment imprimer des caractères spéciaux explicitement en C?

Quand j'utilise le code ci-dessous:

#include <stdio.h>

int main(void)
{
    printf("%s","Hello world\nHello world");
    return 0;
}

il imprime comme:

 Hello world
 Hello world

Comment puis-je empêcher cela et l’imprimer en tant que littéral chaîne brute en C? Je veux dire qu'il devrait être affiché tel quel dans la fenêtre du terminal, comme ci-dessous:

Hello world\nHello world

Je sais que je peux y parvenir en utilisant une barre oblique inversée pour printf, mais existe-t-il une autre fonction C ou un moyen de le faire sans barre oblique inversée? Ce serait utile lors de la lecture de fichiers.

8
Jessie

Il n'y a pas de mécanisme intégré pour faire cela. Vous devez le faire manuellement, caractère par caractère. Cependant, les fonctions dans ctype.h peuvent aider. Spécifiquement, dans les paramètres régionaux "C", la fonction isprint est garantie comme étant vraie pour tous les caractères graphiques du jeu de caractères d'exécution de base}, qui est en réalité identique à tous les caractères graphiques de 7 bits. ASCII, plus espace; et il est garanti que not soit vrai pour tous les contrôles caractères en ASCII 7 bits, ce qui inclut une tabulation, un retour chariot, etc.

Voici un croquis:

#include <stdio.h>
#include <ctype.h>
#include <locale.h>

int main(void)
{
    int x;
    setlocale(LC_ALL, "C"); // (1)

    while ((x = getchar()) != EOF)
    {
        unsigned int c = (unsigned int)(unsigned char)x; // (2)

        if (isprint(c) && c != '\\')
            putchar(c);
        else
            printf("\\x%02x", c);
    }
    return 0;
}

Cela n'échappe pas à ' ni à ", mais il échappe à \, et il est facile de l'étendre si vous en avez besoin.

Imprimer \n pour U + 000A, \r pour U + 000D, etc. est laissé comme exercice. Le traitement des caractères en dehors du jeu de caractères d'exécution de base (par exemple, le codage UTF-8 de U + 0080 à U + 10FFFF) est également laissé comme exercice.

Ce programme contient deux choses qui ne sont pas nécessaires avec une bibliothèque C entièrement conforme aux normes, mais selon mon expérience, elles étaient nécessaires sur des systèmes d'exploitation réels. Ils sont marqués avec (1) et (2).

1) Ceci définit explicitement la configuration des "paramètres régionaux" de la manière dont il est défini par défaut sur {supposé} _.

2) La valeur renvoyée par getchar est une int. Il est supposé être un nombre compris dans la plage représentée par unsigned char (normalement compris entre 0 et 255), ou la valeur spéciale EOF (qui est non dans la plage pouvant être représentée par unsigned char). . Cependant, les bibliothèques C buggées sont connues pour renvoyer des nombres négatifs pour les caractères dont le bit le plus élevé est défini. Si cela se produit, la printf imprimera (par exemple) \xffffffa1 alors qu'elle aurait dû imprimer \xa1. Transformer x en unsigned char puis revenir en unsigned int corrige ce problème.

5
zwol

Quelque chose comme ça pourrait être ce que vous voulez. Exécutez myprint(c) pour imprimer le caractère C ou une représentation imprimable de celui-ci:

#include <ctype.h>

void myprint(int c)
{
    if (isprint(c))
        putchar(c); // just print printable characters
    else if (c == '\n')
        printf("\\n"); // display newline as \n
    else
        printf("%02x", c); // print everything else as a number
}

Si vous utilisez Windows, je pense que toutes vos nouvelles lignes seront au format CRLF (retour à la ligne, saut de ligne). Elles seront donc imprimées en tant que 0d\n comme j'ai écrit cette fonction.

1
yellowantphil

Si je comprends la question, si vous avez une chaîne contenant des caractères de contrôle tels que nouvelle ligne, tabulation, retour arrière, etc., vous souhaitez imprimer une représentation textuelle de ces caractères plutôt que de les interpréter comme des caractères de contrôle. 

Malheureusement, il n'y a pas de spécificateur de conversion intégré printf qui le fera pour vous. Vous devrez parcourir la chaîne caractère par caractère, tester chacun d'eux pour voir s'il s'agit d'un caractère de contrôle et écrire du texte équivalent pour celui-ci.

Voici un exemple rapide et légèrement testé:

#include <stdio.h>
#include <limits.h>
#include <ctype.h>
...
char *src="This\nis\ta\btest";

char *lut[CHAR_MAX] = {0};  // look up table for printable equivalents
                            // of non-printable characters
lut['\n'] = "\\n";
lut['\t'] = "\\t";
lut['\b'] = "\\b";
...
for ( char *p = src; *p != 0; p++ )
{
  if ( isprint( *p ) )
    putchar( *p );
  else
    fputs( lut[ (int) *p], stdout ); // puts adds a newline at the end,
                                     // fputs does not.
}
putchar( '\n' );
0
John Bode

Merci l'utilisateur@chunkpour avoir contribué à l'amélioration de cette réponse.


Pourquoi n'avez-vous pas écrit une solution polyvalente? Cela vous éviterait de nombreux problèmes à l'avenir.

char *
str_escape(char str[])
{
    char chr[3];
    char *buffer = malloc(sizeof(char));
    unsigned int len = 1, blk_size;

    while (*str != '\0') {
        blk_size = 2;
        switch (*str) {
            case '\n':
                strcpy(chr, "\\n");
                break;
            case '\t':
                strcpy(chr, "\\t");
                break;
            case '\v':
                strcpy(chr, "\\v");
                break;
            case '\f':
                strcpy(chr, "\\f");
                break;
            case '\a':
                strcpy(chr, "\\a");
                break;
            case '\b':
                strcpy(chr, "\\b");
                break;
            case '\r':
                strcpy(chr, "\\r");
                break;
            default:
                sprintf(chr, "%c", *str);
                blk_size = 1;
                break;
        }
        len += blk_size;
        buffer = realloc(buffer, len * sizeof(char));
        strcat(buffer, chr);
        ++str;
    }
    return buffer;
}

Comment ça marche!

int
main(const int argc, const char *argv[])
{
    puts(str_escape("\tAnbms\n"));
    puts(str_escape("\tA\v\fZ\a"));
    puts(str_escape("txt \t\n\r\f\a\v 1 \t\n\r\f\a\v tt"));
    puts(str_escape("dhsjdsdjhs hjd hjds "));
    puts(str_escape(""));
    puts(str_escape("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\f\a\v"));
    puts(str_escape("\x0b\x0c\t\n\r\f\a\v"));
    puts(str_escape("\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14"));
}

Sortie

\tAnbms\n
\tA\v\fZ\a
txt \t\n\r\f\a\v 1 \t\n\r\f\a\v tt
dhsjdsdjhs hjd hjds 

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ \t\n\r\f\a\v
\v\f\t\n\r\f\a\v
\a\b\t\n\v\f\r

Cette solution repose sur des informations provenant de Wikipedia https://en.wikipedia.org/wiki/Escape_sequences_in_C#Table_of_escape_sequences Et des réponses des autres utilisateurs du stackoverflow.com.


Environnement de test

$ lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description:    Debian GNU/Linux 8.6 (jessie)
Release:    8.6
Codename:   jessie
$ uname -a
Linux localhost 3.16.0-4-AMD64 #1 SMP Debian 3.16.36-1+deb8u2 (2016-10-19) x86_64 GNU/Linux
$ gcc --version
gcc (Debian 4.9.2-10) 4.9.2
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
0
Seti Volkylany