Gibt es einen printf-Konverter zum Drucken im Binärformat?

433

Ich kann mit printf als Hex- oder Oktalzahl drucken. Gibt es ein Format-Tag, das als binäre oder beliebige Basis gedruckt werden soll?

Ich laufe gcc.

printf("%d %x %o\n", 10, 10, 10); //prints "10 A 12\n"
print("%b\n", 10); // prints "%b\n"
Brian
quelle
Soweit ich weiß, können Sie dies nicht mit printf tun. Sie könnten natürlich eine Hilfsmethode schreiben, um dies zu erreichen, aber das klingt nicht nach der Richtung, in die Sie gehen möchten.
Ian P
Dafür ist kein Format vordefiniert. Sie müssen es selbst in eine Zeichenfolge umwandeln und dann die Zeichenfolge drucken.
Rslite
Eine schnelle Google-Suche ergab
Ian P
12
Nicht als Teil der ANSI Standard C-Bibliothek - wenn Sie tragbaren Code schreiben, ist es am sichersten, Ihren eigenen zu rollen.
Tomlogic
Eine Anweisung Standard und generische (für jeden Integral-Typ beliebiger Länge) Lösung der Konvertierung in eine Binärzeichenfolge unter
luart

Antworten:

266

Hacky aber funktioniert bei mir:

#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c"
#define BYTE_TO_BINARY(byte)  \
  (byte & 0x80 ? '1' : '0'), \
  (byte & 0x40 ? '1' : '0'), \
  (byte & 0x20 ? '1' : '0'), \
  (byte & 0x10 ? '1' : '0'), \
  (byte & 0x08 ? '1' : '0'), \
  (byte & 0x04 ? '1' : '0'), \
  (byte & 0x02 ? '1' : '0'), \
  (byte & 0x01 ? '1' : '0') 
printf("Leading text "BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(byte));

Für Multi-Byte-Typen

printf("m: "BYTE_TO_BINARY_PATTERN" "BYTE_TO_BINARY_PATTERN"\n",
  BYTE_TO_BINARY(m>>8), BYTE_TO_BINARY(m));

Sie benötigen leider alle zusätzlichen Angebote. Dieser Ansatz BYTE_TO_BINARYbirgt die Effizienzrisiken von Makros (geben Sie keine Funktion als Argument an ), vermeidet jedoch die Speicherprobleme und Mehrfachaufrufe von strcat in einigen anderen Vorschlägen.

William Whyte
quelle
13
Und hat auch den Vorteil, mehrfach aufrufbar zu sein, printfwas diejenigen mit staticPuffern nicht können.
Patrick Schlüter
4
Ich habe mir die Freiheit genommen, um die Änderung %dzu %c, weil es noch schneller sein sollte ( %dhat Ziffern-> char - Konvertierung, während %ceinfach das Argument ausgibt
3
Veröffentlichte eine erweiterte Version dieses Makros mit 16, 32, 64-Bit-Int-Unterstützung: stackoverflow.com/a/25108449/432509
ideasman42
2
Beachten Sie, dass dieser Ansatz nicht stapelfreundlich ist. Angenommen, intauf dem System sind 32 Bit vorhanden, erfordert das Drucken eines einzelnen 32-Bit-Werts Platz für 32 * 4-Byte-Werte. insgesamt 128 Bytes. Was je nach Stapelgröße ein Problem sein kann oder nicht.
user694733
1
Es ist wichtig, Klammern um das Byte im Makro einzufügen, da sonst beim Senden einer Operation BYTE_TO_BINARY (a | b) -> a | Probleme auftreten können b & 0x01! = (a | b) & 0x01
Ivan Hoffmann
203

Binär für jeden Datentyp drucken

//assumes little endian
void printBits(size_t const size, void const * const ptr)
{
    unsigned char *b = (unsigned char*) ptr;
    unsigned char byte;
    int i, j;

    for (i=size-1;i>=0;i--)
    {
        for (j=7;j>=0;j--)
        {
            byte = (b[i] >> j) & 1;
            printf("%u", byte);
        }
    }
    puts("");
}

Prüfung

int main(int argv, char* argc[])
{
        int i = 23;
        uint ui = UINT_MAX;
        float f = 23.45f;
        printBits(sizeof(i), &i);
        printBits(sizeof(ui), &ui);
        printBits(sizeof(f), &f);
        return 0;
}
Criptychon steht bei Monica
quelle
8
Schlagen Sie size_t i; for (i=size; i-- > 0; )zu vermeiden size_tvs. intmis-match.
chux
1
Könnte jemand bitte die Logik hinter diesem Code näher erläutern?
jII
2
Nehmen Sie jedes Byte in ptr(äußere Schleife); Maskieren Sie dann für jedes Bit das aktuelle Byte (innere Schleife) das Byte durch das aktuelle Bit ( 1 << j). Verschieben Sie dieses Recht nach rechts, was zu einem Byte führt, das 0 ( 0000 0000b) oder 1 ( 0000 0001b) enthält. Drucken Sie das resultierende Byte printf mit Format %u. HTH.
Nielsbot
1
@ ZX9 Beachten Sie, dass der vorgeschlagene Code verwendet >mit size_tund nicht die >=von Ihrem Kommentar zu bestimmen , wann die Schleife zu beenden.
chux
3
@ ZX9 Immer noch ein nützlicher Originalkommentar von Ihnen, da Codierer angesichts der Verwendung von >und >=mit vorzeichenlosen Typen in Randfällen vorsichtig sein müssen . 0ist ein vorzeichenloser Randfall und tritt häufig auf, im Gegensatz zu vorzeichenbehafteter Mathematik mit weniger häufigem INT_MAX/INT_MIN.
chux
151

Hier ist ein kurzer Hack, um Techniken zu demonstrieren, mit denen Sie das tun können, was Sie wollen.

#include <stdio.h>      /* printf */
#include <string.h>     /* strcat */
#include <stdlib.h>     /* strtol */

const char *byte_to_binary
(
    int x
)
{
    static char b[9];
    b[0] = '\0';

    int z;
    for (z = 128; z > 0; z >>= 1)
    {
        strcat(b, ((x & z) == z) ? "1" : "0");
    }

    return b;
}

int main
(
    void
)
{
    {
        /* binary string to int */

        char *tmp;
        char *b = "0101";

        printf("%d\n", strtol(b, &tmp, 2));
    }

    {
        /* byte to binary string */

        printf("%s\n", byte_to_binary(5));
    }

    return 0;
}
EvilTeach
quelle
2
Dies ist sicherlich weniger "seltsam" als das benutzerdefinierte Schreiben einer Escape-Überladung für printf. Es ist auch für einen Entwickler, der neu im Code ist, einfach zu verstehen.
Furious Coder
43
Einige Änderungen: strcatDies ist eine ineffiziente Methode zum Hinzufügen eines einzelnen Zeichens zur Zeichenfolge bei jedem Durchlauf der Schleife. Fügen Sie stattdessen ein hinzu char *p = b;und ersetzen Sie die innere Schleife durch *p++ = (x & z) ? '1' : '0'. zsollte bei 128 (2 ^ 7) statt 256 (2 ^ 8) beginnen. Erwägen Sie eine Aktualisierung, um einen Zeiger auf den zu verwendenden Puffer (zur Thread-Sicherheit) zu verwenden, ähnlich wie inet_ntoa().
Tomlogic
3
@ EvilTeach: Sie verwenden selbst einen ternären Operator als Parameter für strcat()! Ich bin damit einverstanden, dass dies strcatwahrscheinlich einfacher zu verstehen ist als das Nachinkrementieren eines dereferenzierten Zeigers für die Zuweisung, aber selbst Anfänger müssen wissen, wie sie die Standardbibliothek richtig verwenden. Vielleicht wäre die Verwendung eines indizierten Arrays für die Zuweisung eine gute Demonstration gewesen (und funktioniert tatsächlich, da sie bnicht jedes Mal, wenn Sie die Funktion aufrufen, auf Null gesetzt wird).
Tomlogic
3
Zufällig: Das binäre Pufferzeichen ist statisch und wird in der Zuweisung auf alle Nullen gelöscht. Dies löscht es nur beim ersten Ausführen und danach nicht mehr, sondern verwendet stattdessen den letzten Wert.
Markwatson
8
Außerdem sollte dies dokumentieren, dass das vorherige Ergebnis nach erneutem Aufrufen der Funktion ungültig ist, sodass Aufrufer nicht versuchen sollten, es wie folgt zu verwenden : printf("%s + %s = %s", byte_to_binary(3), byte_to_binary(4), byte_to_binary(3+4)).
Paŭlo Ebermann
84

Normalerweise gibt es in glibc keinen binären Konvertierungsspezifizierer.

In glibc können der Funktionsfamilie printf () benutzerdefinierte Konvertierungstypen hinzugefügt werden. Siehe register_printf_function für Details. Sie können eine benutzerdefinierte% b-Konvertierung für Ihren eigenen Gebrauch hinzufügen, wenn dies den Anwendungscode vereinfacht, damit er verfügbar ist.

Hier ist ein Beispiel für die Implementierung eines benutzerdefinierten Druckformats in glibc.

DGentry
quelle
Ich habe immer mein eigenes v [snf] printf () für die begrenzten Fälle geschrieben, in denen ich verschiedene Radixe haben wollte: Ich bin so froh, dass ich darüber gestöbert habe.
Jamie
3
warning: 'register_printf_function' is deprecated [-Wdeprecated-declarations]Es gibt jedoch eine neue Funktion, um dasselbe zu tun : register_printf_specifier(). Ein Beispiel für die neue Nutzung finden Sie hier: codereview.stackexchange.com/q/219994/200418
Cacahuete Frito
47

Sie können einen kleinen Tisch verwenden, um die Geschwindigkeit 1 zu verbessern . Ähnliche Techniken sind in der eingebetteten Welt nützlich, um beispielsweise ein Byte zu invertieren:

const char *bit_rep[16] = {
    [ 0] = "0000", [ 1] = "0001", [ 2] = "0010", [ 3] = "0011",
    [ 4] = "0100", [ 5] = "0101", [ 6] = "0110", [ 7] = "0111",
    [ 8] = "1000", [ 9] = "1001", [10] = "1010", [11] = "1011",
    [12] = "1100", [13] = "1101", [14] = "1110", [15] = "1111",
};

void print_byte(uint8_t byte)
{
    printf("%s%s", bit_rep[byte >> 4], bit_rep[byte & 0x0F]);
}

1 Ich beziehe mich hauptsächlich auf eingebettete Anwendungen, bei denen Optimierer nicht so aggressiv sind und der Geschwindigkeitsunterschied sichtbar ist.

Shahbaz
quelle
27

Drucken Sie das niedrigstwertige Bit und verschieben Sie es nach rechts. Wenn Sie dies tun, bis die Ganzzahl Null wird, wird die Binärdarstellung ohne führende Nullen in umgekehrter Reihenfolge gedruckt. Mit der Rekursion kann die Reihenfolge ganz einfach korrigiert werden.

#include <stdio.h>

void print_binary(int number)
{
    if (number) {
        print_binary(number >> 1);
        putc((number & 1) ? '1' : '0', stdout);
    }
}

Für mich ist dies eine der saubersten Lösungen für das Problem. Wenn Sie ein 0bPräfix und ein nachfolgendes neues Zeilenzeichen mögen , empfehle ich, die Funktion einzuschließen.

Online-Demo

Danijar
quelle
Fehler: Zu wenige Argumente für Funktionsaufruf, erwartete 2, haben 1 putc ((Nummer & 1)? '1': '0');
Koray Tugay
@KorayTugay Danke, dass du darauf hingewiesen hast. Ich habe den Funktionsaufruf korrigiert und eine Demo hinzugefügt.
Danijar
6
Sie sollten auch eine vorzeichenlose int-Nummer verwenden, da die Funktion bei einer negativen Nummer einen endlosen rekursiven Aufruf ausführt.
Puffy
Effizienterer Ansatz, da in ASCII '0' + 1 = '1':putc('0'+(number&1), stdout);
Roger Dueck
22

Basierend auf @William Whyte Antwort, das ist ein Makro , das bietet int8, 16, 32& 64Versionen, die Wiederverwendung von INT8Makro um Wiederholungen zu vermeiden.

/* --- PRINTF_BYTE_TO_BINARY macro's --- */
#define PRINTF_BINARY_PATTERN_INT8 "%c%c%c%c%c%c%c%c"
#define PRINTF_BYTE_TO_BINARY_INT8(i)    \
    (((i) & 0x80ll) ? '1' : '0'), \
    (((i) & 0x40ll) ? '1' : '0'), \
    (((i) & 0x20ll) ? '1' : '0'), \
    (((i) & 0x10ll) ? '1' : '0'), \
    (((i) & 0x08ll) ? '1' : '0'), \
    (((i) & 0x04ll) ? '1' : '0'), \
    (((i) & 0x02ll) ? '1' : '0'), \
    (((i) & 0x01ll) ? '1' : '0')

#define PRINTF_BINARY_PATTERN_INT16 \
    PRINTF_BINARY_PATTERN_INT8              PRINTF_BINARY_PATTERN_INT8
#define PRINTF_BYTE_TO_BINARY_INT16(i) \
    PRINTF_BYTE_TO_BINARY_INT8((i) >> 8),   PRINTF_BYTE_TO_BINARY_INT8(i)
#define PRINTF_BINARY_PATTERN_INT32 \
    PRINTF_BINARY_PATTERN_INT16             PRINTF_BINARY_PATTERN_INT16
#define PRINTF_BYTE_TO_BINARY_INT32(i) \
    PRINTF_BYTE_TO_BINARY_INT16((i) >> 16), PRINTF_BYTE_TO_BINARY_INT16(i)
#define PRINTF_BINARY_PATTERN_INT64    \
    PRINTF_BINARY_PATTERN_INT32             PRINTF_BINARY_PATTERN_INT32
#define PRINTF_BYTE_TO_BINARY_INT64(i) \
    PRINTF_BYTE_TO_BINARY_INT32((i) >> 32), PRINTF_BYTE_TO_BINARY_INT32(i)
/* --- end macros --- */

#include <stdio.h>
int main() {
    long long int flag = 1648646756487983144ll;
    printf("My Flag "
           PRINTF_BINARY_PATTERN_INT64 "\n",
           PRINTF_BYTE_TO_BINARY_INT64(flag));
    return 0;
}

Dies gibt aus:

My Flag 0001011011100001001010110111110101111000100100001111000000101000

Zur besseren Lesbarkeit möchten Sie möglicherweise ein Trennzeichen hinzufügen, z.

My Flag 00010110,11100001,00101011,01111101,01111000,10010000,11110000,00101000
ideasman42
quelle
Das ist ausgezeichnet. Gibt es einen bestimmten Grund für das Drucken der Bits, beginnend mit den am wenigsten signifikanten Bits?
Gaganso
2
Wie würden Sie das Hinzufügen des Kommas empfehlen?
nmz787
Fügt eine gruppierte Version von PRINTF_BYTE_TO_BINARY_INT#Definitionen hinzu, die optional verwendet werden soll.
ideasman42
16

Hier ist eine Version der Funktion, die nicht unter Wiedereintrittsproblemen oder Einschränkungen der Größe / des Typs des Arguments leidet:

#define FMT_BUF_SIZE (CHAR_BIT*sizeof(uintmax_t)+1)
char *binary_fmt(uintmax_t x, char buf[static FMT_BUF_SIZE])
{
    char *s = buf + FMT_BUF_SIZE;
    *--s = 0;
    if (!x) *--s = '0';
    for(; x; x/=2) *--s = '0' + x%2;
    return s;
}

Beachten Sie, dass dieser Code für jede Basis zwischen 2 und 10 genauso gut funktioniert, wenn Sie nur die 2 durch die gewünschte Basis ersetzen. Verwendung ist:

char tmp[FMT_BUF_SIZE];
printf("%s\n", binary_fmt(x, tmp));

Wo xist ein integraler Ausdruck?

R .. GitHub HÖREN SIE AUF, EIS ZU HELFEN
quelle
7
Ja, das kannst du machen. Aber es ist wirklich schlechtes Design. Selbst wenn Sie keine Threads oder Wiedereintritte haben, muss der Aufrufer wissen, dass der statische Puffer wiederverwendet wird und dass solche Dinge char *a = binary_fmt(x), *b = binary_fmt(y);nicht wie erwartet funktionieren. Wenn der Aufrufer gezwungen wird, einen Puffer zu übergeben, wird die Speicheranforderung explizit. Dem Aufrufer steht es natürlich frei, einen statischen Puffer zu verwenden, wenn dies wirklich gewünscht wird, und dann wird die Wiederverwendung desselben Puffers explizit. Beachten Sie auch, dass bei modernen PIC-ABIs statische Puffer normalerweise mehr Code für den Zugriff kosten als Puffer auf dem Stapel.
R .. GitHub STOP HELPING ICE
8
Das ist immer noch ein schlechtes Design. In diesen Fällen ist ein zusätzlicher Kopierschritt erforderlich, und es ist nicht weniger teuer, als wenn der Anrufer den Puffer bereitstellt, selbst wenn kein Kopieren erforderlich wäre. Die Verwendung von statischem Speicher ist nur eine schlechte Redewendung.
R .. GitHub STOP HELPING ICE
3
Sie müssen den Namespace der Präprozessor- oder Variablensymboltabelle mit einem unnötigen zusätzlichen Namen verschmutzen, der verwendet werden muss, um den von jedem Anrufer zugewiesenen Speicher richtig zu dimensionieren , und jeden Anrufer dazu zwingen, diesen Wert zu kennen und die erforderliche Menge von zuzuweisen Speicher ist ein schlechtes Design, wenn die einfachere funktionslokale Speicherlösung für die meisten Absichten und Zwecke ausreicht und wenn ein einfacher Aufruf von strdup () 99% der restlichen Verwendungen abdeckt.
Greg A. Woods
5
Hier müssen wir nicht zustimmen. Ich kann nicht sehen, wie das Hinzufügen eines unauffälligen Präprozessorsymbols der Schädlichkeit nahe kommt, die Anwendungsfälle stark einzuschränken, die Schnittstelle fehleranfällig zu machen, permanenten Speicher für die Dauer des Programms für einen temporären Wert zu reservieren und bei den meisten schlechteren Code zu generieren moderne Plattformen.
R .. GitHub STOP HELPING ICE
5
Ich befürworte keine Mikrooptimierung ohne Grund (dh Messungen). Ich denke jedoch, dass Leistung, auch wenn sie auf der Mikrogewinnskala liegt, erwähnenswert ist, wenn sie als Bonus zusammen mit einem grundlegend überlegenen Design angeboten wird.
R .. GitHub STOP HELPING ICE
13
const char* byte_to_binary( int x )
{
    static char b[sizeof(int)*8+1] = {0};
    int y;
    long long z;
    for (z=1LL<<sizeof(int)*8-1,y=0; z>0; z>>=1,y++)
    {
        b[y] = ( ((x & z) == z) ? '1' : '0');
    }

    b[y] = 0;

    return b;
}
DanSkeel
quelle
6
Klarer, wenn Sie '1'und '0'anstelle von 49und 48in Ihrem ternären verwenden. Sollte außerdem b9 Zeichen lang sein, damit das letzte Zeichen ein Nullterminator bleiben kann.
Tomlogic
Auch B muss jedes Mal initialisiert werden.
EvilTeach
2
Nicht, wenn Sie einige ändern: 1. Platz für eine endgültige Null hinzufügen: static char b[9] = {0}2. Deklaration aus der Schleife verschieben: int z,y;3. Endgültige Null hinzufügen : b[y] = 0. Auf diese Weise ist keine Reinitalisierung erforderlich.
Kobor42
1
Schöne Lösung. Ich würde allerdings ein paar Sachen ändern. Dh in der Zeichenfolge rückwärts gehen, damit Eingaben beliebiger Größe ordnungsgemäß verarbeitet werden können.
Kobor42
Alle diese 8s sollten durch ersetzt werden CHAR_BIT.
Alk
12

Schnelle und einfache Lösung:

void printbits(my_integer_type x)
{
    for(int i=sizeof(x)<<3; i; i--)
        putchar('0'+((x>>(i-1))&1));
}

Funktioniert für alle Größen und für signierte und nicht signierte Ints. Das '& 1' wird benötigt, um vorzeichenbehaftete Ints zu verarbeiten, da die Verschiebung möglicherweise eine Vorzeichenerweiterung bewirkt.

Es gibt so viele Möglichkeiten, dies zu tun. Hier ist eine supereinfache Methode zum Drucken von 32 Bit oder n Bits von einem vorzeichenbehafteten oder vorzeichenlosen 32-Bit-Typ (kein Negativ setzen, wenn signiert, nur Drucken der tatsächlichen Bits) und ohne Wagenrücklauf. Beachten Sie, dass i vor der Bitverschiebung dekrementiert wird:

#define printbits_n(x,n) for (int i=n;i;i--,putchar('0'|(x>>i)&1))
#define printbits_32(x) printbits_n(x,32)

Was ist mit der Rückgabe einer Zeichenfolge mit den Bits, die später gespeichert oder gedruckt werden sollen? Sie können den Speicher entweder zuweisen und zurückgeben, und der Benutzer muss ihn freigeben, oder Sie geben eine statische Zeichenfolge zurück, die jedoch beim erneuten Aufruf oder durch einen anderen Thread überlastet wird. Beide Methoden gezeigt:

char *int_to_bitstring_alloc(int x, int count)
{
    count = count<1 ? sizeof(x)*8 : count;
    char *pstr = malloc(count+1);
    for(int i = 0; i<count; i++)
        pstr[i] = '0' | ((x>>(count-1-i))&1);
    pstr[count]=0;
    return pstr;
}

#define BITSIZEOF(x)    (sizeof(x)*8)

char *int_to_bitstring_static(int x, int count)
{
    static char bitbuf[BITSIZEOF(x)+1];
    count = (count<1 || count>BITSIZEOF(x)) ? BITSIZEOF(x) : count;
    for(int i = 0; i<count; i++)
        bitbuf[i] = '0' | ((x>>(count-1-i))&1);
    bitbuf[count]=0;
    return bitbuf;
}

Rufen Sie an mit:

// memory allocated string returned which needs to be freed
char *pstr = int_to_bitstring_alloc(0x97e50ae6, 17);
printf("bits = 0b%s\n", pstr);
free(pstr);

// no free needed but you need to copy the string to save it somewhere else
char *pstr2 = int_to_bitstring_static(0x97e50ae6, 17);
printf("bits = 0b%s\n", pstr2);
Robotbugs
quelle
10

Keine der zuvor veröffentlichten Antworten ist genau das, wonach ich gesucht habe, also habe ich eine geschrieben. Es ist super einfach,% B mit dem zu verwenden printf!

    /*
     * File:   main.c
     * Author: Techplex.Engineer
     *
     * Created on February 14, 2012, 9:16 PM
     */

    #include <stdio.h>
    #include <stdlib.h>
    #include <printf.h>
    #include <math.h>
    #include <string.h>


    static int printf_arginfo_M(const struct printf_info *info, size_t n, int *argtypes) {
        /* "%M" always takes one argument, a pointer to uint8_t[6]. */
        if (n > 0) {
            argtypes[0] = PA_POINTER;
        }
        return 1;
    } /* printf_arginfo_M */

    static int printf_output_M(FILE *stream, const struct printf_info *info, const void *const *args) {
        int value = 0;
        int len;

        value = *(int **) (args[0]);

        //Beginning of my code ------------------------------------------------------------
        char buffer [50] = ""; //Is this bad?
        char buffer2 [50] = ""; //Is this bad?
        int bits = info->width;
        if (bits <= 0)
            bits = 8; // Default to 8 bits

        int mask = pow(2, bits - 1);
        while (mask > 0) {
            sprintf(buffer, "%s", (((value & mask) > 0) ? "1" : "0"));
            strcat(buffer2, buffer);
            mask >>= 1;
        }
        strcat(buffer2, "\n");
        // End of my code --------------------------------------------------------------
        len = fprintf(stream, "%s", buffer2);
        return len;
    } /* printf_output_M */

    int main(int argc, char** argv) {

        register_printf_specifier('B', printf_output_M, printf_arginfo_M);

        printf("%4B\n", 65);

        return (EXIT_SUCCESS);
    }
TechplexEngineer
quelle
1
Wird dies mit mehr als 50 Bit überlaufen?
Janus Troelsen
Guter Anruf, ja, das wird es ... Mir wurde gesagt, ich müsse Malloc verwenden, jemals das anziehen?
TechplexEngineer
ja natürlich. super einfach:char* buffer = (char*) malloc(sizeof(char) * 50);
Janus Troelsen
@ JanusTroelsen oder viel sauberer, kleiner, wartbar:char *buffer = malloc(sizeof(*buffer) * 50);
Shahbaz
Warum sollte sich "% B" in dieser Hinsicht von "% b" unterscheiden? Frühere Antworten besagten Dinge wie "Es gibt keine Formatierungsfunktion in der C-Standardbibliothek, um solche Binärdateien auszugeben." und " Einige Laufzeiten unterstützen"% b ", obwohl dies kein Standard ist." .
Peter Mortensen
8

Einige Laufzeiten unterstützen "% b", obwohl dies kein Standard ist.

Siehe auch hier für eine interessante Diskussion:

http://bytes.com/forum/thread591027.html

HTH

Rlerallut
quelle
1
Dies ist eigentlich eine Eigenschaft der C-Laufzeitbibliothek, nicht des Compilers.
cjm
7

Dieser Code sollte Ihre Anforderungen bis zu 64 Bit erfüllen. Ich habe 2 Funktionen pBin & pBinFill erstellt. Beide machen dasselbe, aber pBinFill füllt die führenden Felder mit fillChar aus. Die Testfunktion generiert einige Testdaten und druckt sie dann mit der Funktion aus.



char* pBinFill(long int x,char *so, char fillChar); // version with fill
char* pBin(long int x, char *so);                   // version without fill
#define kDisplayWidth 64

char* pBin(long int x,char *so)
{
 char s[kDisplayWidth+1];
 int  i=kDisplayWidth;
 s[i--]=0x00;   // terminate string
 do
 { // fill in array from right to left
  s[i--]=(x & 1) ? '1':'0';  // determine bit
  x>>=1;  // shift right 1 bit
 } while( x > 0);
 i++;   // point to last valid character
 sprintf(so,"%s",s+i); // stick it in the temp string string
 return so;
}

char* pBinFill(long int x,char *so, char fillChar)
{ // fill in array from right to left
 char s[kDisplayWidth+1];
 int  i=kDisplayWidth;
 s[i--]=0x00;   // terminate string
 do
 { // fill in array from right to left
  s[i--]=(x & 1) ? '1':'0';
  x>>=1;  // shift right 1 bit
 } while( x > 0);
 while(i>=0) s[i--]=fillChar;    // fill with fillChar 
 sprintf(so,"%s",s);
 return so;
}

void test()
{
 char so[kDisplayWidth+1]; // working buffer for pBin
 long int val=1;
 do
 {
   printf("%ld =\t\t%#lx =\t\t0b%s\n",val,val,pBinFill(val,so,'0'));
   val*=11; // generate test data
 } while (val < 100000000);
}

Output:
00000001 =  0x000001 =  0b00000000000000000000000000000001
00000011 =  0x00000b =  0b00000000000000000000000000001011
00000121 =  0x000079 =  0b00000000000000000000000001111001
00001331 =  0x000533 =  0b00000000000000000000010100110011
00014641 =  0x003931 =  0b00000000000000000011100100110001
00161051 =  0x02751b =  0b00000000000000100111010100011011
01771561 =  0x1b0829 =  0b00000000000110110000100000101001
19487171 = 0x12959c3 =  0b00000001001010010101100111000011
mrwes
quelle
1
"#define width 64" steht in Konflikt mit stream.h von log4cxx. Bitte verwenden Sie konventionell zufällig definierte Namen :)
Kagali-San
5
@mhambra: Sie sollten log4cxx für die Verwendung eines generischen Namens wie widthstattdessen ausschalten !
u0b34a0f6ae
7

Gibt es einen printf-Konverter zum Drucken im Binärformat?

Die printf()Familie kann nur in Basis 8, 10 und 16 direkt mit den Standardspezifizierern drucken. Ich schlage vor, eine Funktion zu erstellen, die die Zahl gemäß den besonderen Anforderungen des Codes in eine Zeichenfolge konvertiert.


Zum Drucken in einer beliebigen Basis [2-36]

Alle anderen Antworten haben bisher mindestens eine dieser Einschränkungen.

  1. Verwenden Sie den statischen Speicher für den Rückgabepuffer. Dies begrenzt die Häufigkeit, mit der die Funktion als Argument verwendet werden kann printf().

  2. Ordnen Sie Speicher, für den der aufrufende Code erforderlich ist, freien Zeigern zu.

  3. Der aufrufende Code muss explizit einen geeigneten Puffer bereitstellen.

  4. Rufen Sie printf()direkt an. Dies verpflichtet eine neue Funktion zu fprintf(), sprintf(), vsprintf()etc.

  5. Verwenden Sie einen reduzierten ganzzahligen Bereich.

Das Folgende hat keine der oben genannten Einschränkungen . Es erfordert C99 oder höher und die Verwendung von "%s". Es verwendet ein zusammengesetztes Literal , um den Pufferraum bereitzustellen. Es hat keine Probleme mit mehreren Anrufen in einem printf().

#include <assert.h>
#include <limits.h>
#define TO_BASE_N (sizeof(unsigned)*CHAR_BIT + 1)

//                               v. compound literal .v
#define TO_BASE(x, b) my_to_base((char [TO_BASE_N]){""}, (x), (b))

// Tailor the details of the conversion function as needed
// This one does not display unneeded leading zeros
// Use return value, not `buf`
char *my_to_base(char *buf, unsigned i, int base) {
  assert(base >= 2 && base <= 36);
  char *s = &buf[TO_BASE_N - 1];
  *s = '\0';
  do {
    s--;
    *s = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[i % base];
    i /= base;
  } while (i);

  // Could employ memmove here to move the used buffer to the beginning

  return s;
}

#include <stdio.h>
int main(void) {
  int ip1 = 0x01020304;
  int ip2 = 0x05060708;
  printf("%s %s\n", TO_BASE(ip1, 16), TO_BASE(ip2, 16));
  printf("%s %s\n", TO_BASE(ip1, 2), TO_BASE(ip2, 2));
  puts(TO_BASE(ip1, 8));
  puts(TO_BASE(ip1, 36));
  return 0;
}

Ausgabe

1020304 5060708
1000000100000001100000100 101000001100000011100001000
100401404
A2F44
chux - Monica wieder einsetzen
quelle
Das ist sehr nützlich. Wissen Sie, wie man es in C ++ benutzt? Beim Kompilieren wird der Fehler "Schweregrad Code Beschreibung Projektdatei Zeilenunterdrückungsstatus Fehler C4576" generiert. Ein in Klammern stehender Typ gefolgt von einer Initialisiererliste ist eine nicht standardmäßige explizite Typkonvertierungssyntax. Hallo C: \ my_projects \ hello \ hello \ main.cpp 39 "
Nur ein Lernender
1
@Justalearner Dies generiert ein C ++, da bei Verwendung eines zusammengesetzten C-Feature- Literals, das nicht Teil von C ++ ist. Veröffentlichen Sie möglicherweise Ihre C ++ - Implementierung, die versucht, dasselbe zu tun - auch wenn sie unvollständig ist, ich bin sicher, dass Sie Hilfe erhalten werden -, solange Sie zuerst Ihren Versuch zeigen.
chux
6

Vielleicht ein bisschen OT, aber wenn Sie dies nur zum Debuggen benötigen, um einige von Ihnen ausgeführte Binäroperationen zu verstehen oder nachzuvollziehen, können Sie sich wcalc (einen einfachen Konsolenrechner) ansehen. Mit den Optionen -b erhalten Sie eine binäre Ausgabe.

z.B

$ wcalc -b "(256 | 3) & 0xff"
 = 0b11
Quinmars
quelle
1
es gibt zu wenig andere Optionen an dieser Front, ... ruby -e 'printf("%b\n", 0xabc)', dcgefolgt von 2ogefolgt von 0x123p, und so weiter.
Lindes
6

In der C-Standardbibliothek gibt es keine Formatierungsfunktion, um solche Binärdateien auszugeben. Alle Formatierungsvorgänge, die von der printf-Familie unterstützt werden, beziehen sich auf lesbaren Text.

Florian Bösch
quelle
5

Die folgende rekursive Funktion kann nützlich sein:

void bin(int n)
{
    /* Step 1 */
    if (n > 1)
        bin(n/2);
    /* Step 2 */
    printf("%d", n % 2);
}
kapil
quelle
7
Seien Sie vorsichtig, dies funktioniert nicht mit negativen ganzen Zahlen.
Anderson Freitas
4

Ich habe die Top-Lösung für Größe und C ++ optimiert und bin zu dieser Lösung gekommen:

inline std::string format_binary(unsigned int x)
{
    static char b[33];
    b[32] = '\0';

    for (int z = 0; z < 32; z++) {
        b[31-z] = ((x>>z) & 0x1) ? '1' : '0';
    }

    return b;
}
paniq
quelle
3
Wenn Sie dynamischen Speicher (durch std::string) verwenden möchten , können Sie das staticArray auch entfernen. Am einfachsten wäre es, einfach das staticQualifikationsmerkmal zu löschen und bdie Funktion lokal zu machen .
Shahbaz
((x>>z) & 0x01) + '0'ist ausreichend.
Jason C
4

Drucken Sie Bits von jedem Typ mit weniger Code und Ressourcen

Dieser Ansatz hat als Attribute:

  • Arbeitet mit Variablen und Literalen.
  • Iteriert nicht alle Bits, wenn dies nicht erforderlich ist.
  • Rufen Sie printf nur auf, wenn ein Byte vollständig ist (nicht unnötig für alle Bits).
  • Funktioniert für jeden Typ.
  • Funktioniert mit kleinen und großen Endianness (verwendet GCC #defines zur Überprüfung).
  • Verwendet typeof (), das nicht dem C-Standard entspricht, aber weitgehend definiert ist.
#include <stdio.h>
#include <stdint.h>
#include <string.h>

#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define for_endian(size) for (int i = 0; i < size; ++i)
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define for_endian(size) for (int i = size - 1; i >= 0; --i)
#else
#error "Endianness not detected"
#endif

#define printb(value)                                   \
({                                                      \
        typeof(value) _v = value;                       \
        __printb((typeof(_v) *) &_v, sizeof(_v));       \
})

void __printb(void *value, size_t size)
{
        uint8_t byte;
        size_t blen = sizeof(byte) * 8;
        uint8_t bits[blen + 1];

        bits[blen] = '\0';
        for_endian(size) {
                byte = ((uint8_t *) value)[i];
                memset(bits, '0', blen);
                for (int j = 0; byte && j < blen; ++j) {
                        if (byte & 0x80)
                                bits[j] = '1';
                        byte <<= 1;
                }
                printf("%s ", bits);
        }
        printf("\n");
}

int main(void)
{
        uint8_t c1 = 0xff, c2 = 0x44;
        uint8_t c3 = c1 + c2;

        printb(c1);
        printb((char) 0xff);
        printb((short) 0xff);
        printb(0xff);
        printb(c2);
        printb(0x44);
        printb(0x4411ff01);
        printb((uint16_t) c3);
        printf("\n");

        return 0;
}

Ausgabe

$ ./printb 
11111111 
11111111 
00000000 11111111 
00000000 00000000 00000000 11111111 
01000100 
00000000 00000000 00000000 01000100 
01000100 00010001 11111111 00000001 
00000000 01000011 

Ich habe einen anderen Ansatz ( bitprint.h ) verwendet, um eine Tabelle mit allen Bytes (als Bitfolgen) zu füllen und sie basierend auf dem Eingabe- / Indexbyte zu drucken. Ein Blick lohnt sich.

Geyslan G. Bem
quelle
4
void
print_binary(unsigned int n)
{
    unsigned int mask = 0;
    /* this grotesque hack creates a bit pattern 1000... */
    /* regardless of the size of an unsigned int */
    mask = ~mask ^ (~mask >> 1);

    for(; mask != 0; mask >>= 1) {
        putchar((n & mask) ? '1' : '0');
    }

}
малин чекуров
quelle
Oder addiere 0 oder 1 zum Zeichenwert von '0';) Kein Ternär erforderlich.
Eule
3

Ich mochte den Code von paniq, der statische Puffer ist eine gute Idee. Es schlägt jedoch fehl, wenn Sie mehrere Binärformate in einem einzigen printf () möchten, da immer derselbe Zeiger zurückgegeben und das Array überschrieben wird.

Hier ist ein Drop-In im C-Stil, das den Zeiger auf einen geteilten Puffer dreht.

char *
format_binary(unsigned int x)
{
    #define MAXLEN 8 // width of output format
    #define MAXCNT 4 // count per printf statement
    static char fmtbuf[(MAXLEN+1)*MAXCNT];
    static int count = 0;
    char *b;
    count = count % MAXCNT + 1;
    b = &fmtbuf[(MAXLEN+1)*count];
    b[MAXLEN] = '\0';
    for (int z = 0; z < MAXLEN; z++) { b[MAXLEN-1-z] = ((x>>z) & 0x1) ? '1' : '0'; }
    return b;
}
eMPee584
quelle
1
Sobald counterreicht MAXCNT - 1, würde das nächste Inkrement von countes MAXCNTanstelle von Null machen, was einen Zugriff außerhalb der Grenzen des Arrays verursachen würde. Du hättest es tun sollen count = (count + 1) % MAXCNT.
Shahbaz
1
Übrigens würde dies später einen Entwickler überraschen, der MAXCNT + 1Aufrufe dieser Funktion in einem einzigen verwendet printf. Wenn Sie die Option für mehr als eine Sache angeben möchten, machen Sie sie im Allgemeinen unendlich. Zahlen wie 4 können nur Probleme verursachen.
Shahbaz
3

Kein Standard und tragbarer Weg.

Einige Implementierungen bieten itoa () , aber es wird nicht in den meisten sein, und es hat eine etwas miese Schnittstelle. Der Code befindet sich jedoch hinter dem Link und sollte es Ihnen ermöglichen, Ihren eigenen Formatierer ziemlich einfach zu implementieren.

wnoise
quelle
3

Eine generische Konvertierung eines beliebigen Integraltyps in die binäre Zeichenfolgendarstellung unter Verwendung der Standardbibliothek :

#include <bitset>
MyIntegralType  num = 10;
print("%s\n",
    std::bitset<sizeof(num) * 8>(num).to_string().insert(0, "0b").c_str()
); // prints "0b1010\n"

Oder nur: std::cout << std::bitset<sizeof(num) * 8>(num);

luart
quelle
1
Das ist eine idiomatische Lösung für C ++, aber er fragte nach C.
Danijar
3

Meine Lösung:

long unsigned int i;
for(i = 0u; i < sizeof(integer) * CHAR_BIT; i++) {
    if(integer & LONG_MIN)
        printf("1");
    else
        printf("0");
    integer <<= 1;
}
printf("\n");
SarahGaidi
quelle
3

Basierend auf @ ideasman42 Vorschlag in seiner Antwort, das ist ein Makro , das bietet int8, 16, 32& 64Versionen, die Wiederverwendung den INT8Makro um Wiederholungen zu vermeiden.

/* --- PRINTF_BYTE_TO_BINARY macro's --- */
#define PRINTF_BINARY_SEPARATOR
#define PRINTF_BINARY_PATTERN_INT8 "%c%c%c%c%c%c%c%c"
#define PRINTF_BYTE_TO_BINARY_INT8(i)    \
    (((i) & 0x80ll) ? '1' : '0'), \
    (((i) & 0x40ll) ? '1' : '0'), \
    (((i) & 0x20ll) ? '1' : '0'), \
    (((i) & 0x10ll) ? '1' : '0'), \
    (((i) & 0x08ll) ? '1' : '0'), \
    (((i) & 0x04ll) ? '1' : '0'), \
    (((i) & 0x02ll) ? '1' : '0'), \
    (((i) & 0x01ll) ? '1' : '0')

#define PRINTF_BINARY_PATTERN_INT16 \
    PRINTF_BINARY_PATTERN_INT8               PRINTF_BINARY_SEPARATOR              PRINTF_BINARY_PATTERN_INT8
#define PRINTF_BYTE_TO_BINARY_INT16(i) \
    PRINTF_BYTE_TO_BINARY_INT8((i) >> 8),   PRINTF_BYTE_TO_BINARY_INT8(i)
#define PRINTF_BINARY_PATTERN_INT32 \
    PRINTF_BINARY_PATTERN_INT16              PRINTF_BINARY_SEPARATOR              PRINTF_BINARY_PATTERN_INT16
#define PRINTF_BYTE_TO_BINARY_INT32(i) \
    PRINTF_BYTE_TO_BINARY_INT16((i) >> 16), PRINTF_BYTE_TO_BINARY_INT16(i)
#define PRINTF_BINARY_PATTERN_INT64    \
    PRINTF_BINARY_PATTERN_INT32              PRINTF_BINARY_SEPARATOR              PRINTF_BINARY_PATTERN_INT32
#define PRINTF_BYTE_TO_BINARY_INT64(i) \
    PRINTF_BYTE_TO_BINARY_INT32((i) >> 32), PRINTF_BYTE_TO_BINARY_INT32(i)
/* --- end macros --- */

#include <stdio.h>
int main() {
    long long int flag = 1648646756487983144ll;
    printf("My Flag "
           PRINTF_BINARY_PATTERN_INT64 "\n",
           PRINTF_BYTE_TO_BINARY_INT64(flag));
    return 0;
}

Dies gibt aus:

My Flag 0001011011100001001010110111110101111000100100001111000000101000

Zur besseren Lesbarkeit können Sie Folgendes ändern: #define PRINTF_BINARY_SEPARATORzu #define PRINTF_BINARY_SEPARATOR ","oder#define PRINTF_BINARY_SEPARATOR " "

Dies wird Folgendes ausgeben:

My Flag 00010110,11100001,00101011,01111101,01111000,10010000,11110000,00101000

oder

My Flag 00010110 11100001 00101011 01111101 01111000 10010000 11110000 00101000
Et7f3XIV
quelle
danke, dass du diesen Code kopiert
hast
3

Verwenden:

char buffer [33];
itoa(value, buffer, 2);
printf("\nbinary: %s\n", buffer);

Weitere Informationen finden Sie unter Drucken von Binärzahlen über printf .

kapilddit
quelle
1
Eine frühere Antwort lautete : "Einige Implementierungen bieten itoa (), aber es wird in den meisten nicht sein" ?
Peter Mortensen
2
void print_ulong_bin(const unsigned long * const var, int bits) {
        int i;

        #if defined(__LP64__) || defined(_LP64)
                if( (bits > 64) || (bits <= 0) )
        #else
                if( (bits > 32) || (bits <= 0) )
        #endif
                return;

        for(i = 0; i < bits; i++) { 
                printf("%lu", (*var >> (bits - 1 - i)) & 0x01);
        }
}

sollte funktionieren - ungetestet.

olli
quelle
2
/* Convert an int to it's binary representation */

char *int2bin(int num, int pad)
{
 char *str = malloc(sizeof(char) * (pad+1));
  if (str) {
   str[pad]='\0';
   while (--pad>=0) {
    str[pad] = num & 1 ? '1' : '0';
    num >>= 1;
   }
  } else {
   return "";
  }
 return str;
}

/* example usage */

printf("The number 5 in binary is %s", int2bin(5, 4));
/* "The number 5 in binary is 0101" */
Ben Cordero
quelle
4
Das Bezahlen der Kosten für eine Mallocation beeinträchtigt die Leistung. Es ist unfreundlich, die Verantwortung für die Zerstörung des Puffers an den Anrufer weiterzugeben.
EvilTeach
2

Als nächstes wird Ihnen das Speicherlayout angezeigt:

#include <limits>
#include <iostream>
#include <string>

using namespace std;

template<class T> string binary_text(T dec, string byte_separator = " ") {
    char* pch = (char*)&dec;
    string res;
    for (int i = 0; i < sizeof(T); i++) {
        for (int j = 1; j < 8; j++) {
            res.append(pch[i] & 1 ? "1" : "0");
            pch[i] /= 2;
        }
        res.append(byte_separator);
    }
    return res;
}

int main() {
    cout << binary_text(5) << endl;
    cout << binary_text(.1) << endl;

    return 0;
}
Yola
quelle
Was meinst du mit "Weiter wird dir das Speicherlayout zeigen" ?
Peter Mortensen
2

Hier ist eine kleine Variation der paniq -Lösung, die Vorlagen verwendet, um das Drucken von 32- und 64-Bit-Ganzzahlen zu ermöglichen:

template<class T>
inline std::string format_binary(T x)
{
    char b[sizeof(T)*8+1] = {0};

    for (size_t z = 0; z < sizeof(T)*8; z++)
        b[sizeof(T)*8-1-z] = ((x>>z) & 0x1) ? '1' : '0';

    return std::string(b);
}

Und kann verwendet werden wie:

unsigned int value32 = 0x1e127ad;
printf( "  0x%x: %s\n", value32, format_binary(value32).c_str() );

unsigned long long value64 = 0x2e0b04ce0;
printf( "0x%llx: %s\n", value64, format_binary(value64).c_str() );

Hier ist das Ergebnis:

  0x1e127ad: 00000001111000010010011110101101
0x2e0b04ce0: 0000000000000000000000000000001011100000101100000100110011100000
Löwe
quelle