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

I am trying to convert a ethernet addresses from the form 8:0:20:0:2:10 to 080020000210. I need to add a zero to the single digit hex numbers and strip out the colons from the address.

I could split the address using split and then recombine it without the colons, but using regular expressions sounds like more fun.

Here what works:

my $foo = "0:0A:0C:B:B8:F"; $foo =~ s/\A([0-9A-F]){1}:/0$&/g; #take care of the start $foo =~ s/:([0-9A-F]{1}):/:0$1:/g; #take care of the middle $foo =~ s/:([0-9A-F]{1})\Z/:0$1/g; #take care of the end $foo =~ s/\://g; #remove the colon

I got it working using four different expressions, but I think it is sorta a kludgy solution. Any ideas on how I could improve it?

Replies are listed 'Best First'.
•Re: Converting MAC Address
by merlyn (Sage) on Jan 10, 2003 at 23:40 UTC
Re: Converting MAC Address
by rob_au (Abbot) on Jan 10, 2003 at 23:40 UTC
    How about this?

    my $value = "8:0:20:0:2:10"; print join "", map { sprintf "%02s", $_ } split /:/, $value;

    or

    print sprintf "%02s%02s%02s%02s%02s%02s", split /:/, $value;

    Updated as per merlyn's and sauoq's comments below (changed %02d to %02s).

     

    perl -le 'print+unpack("N",pack("B32","00000000000000000000001000010100"))'

      Careful. The %d format is for signed integers in decimal. The better choice would be %s.

      -sauoq
      "My two cents aren't worth a dime.";
      
Re: Converting MAC Address
by Enlil (Parson) on Jan 10, 2003 at 23:44 UTC
    using a regular expression this results in the same thing as your 4 lines do:
    my $foo = "0:0A:0C:B:B8:F"; $foo =~ s/([0-9A-F])+:?/length($1) < 2?"0$1":$1/ge;

    update:let's try again

    my $foo = "0:0A:0C:B:B8:F"; $foo =~ s/([0-9A-F]+):?/length($1) < 2?"0$1":$1/ge;
    The problem with the initial one was where the + was which would match a series of [0-9A-F], but only the last character would be captured, so the length would always be one and it just happened that it was adding a 0 to the front of that and I missed the B8 thing initially. so ++ to you for the catch.

    Which brings the question, which maybe I am not thinking but why would it capture only the last character instead of only the initial character?

    -enlil

      Using:

      my $foo = "0:0A:0C:B:B8:F"; $foo =~ s/([0-9A-F])+:?/length($1) < 2?"0$1":$1/ge;

      Yields 000A0C0B080F instead of 000A0C0BB80F. I don't understand why it produces that. From what I understand, which is obviously incorrect, it should work.

      Any ideas why it doesn't work?

      Thanks,

      HiFoo

        Try

        $foo = "0:0A:0C:B:B8:F"; $foo =~ s/([0-9A-F]+)(?::|$)/length($1) < 2?"0$1":$1/ge; print $foo 000A0C0BB80F

        With your version [0-9A-F]+ can just as easily match one hex char as two as the :? part is optional (so that you will match at the end of line. Therefore, it matches the ...:B8:... in two chunks instead of one.

        The version above makes the : or end-of-string mandatory (?::|$) and forces the B8 part to be matched as a single piece rather than two.

        Not necessarially the best way to do it, but it corrects the problem you've identified.

        You can also simplify the right-hand side of the s/// somewhat like this:

        $foo =~ s/([0-9A-F]+)(?::|$)/substr "0$1", -2/ge;

        Examine what is said, not who speaks.

        The 7th Rule of perl club is -- pearl clubs are easily damaged. Use a diamond club instead.

        In the vein of TMTOWTDI, I think this is clearer:

        my $mac = "0:0A:0C:B:B8:F"; my @mac = split /:/, $mac; $mac = join ':', map {sprintf '%02X', hex $_};
        (BTW, this will reformat things that aren't even close to being valid MACs into things that look a little closer, but still aren't. For example, 'this::isn't:a:mac:foo' would become '00:00:0A:00:0F'.)


        Warning: Unless otherwise stated, code is untested. Do not use without understanding. Code is posted in the hopes it is useful, but without warranty. All copyrights are relinquished into the public domain unless otherwise stated. I am not an angel. I am capable of error, and err on a fairly regular basis. If I made a mistake, please let me know (such as by replying to this node).