Binary H(e)art

binary heart Heute schrieb die ettercat einen Link auf nebenstehenden Comic von xkcd.com ins IRC. Die Seite hat mich schon des öfteren mit wirklich lustigen Comics begeistert, zumal einige nur mit entsprechendem Geek-Hintergrundwissen wirklich lustig sind. Nummer 99 – »binary heart« – trifft vom Untertitel der Seite (»A webcomic of romance,
sarcasm, math, and language.«) klar den romantischen Teil. Aber ganz ehrlich, seid Ihr nicht auch total neugierig, ob sich hinter den Nullen und Einsen im Bild nicht noch eine zusätzliche Botschaft verbirgt? Ich war es jedenfalls und da nur selbst dekodieren schlau macht, hab ich frei nach dem Perl-Motto »Es gibt mehr als einen Weg, etwas zu tun« die Zeichen einzeln abgetippt und ein kleines Skript geschrieben.

Hart (siehe Titel ;-)) war da dran einerseits das Abtippen vom Comic – man verrutscht schon sehr leicht in Zeile und Spalte – und andererseits um Mitternacht die eingerosteten Perl-Kenntnisse zu reaktivieren. Wer sich gern selbst an einem Progrämmchen in beliebiger Sprache probieren will, kann sich die binär codierte Nachricht runterladen, die Textdatei, wo ich mal die Einsen und Nullen abgetippt habe.

Ach und für die ganz Neugierigen oder zur Kontrolle des eigenen Programms hier noch die Auflösung:

iloveyOuilOveyouiloveyOuilOveyOuiloveyouilOveyouilOveyOuilOv

7 thoughts on “Binary H(e)art

  1. Vogi

    [[All the numbers are black except for a heart-shaped red section in the middle.]] / 011010010110110001101 / 111011101100110010101 / 111001010011110111010 / 101101001011011000100 / 111101110110011001010 / 111100101101111011101 / 010110100101101100011 / 011110111011001100101 / 011110010100111101110 / 101011010010110110001 / 001111011101100110010 / 101111001010011110111 / 010101101001011011000 / 101010110100101101100 / 010011110111011001100 / 101011110010110111101 / 110101011010010110110 / 001001111011101100110 / 010101111001010011110 / 111010101101001011011 / 000100111101110110011 / {{alt: i love you}}

    Reply
  2. LeSpocky

    Man kann sich natürlich maximal umständlich selbst bauen, was Perl schon direkt kann. Mit pack lässt sich das doch um einiges schneller lösen:

    my @buffer;
    my $string;
    my $fh = new IO::File;
    
    $fh->open("xkcd99_bin.txt") || die $1;
    @buffer = <$fh>;
    $fh->close;
    
    $string = join('', @buffer);
    $string =~ s/[^01]//g;
    
    while (length $string > 8) {
            print pack("B8", $string);
            $string = substr($string, 8);
    }
    print "n";
    
    Reply
  3. Tux
    undef $/;
    $_ = ;
    eval "s/".("(?:[.\n]*?([01]))"x8)."/$1$2$3$4$5$6$7$8 /g";
    print join '', map {pack "B8", $_} grep {length == 8} split ' ';
    print "n";
    

    Noch etwas kuerzer. O:)

    Die ersten beiden Zeilen lesen die Eingabedaten von der Standardeingabe (in einem Rutsch).

    Dann wird der String in Achtergruppen von [01] umgewandelt, alles andere wird ignoriert, diese Gruppen sind durch Leerzeichen getrennt.
    Das eval ist da nur drin, um beim regex nicht achtmal das Gleiche hinschreiben zu muessen.

    Nun nur noch an den Leerzeichen aufsplitten (split), alles, was kuerzer als acht Zeichen ist, weglassen (grep), dann die Binaercodes umwandeln (mit map auf der ganzen Liste) und wieder zusammenfuegen (join).

    Perl. :)

    Reply
  4. LeSpocky

    Soll das hier einfach oder obfuscated werden? :P

    #!/usr/bin/perl -w
    undef $/;
    my $s = <>;
    $s =~ s/[^01]//g;
    print pack("B*", substr($s, 0, 8*int length($s)/8) ) . "n";
    

    Damit kürze ich mit substr vorher noch den String auf ein Vielfaches von 8, den Rest macht pack allein. Die Frage ist allerdings, warum er bei

    print pack("B8" x int length($string)/8, $string);
    

    nur das erste ‘i’ ausgibt. Folgendes gibt auch nur das erste ‘i’ aus:

    print pack("B8B8B8", $string) . "n";
    

    Kann das jemand erklären?

    Reply
  5. LeSpocky
    eval "s/".("(?:[.\n]*?([01]))"x8)."/$1$2$3$4$5$6$7$8 /g";
    print join '', map {pack "B8", $_} grep {length == 8} split ' ';
    print "n";
    

    Wenn man nicht unter allen Umständen die Bereinigung des Eingabestrings und die Aufteilung in 8er-Gruppen in eine Zeile quetschen will, geht’s so etwas leichter verständlich:

    #!/usr/bin/perl -w
    undef $/;
    $_ = ;
    s/[^01]//g;
    s/([01]{8})/$1 /g;
    print join('', map {pack "B8", $_} grep {length == 8} split ' ') . "n";
    

    Und wenn man Klammern für join benutzt, kann man das Newline noch in die gleiche Zeile packen. 8-)

    Reply
  6. LeSpocky

    Die Frage ist allerdings, warum er bei

    print pack("B8" x int length($string)/8, $string);

    nur das erste ‘i’ ausgibt. Folgendes gibt auch nur das erste ‘i’ aus:

    print pack("B8B8B8", $string) . "n";

    Ein ‘B’ reicht und wenn man dem was größeres als 8, am besten was passendes mitgibt, klappt’s auch:

    undef $/;
    $s = ;
    $s =~ s/[^01]//g;
    $l = length($s)-length($s)%8;
    print pack("B[$l]", $s) . "n";
    

    Das nenn ich TIMTOWTDI. ;-)

    Reply
  7. alex Post author

    Also oneliner, wenn man nicht ausschließlich Perl benutzt:

    % tr -d '[:space:]' < xkcd99_bin.txt | perl -nle 'print pack "B*", $_'
    iloveyOuilOveyouiloveyOuilOveyOuiloveyouilOveyouilOveyOuilOv`
    
    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *