Beefy Boxes and Bandwidth Generously Provided by pair Networks
more useful options
 
PerlMonks  

Converting ASCII to Hex

by Anonymous Monk
on Jul 21, 2005 at 00:55 UTC ( [id://476675]=perlquestion: print w/replies, xml ) Need Help??

Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Hey Monks, Im trying to convert a set of ASCII Characters to Hex Characters. The code below opens a plain text file, converts things to ASCII but I cant figure out how to goto Hex. Any ideas?
#!/usr/bin/perl open(fil,"file.txt"); @file = <fil>; close(fil); $converted=""; foreach(@file) { (@chrs) = split("",$_); foreach $chr(@chrs) { my $asciiChar = ord($chr); ###########DO Hex Stuff HERE########## $hexChr $converted = join("",$converted,$hexChr); } } open(outp, ">output.txt"); print outp $converted; close(outp);

Replies are listed 'Best First'.
Re: Converting ASCII to Hex
by merlyn (Sage) on Jul 21, 2005 at 01:01 UTC

      Any chance of updating this topic now that unpack doesn't work as stated on strings in post-5.1 versions? I don't really want to have to "use bytes". Data::Dumper doesn't give me what I want: it shows the string contents with Unicode characters turned into something like "\x{2013}" (whatever that is) instead of the expected "E2 80 93". Thanks.

        Here's one method. Doubtless there's a neater approach but this works for your single example. Feel free to add to @tests to convince yourself that it is doing what you want.

        #!/usr/bin/env perl use strict; use warnings; use Encode; use Test::More; my @tests = ( { source => "\x{2013}", want => 'E2 80 93' } ); plan tests => scalar @tests; for my $t (@tests) { Encode::_utf8_off ($t->{source}); # bytes my $have = uc unpack "H*", $t->{source}; # into hex $have = join (' ', $have =~ /(..)/g); # spaced pairs is ($have, $t->{want}, "Covert $t->{source} into $t->{want}"); }
      That did not seem to work My Hex codes are coming out like "313030" instead of things like "A5" or "0C" etc...
        If the input string is made of digits, you will see an hex like that you're describing. Consider that in ASCII the digits characters range from decimal 48, which is 0x30, to decimal 57, which is 0x39.

        If you have numbers that you want to treat like integers, and they're in the range 0-255, you can put a chr in the chain:

        #!/usr/bin/perl use strict; use warnings; my @values = (0, 1, 5, 25, 100, 200, 255); print join(", ", map { unpack "H*", chr } @values), $/; __END__ 00, 01, 05, 19, 64, c8, ff
        You may also consider printf/sprintf with the X indicator:
        #!/usr/bin/perl use strict; use warnings; my @values = (0, 1, 5, 25, 100, 200, 255, 500, 1000); print join(", ", map { sprintf "%X", $_ } @values), $/; __END__ 0, 1, 5, 19, 64, C8, FF, 1F4, 3E8
        Note that this second solution works well with numbers beyond 255. You can also pad with zeroes on the left, just look in the docs for sprintf.

        Flavio
        perl -ple'$_=reverse' <<<ti.xittelop@oivalf

        Don't fool yourself.
        Well, 313030 are the hex equivelents for the three characters "1", "0" and "0". Without knowing what your data is, we don't know if that's right or wrong. To clarify matters, are you sure you want the ASCII values of the characters, or do you want the hex representation of the number 100?

        You are missing what merlyn is saying. Use the unpack on the original string, not on the array of ordinals.

        If you run it on the ordinals, then you are converting strings that are made up of digits, so you will get back strings in the range 30..39.

        Update: The following should be more than sufficient to replace the logic in the OP:

        use strict; use warnings; open my $in, "<", "./infile"; my $input = do { local $/; <$in> }; open my $out, ">", "./outfile"; print $out unpack 'H*', $input;

        Though presumably you aren't hardcoding filenames in real life.

        Was your test string "100" by chance? :) 31 = hex 1, 30 = hex 0. You're getting the right output, it just doesn't look like what you expected.

        Here's an example you can try:
        my $text = 'WHY HELLO THERE!'; # split into array of individual characters my @characters = split(//,$text); # transform each one individually into uppercase hex foreach my $char (@characters) { $char = uc(unpack "H*", $char); } # print in quotes to demonstrate separation between array elements print "@characters";
        You can cross-check the results with http://www.lookuptables.com.
Re: Converting ASCII to Hex
by zentara (Archbishop) on Jul 21, 2005 at 11:37 UTC
    Here is a clever little converter...it prints the hex vertically under each ascii char. Stick this in your tool box.
    #!/usr/bin/perl -wnl012 # Prints the contents of a file a line at a time # followed by the ASCII value of each character in vertical columns. # Useful for debugging. # If no filename is specified then input is read from the keyboard. # Version 1.00 Ian Howlett ian@ian-howlett.com 6 July 2001 # Version 1.10 James Yolkowski ajy@sentex.net 8 July 2001 print; # Print the line we've just read @hexvals = map {sprintf "%02X", ord $_} split //; # Get hex value of e +ach char for $a (0, 1) {print map {substr $_, $a, 1} @hexvals} # Print the hex +values. print "\n";

    I'm not really a human, but I play one on earth. flash japh
Re: Converting ASCII to Hex
by graff (Chancellor) on Jul 22, 2005 at 04:05 UTC
    There is one slight disadvantage to merlyn's solution in the initial reply: the strings you are converting into hex numerics will probably include "\n" characters, which of course are "\x0a" -- but  unpack "H*", "\n" will produce just "a" (no leading zero), and based on your reply to merlyn, this is not what you want.

    You seem to want output that consistently shows two hex digits per ASCII character, with leading zero where needed.

    So use sprintf (and use ALL_CAPS for a file handle name, or else use a lexically scoped scalar variable, instead of a lower-case bare word; and don't bother holding the whole file content in an array):

    use strict; open( IN, "file.txt" ) or die "file.txt: $!"; open( OUT, "out.txt" ) or die "out.txt: $!"; # or: open( my $in, "file.txt") ... while (<IN>) { # or: while (<$in> ...) $_ = join "", map { sprintf("%2.2x",ord($_)) } split //; print OUT $_, $/; }
    If "map" is foreign to you, you can use a loop like the following, but you should learn to like map because in this kind of case it'll probably run faster than the for loop):
    my $outstr = ""; $outstr .= sprintf("%2.2x",ord($_)) for (split //); print $outstr, $/;
    For that matter, when doing a simple "in->out" text filter like this, I prefer not to use open at all -- just  while (<>) { ... print; } -- then provide the input file name as a command line arg, and redirect STDOUT to some other file.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://476675]
Approved by Errto
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others chilling in the Monastery: (3)
As of 2024-04-19 05:05 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found