Czy istnieje konwerter printf do drukowania w formacie binarnym?

434

Mogę drukować za pomocą printf jako liczby szesnastkowej lub ósemkowej. Czy istnieje znacznik formatu do wydrukowania jako baza binarna lub dowolna?

Korzystam z gcc.

printf("%d %x %o\n", 10, 10, 10); //prints "10 A 12\n"
print("%b\n", 10); // prints "%b\n"
Brian
źródło
O ile mi wiadomo, nie można tego zrobić za pomocą printf. Możesz oczywiście napisać metodę pomocniczą, aby to osiągnąć, ale to nie brzmi jak kierunek, w którym chcesz iść.
Ian P
Nie ma predefiniowanego formatu dla tego. Musisz przekształcić go samodzielnie w ciąg, a następnie wydrukować ciąg.
rslite,
Szybkie wyszukiwanie Google przyniosło tę stronę z pewnymi informacjami, które mogą być przydatne: forums.macrumors.com/archive/index.php/t-165959.html
Ian P
12
Nie jako część ANSI Standard C Library - jeśli piszesz przenośny kod, najbezpieczniejszą metodą jest wyrzucenie własnego.
tomlogic
Jedno oświadczenie standardowe i ogólne (dla dowolnego typu Integral dowolnej długości) rozwiązania konwersji na ciąg binarny w C ++: stackoverflow.com/a/31660310/1814353
luart

Odpowiedzi:

266

Hacky, ale działa dla mnie:

#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));

Dla typów wielobajtowych

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

Niestety potrzebujesz wszystkich dodatkowych cytatów. Takie podejście wiąże się z ryzykiem wydajności makr (nie przekazuj funkcji jako argumentu BYTE_TO_BINARY), ale pozwala uniknąć problemów z pamięcią i wielokrotnych wywołań strcat w niektórych innych propozycjach tutaj.

William Whyte
źródło
13
Ma tę zaletę, że może być wielokrotnie wywoływana, w przeciwieństwie printfdo staticbuforów z buforami.
Patrick Schlüter
4
Wziąłem wolność, aby zmienić %dsię %c, ponieważ powinien być jeszcze szybszy ( %dmusi wykonać digit-> CHAr, podczas gdy %cpo prostu wyprowadza argument
3
Opublikowano rozszerzoną wersję tego makra z obsługą 16, 32, 64-bitowych int: stackoverflow.com/a/25108449/432509
ideasman42
2
Zauważ, że to podejście nie jest przyjazne stosom. Zakładając, że intw systemie jest 32 bity, wydrukowanie pojedynczej wartości 32-bitowej będzie wymagało miejsca na wartości 32 * 4 bajty; łącznie 128 bajtów. Co, w zależności od wielkości stosu, może, ale nie musi stanowić problemu.
user694733,
1
ważne jest, aby dodać nawiasy wokół bajtu w makrze; w przeciwnym razie mogą wystąpić problemy podczas wysyłania operacji BYTE_TO_BINARY (a | b) -> a | b & 0x01! = (a | b) i 0x01
Ivan Hoffmann
203

Drukuj plik binarny dla dowolnego typu danych

//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("");
}

test

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;
}
cryptych stoi z Moniką
źródło
8
Sugeruj, size_t i; for (i=size; i-- > 0; )aby unikać size_tkontra intniewłaściwe dopasowanie.
chux - Przywróć Monikę
1
Czy ktoś mógłby rozwinąć logikę tego kodu?
jII
2
Weź każdy bajt do ptr(zewnętrzna pętla); następnie dla każdego bitu bieżący bajt (pętla wewnętrzna) zamaskuj bajt bieżącym bitem ( 1 << j). Przesunięcie w prawo powoduje powstanie bajtu zawierającego 0 ( 0000 0000b) lub 1 ( 0000 0001b). Wydrukuj wynikowy bajt printf z formatem %u. HTH.
nielsbot
1
@ ZX9 Zauważ, że sugerowany kod został użyty >z komentarzem, size_ta nie >=z twojego komentarza, aby określić, kiedy zakończyć pętlę.
chux - Przywróć Monikę
3
@ ZX9 Nadal przydatny oryginalny komentarz, ponieważ koderzy muszą zachować ostrożność, biorąc pod uwagę użycie małych liter >i >=typów bez znaku. 0jest nieoznaczonym przypadkiem na krawędzi i często występuje, w przeciwieństwie do podpisanej matematyki z mniej powszechnym INT_MAX/INT_MIN.
chux - Przywróć Monikę
151

Oto szybki hack, aby zademonstrować techniki robienia tego, co chcesz.

#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
źródło
2
Jest to z pewnością mniej „dziwne” niż niestandardowe pisanie przeciążenia awaryjnego dla printf. Łatwo jest też zrozumieć programistę, który nie zna kodu.
Furious Coder
43
Kilka zmian: strcatjest nieefektywną metodą dodawania pojedynczego znaku do ciągu przy każdym przejściu pętli. Zamiast tego dodaj a char *p = b;i zamień wewnętrzną pętlę na *p++ = (x & z) ? '1' : '0'. zpowinien zaczynać się od 128 (2 ^ 7) zamiast 256 (2 ^ 8). Rozważ aktualizację, aby wziąć wskaźnik do bufora do użycia (dla bezpieczeństwa wątków), podobnie jak inet_ntoa().
tomlogic
3
@EvilTeach: Sam używasz operatora trójskładnikowego jako parametru strcat()! Zgadzam się, że strcatjest to prawdopodobnie łatwiejsze do zrozumienia niż zwiększanie wskaźnika do zadania z pominięciem odniesienia, ale nawet początkujący muszą wiedzieć, jak prawidłowo korzystać ze standardowej biblioteki. Być może użycie tablicy indeksowanej do przypisania byłoby dobrą demonstracją (i faktycznie będzie działać, ponieważ bnie resetuje się do zer zerowych przy każdym wywołaniu funkcji).
tomlogic,
3
Losowo: binarny znak bufora jest statyczny i jest kasowany do wszystkich zer w przypisaniu. Spowoduje to wyczyszczenie go tylko przy pierwszym uruchomieniu, a następnie nie zostanie wyczyszczone, ale zamiast tego użyje ostatniej wartości.
markwatson
8
Ponadto, należy to udokumentować, że poprzedni wynik będzie nieprawidłowy po ponownym wywołaniu funkcji, więc dzwoniący nie należy próbować używać go tak: printf("%s + %s = %s", byte_to_binary(3), byte_to_binary(4), byte_to_binary(3+4)).
Paŭlo Ebermann
84

Normalnie w glibc nie ma specyfikatora konwersji binarnej.

Możliwe jest dodanie niestandardowych typów konwersji do rodziny funkcji printf () w glibc. Aby uzyskać szczegółowe informacje, patrz register_printf_function . Możesz dodać niestandardową konwersję% b do własnego użytku, jeśli upraszcza to kod aplikacji, aby był dostępny.

Oto przykład implementacji niestandardowych formatów printf w glibc.

DGentry
źródło
Zawsze pisałem własny v [snf] printf () dla ograniczonych przypadków, w których chciałem różnych podstawników: tak się cieszę, że przejrzałem to.
Jamie,
3
warning: 'register_printf_function' is deprecated [-Wdeprecated-declarations]Jest to nowa funkcja, aby zrobić to samo, ale: register_printf_specifier(). Przykład nowego zastosowania można znaleźć tutaj: codereview.stackexchange.com/q/219994/200418
Cacahuete Frito
47

Możesz użyć małego stolika, aby poprawić prędkość 1 . Podobne techniki są przydatne w świecie osadzonym, na przykład do odwrócenia bajtu:

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 Mam na myśli głównie aplikacje wbudowane, w których optymalizatory nie są tak agresywne, a różnica prędkości jest widoczna.

Shahbaz
źródło
27

Wydrukuj najmniej znaczący bit i przesuń go w prawo. Robiąc to, dopóki liczba całkowita nie zmieni się w zero, drukuje reprezentację binarną bez zer wiodących, ale w odwrotnej kolejności. Za pomocą rekurencji kolejność można dość łatwo poprawić.

#include <stdio.h>

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

Dla mnie jest to jedno z najczystszych rozwiązań problemu. Jeśli podoba Ci się 0bprzedrostek i znak nowej linii, sugeruję zawinięcie funkcji.

Demo online

danijar
źródło
błąd: zbyt mało argumentów do wywołania funkcji, oczekiwane 2, mają 1 putc ((liczba i 1)? '1': '0');
Koray Tugay
@KorayTugay Dzięki za zwrócenie na to uwagi. Poprawiłem wywołanie funkcji i dodałem wersję demo.
danijar
6
powinieneś także użyć niepodpisanego numeru int, ponieważ gdy podany numer jest ujemny, funkcja wchodzi w niekończące się wywołanie rekurencyjne.
Puffy
Bardziej wydajne podejście, ponieważ w ASCII „0” + 1 = „1”:putc('0'+(number&1), stdout);
Roger Dueck
22

Na podstawie odpowiedzi @William Whyte, ten jest makro, które zapewnia int8, 16, 32i 64wersje, ponowne użycie INT8makra w celu uniknięcia powtórzeń.

/* --- 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;
}

To daje:

My Flag 0001011011100001001010110111110101111000100100001111000000101000

Aby zapewnić czytelność, możesz dodać separator dla np .:

My Flag 00010110,11100001,00101011,01111101,01111000,10010000,11110000,00101000
ideasman42
źródło
To jest doskonałe. Czy istnieje jakiś szczególny powód drukowania bitów zaczynających się od najmniej znaczących bitów?
gaganso
2
jak poleciłbyś dodać przecinek?
nmz787,
Dodałbym zgrupowaną wersję PRINTF_BYTE_TO_BINARY_INT#definicji do opcjonalnego użycia.
ideasman42
16

Oto wersja funkcji, która nie cierpi z powodu problemów z ponownym uruchomieniem lub ograniczeń rozmiaru / typu argumentu:

#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;
}

Zauważ, że ten kod będzie działał równie dobrze dla dowolnej bazy między 2 a 10, jeśli po prostu zastąpisz 2 przez pożądaną bazę. Zastosowanie to:

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

Gdzie xjest jakieś wyrażenie integralne.

R .. GitHub ZATRZYMAJ LÓD
źródło
7
Tak, możesz to zrobić. Ale to naprawdę zły projekt. Nawet jeśli nie masz wątków ani ponownego połączenia, osoba dzwoniąca musi być świadoma, że ​​bufor statyczny jest ponownie wykorzystywany i że takie rzeczy char *a = binary_fmt(x), *b = binary_fmt(y);nie będą działać zgodnie z oczekiwaniami. Zmuszenie osoby dzwoniącej do przekazania bufora powoduje, że wymagania dotyczące miejsca stają się oczywiste; osoba dzwoniąca może oczywiście użyć bufora statycznego, jeśli jest to naprawdę pożądane, a następnie ponowne użycie tego samego bufora staje się jawne. Zauważ również, że w nowoczesnych PIC ABI bufory statyczne zwykle kosztują więcej kodu niż dostęp do buforów na stosie.
R .. GitHub ZATRZYMAJ LÓD
8
To wciąż zły projekt. W takich przypadkach wymaga dodatkowego kroku kopiowania i nie jest mniej kosztowne niż udostępnienie bufora przez osobę dzwoniącą, nawet w przypadkach, w których kopiowanie nie byłoby wymagane. Używanie przechowywania statycznego to po prostu zły idiom.
R .. GitHub ZATRZYMAJ LÓD
3
Konieczność zanieczyszczenia przestrzeni nazw preprocesora lub tablicy symboli zmiennych niepotrzebną dodatkową nazwą, której należy użyć, aby właściwie określić rozmiar pamięci, która musi być przydzielona przez każdego dzwoniącego, i zmuszając każdego dzwoniącego do znajomości tej wartości oraz do przydzielenia niezbędnej ilości storage, jest złym projektem, gdy prostsze lokalne rozwiązanie do przechowywania wystarczy do większości celów i celów, a proste wywołanie strdup () obejmuje 99% pozostałych zastosowań.
Greg A. Woods
5
Tutaj będziemy musieli się nie zgodzić. Nie widzę, w jaki sposób dodanie jednego dyskretnego symbolu preprocesora zbliża się do szkodliwości poważnego ograniczenia przypadków użycia, powodowania podatności interfejsu, rezerwowania stałej pamięci na czas trwania programu na tymczasową wartość i generowania gorszego kodu na większości nowoczesne platformy.
R .. GitHub ZATRZYMAJ LÓD
5
Nie opowiadam się za mikrooptymalizacją bez powodu (tj. Pomiary). Ale myślę, że wydajność, nawet jeśli jest w skali mikro-wzmocnienia, jest warta wspomnienia, jeśli chodzi o bonus wraz z zasadniczo lepszym designem.
R .. GitHub ZATRZYMAJ LÓD
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
źródło
6
Wyraźniejsze, jeśli używasz '1'i '0'zamiast 49i 48w trójskładnikowego. Ponadto, bpowinien być długi 9 znaków więc ostatnia postać może pozostać null terminator.
tomlogic
Również B musi być inicjalizowane za każdym razem.
EvilTeach
2
Nie, jeśli zmienić pewne: 1. Dodać do ostatecznego miejsca zerowe: static char b[9] = {0}2. Oświadczenie wyjść z pętli: int z,y;3. Dodać ostateczną Zero b[y] = 0. W ten sposób nie jest wymagana ponowna inicjalizacja.
Kobor42,
1
Niezłe rozwiązanie. Chciałbym jednak coś zmienić. Tzn. Cofanie się łańcucha, aby wejście o dowolnym rozmiarze mogło być poprawnie obsługiwane.
Kobor42
Wszystkie te 8powinny zostać zastąpione przez CHAR_BIT.
alk
12

Szybkie i łatwe rozwiązanie:

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

Działa dla dowolnego typu rozmiaru i dla podpisanych i niepodpisanych int. „& 1” jest potrzebne do obsługi podpisanych znaków wewnętrznych, ponieważ przesunięcie może powodować rozszerzenie znaku.

Jest na to wiele sposobów. Oto bardzo prosty do drukowania 32 bitów lub n bitów z 32-bitowego typu ze znakiem lub bez znaku (nie wstawianie ujemnego znaku, tylko drukowanie rzeczywistych bitów) i bez powrotu karetki. Zauważ, że i jest zmniejszane przed przesunięciem bitów:

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

Co powiesz na zwrócenie ciągu z bitami do późniejszego zapisania lub wydrukowania? Możesz albo przydzielić pamięć i zwrócić ją, a użytkownik musi ją zwolnić, albo zwróci ciąg statyczny, ale zostanie zablokowany, jeśli zostanie ponownie wywołany lub przez inny wątek. Pokazano obie metody:

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;
}

Zadzwoń z:

// 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);
Roboty
źródło
10

Żadna z wcześniej opublikowanych odpowiedzi nie była dokładnie tym, czego szukałem, więc napisałem jedną. Jest bardzo prosty w użyciu% B z 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
źródło
1
czy to przepełni się więcej niż 50 bitami?
Janus Troelsen
Dobra rozmowa, tak to będzie ... Powiedziano mi, że muszę użyć Malloc, czy kiedykolwiek tego nie zrobiłeś?
TechplexEngineer
tak oczywiście. bardzo łatwe:char* buffer = (char*) malloc(sizeof(char) * 50);
Janus Troelsen
@JanusTroelsen, lub znacznie czystszy, mniejszy, możliwy do utrzymania:char *buffer = malloc(sizeof(*buffer) * 50);
Shahbaz
Dlaczego pod tym względem „% B” różni się od „% b”? Poprzednie odpowiedzi mówiły takie rzeczy, jak: „W standardowej bibliotece C nie ma funkcji formatowania, która wyprowadzałaby takie pliki binarne”. i „ Niektóre środowiska wykonawcze obsługują„% b ”, chociaż nie jest to standard.” .
Peter Mortensen,
8

Niektóre środowiska wykonawcze obsługują „% b”, chociaż nie jest to standard.

Zobacz także tutaj ciekawą dyskusję:

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

HTH

rlerallut
źródło
1
Jest to właściwie właściwość biblioteki wykonawczej C, a nie kompilatora.
cjm
7

Ten kod powinien obsłużyć Twoje potrzeby do 64 bitów. Stworzyłem 2 funkcje pBin i pBinFill. Oba robią to samo, ale pBinFill wypełnia wiodące spacje znakiem fillChar. Funkcja testowa generuje niektóre dane testowe, a następnie drukuje je za pomocą funkcji.



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
źródło
1
„#define width 64” powoduje konflikt ze stream.h z log4cxx. Proszę używać konwencjonalnie losowych definicji nazw :)
kagali-san 30.01.11
5
@mhambra: powinieneś poinformować log4cxx o używaniu tak ogólnej nazwy jak widthzamiast tego!
u0b34a0f6ae,
7

Czy istnieje konwerter printf do drukowania w formacie binarnym?

printf()Rodzina jest tylko w stanie drukować w bazie 8, 10 i 16, stosując bezpośrednio standardowe specyfikatorów. Sugeruję utworzenie funkcji, która konwertuje liczbę na ciąg według określonych potrzeb kodu.


Aby wydrukować w dowolnej bazie [2-36]

Wszystkie pozostałe odpowiedzi mają co najmniej jedno z tych ograniczeń.

  1. Użyj pamięci statycznej dla bufora powrotu. Ogranicza to liczbę przypadków użycia funkcji jako argumentu printf().

  2. Przydziel pamięć wymagającą kodu wywołującego, aby zwolnić wskaźniki.

  3. Wymagaj od kodu wywołującego jawnego udostępnienia odpowiedniego bufora.

  4. Zadzwoń printf()bezpośrednio. To zobowiązuje nową funkcję do fprintf(), sprintf(), vsprintf(), itd.

  5. Użyj zmniejszonego zakresu liczb całkowitych.

Poniższe nie ma żadnego z powyższych ograniczeń . Wymaga C99 lub nowszego i użycia "%s". Używa literału złożonego, aby zapewnić przestrzeń buforową. Nie ma problemu z wieloma połączeniami w 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;
}

Wynik

1020304 5060708
1000000100000001100000100 101000001100000011100001000
100401404
A2F44
chux - Przywróć Monikę
źródło
To jest bardzo przydatne. Czy wiesz, jak go używać w C ++? Podczas kompilacji generowany jest błąd „Kod istotności Opis Błąd stanu linii projektu Plik C4576 typ w nawiasach, po którym następuje lista inicjująca, jest niestandardową składnią konwersji typu jawnego witaj C: \ moje_projekty \ hello \ hello \ main.cpp 39 „
Tylko uczeń
1
@Justalearner To generuje C ++, ponieważ jeśli używa literału złożonego funkcji C , który nie jest częścią C ++. Być może opublikuj swoją implementację C ++, która próbuje zrobić to samo - nawet jeśli jest niekompletna, jestem pewien, że otrzymasz pomoc - pod warunkiem, że najpierw pokażesz swoją próbę.
chux - Przywróć Monikę
6

Może trochę OT, ale jeśli potrzebujesz tego tylko do debugowania, aby zrozumieć lub odtworzyć niektóre wykonywane operacje binarne, możesz rzucić okiem na wcalc (prosty kalkulator konsoli). Z opcjami -b otrzymujesz wyjście binarne.

na przykład

$ wcalc -b "(256 | 3) i 0xff"
 = 0b11
chinmary
źródło
1
istnieje kilka innych opcji na tym froncie, zbyt ... ruby -e 'printf("%b\n", 0xabc)', dcpo czym 2onastępuje 0x123p, i tak dalej.
Lindes
6

W standardowej bibliotece C nie ma funkcji formatowania, która wyprowadzałaby takie pliki binarne. Wszystkie operacje formatowania obsługiwane przez rodzinę printf są skierowane na tekst czytelny dla człowieka.

Florian Bösch
źródło
5

Przydatna może być następująca funkcja rekurencyjna:

void bin(int n)
{
    /* Step 1 */
    if (n > 1)
        bin(n/2);
    /* Step 2 */
    printf("%d", n % 2);
}
kapil
źródło
7
Uważaj, to nie działa z ujemnymi liczbami całkowitymi.
Anderson Freitas
4

Zoptymalizowałem najlepsze rozwiązanie dla rozmiaru i C ++ - i doszedłem do tego rozwiązania:

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
źródło
3
Jeśli chcesz użyć pamięci dynamicznej (poprzez std::string), równie dobrze możesz pozbyć się statictablicy. Najprostszym sposobem byłoby po prostu upuszczenie statickwalifikatora i uczynienie go blokalnym dla funkcji.
Shahbaz
((x>>z) & 0x01) + '0'jest wystarczający.
Jason C
4

Drukuj bity dowolnego typu, używając mniej kodu i zasobów

Podejście to ma następujące atrybuty:

  • Działa ze zmiennymi i literałami.
  • Nie iteruje wszystkich bitów, gdy nie jest to konieczne.
  • Wywołaj printf tylko po uzupełnieniu bajtu (nie niepotrzebnie dla wszystkich bitów).
  • Działa dla każdego typu.
  • Działa z małą i dużą endianowością (używa GCC #defines do sprawdzania).
  • Używa typeof (), który nie jest standardem C, ale jest w dużej mierze zdefiniowany.
#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;
}

Wynik

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

Użyłem innego podejścia ( bitprint.h ), aby wypełnić tabelę wszystkimi bajtami (jako ciągi bitów) i wydrukować je w oparciu o bajt wejściowy / indeksowy. Warto się przyjrzeć.

Geyslan G. Bem
źródło
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');
    }

}
малин чекуров
źródło
Lub dodaj 0 lub 1 do wartości znaku „0”;) Nie jest konieczne stosowanie trójki.
Sowa
3

Podobał mi się kod paniq, bufor statyczny to dobry pomysł. Jednak nie powiedzie się, jeśli chcesz wielu formatów binarnych w jednym printf (), ponieważ zawsze zwraca ten sam wskaźnik i zastępuje tablicę.

Oto lista rozwijana w stylu C, która obraca wskaźnik na podzielonym buforze.

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
źródło
1
Gdy countosiągnie MAXCNT - 1, następny przyrost countspowoduje, że MAXCNTzamiast zera, spowoduje to dostęp poza granice tablicy. Powinieneś był to zrobić count = (count + 1) % MAXCNT.
Shahbaz
1
Nawiasem mówiąc, byłoby to później niespodzianką dla programisty, który używa MAXCNT + 1wywołań tej funkcji w jednym printf. Ogólnie, jeśli chcesz dać opcję więcej niż 1 rzeczy, ustaw ją na nieskończoność. Liczby takie jak 4 mogą tylko powodować problemy.
Shahbaz
3

Brak standardowego i przenośnego sposobu.

Niektóre implementacje zapewniają itoa () , ale w większości nie będzie i ma nieco kiepski interfejs. Ale kod znajduje się za linkiem i powinien umożliwić łatwe wdrożenie własnego formatera.

wnoise
źródło
3

Jedna instrukcja ogólna konwersja dowolnego typu integralnego na reprezentację ciągu binarnego przy użyciu standardowej biblioteki:

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

Lub tylko: std::cout << std::bitset<sizeof(num) * 8>(num);

luart
źródło
1
To idiomatyczne rozwiązanie dla C ++, ale prosił o C.
danijar
3

Moje rozwiązanie:

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
źródło
3

Na podstawie sugestii użytkownika @ ideasman42 w jego odpowiedzi, jest to makro, który zapewnia int8, 16, 32i 64wersje, ponowne użycie INT8makra w celu uniknięcia powtórzeń.

/* --- 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;
}

To daje:

My Flag 0001011011100001001010110111110101111000100100001111000000101000

Dla czytelności możesz zmienić: #define PRINTF_BINARY_SEPARATORna#define PRINTF_BINARY_SEPARATOR "," lub#define PRINTF_BINARY_SEPARATOR " "

Spowoduje to:

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

lub

My Flag 00010110 11100001 00101011 01111101 01111000 10010000 11110000 00101000
Et7f3XIV
źródło
dziękuję za skopiowanie tego kodu, pierwszego do skopiowania w tym projekcie, nad którym pracuję, pisząc to czułem się jak żmudne zadanie :)
DevZer0
3

Posługiwać się:

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

Aby uzyskać więcej informacji, zobacz Jak wydrukować liczbę binarną za pomocą printf .

kapilddit
źródło
1
Poprzednia odpowiedź brzmiała: „Niektóre implementacje zapewniają itoa (), ale nie będzie w większości” ?
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);
        }
}

powinien działać - niesprawdzony.

olli
źródło
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
źródło
4
Płacenie kosztów mallokalizacji zaszkodzi wydajności. Przekazywanie dzwoniącemu odpowiedzialności za zniszczenie bufora jest niemiłe.
EvilTeach
2

Następnie pokaże ci układ pamięci:

#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
źródło
Co rozumiesz przez „Next pokaże ci układ pamięci” ?
Peter Mortensen,
2

Oto niewielka odmiana rozwiązania paniq , która wykorzystuje szablony do drukowania liczb całkowitych 32- i 64-bitowych:

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);
}

I może być używany jak:

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() );

Oto wynik:

  0x1e127ad: 00000001111000010010011110101101
0x2e0b04ce0: 0000000000000000000000000000001011100000101100000100110011100000
Lew
źródło