有人能为我翻译这段糟糕的 Perl 代码吗?

3

我需要获取这段Perl代码中使用的算法,但我对Perl一无所知。通常这不是问题,因为我会研究该语言,但这个正则表达式的东西完全超出了我的理解范围!

有人能伪代码化吗?我只需要知道正在发生什么,以便我可以在其他地方实现它,最好是PHP甚至C ++,但我将担心那部分。 我只需要以某种方式解密正在发生的事情:

$a = $ARGV[0];
$a =~ s/[^A-F0-9]+//simg;
@b = reverse split /(\S{2})/,$a;
$c = join "", @b;
$c .= "0000";
$d = hex($c) % 999999929;
print "$d\n";

@Sinan,感谢您更改标题。 :) 确实写得很差,我希望至少有人会评论一下,但事情就是这样。 - armani
这对我来说并不是“糟糕编写的Perl代码”,如果它能够完成预期功能,那么它甚至是一段良好编写的代码。 - René Nyffenegger
5
这段文字写得很糟糕,因为它并没有提取十六进制数字,而是用了s///split,其中第一个参数不清楚。与其使用位移操作,不如在末尾拼接零。甚至s///都很愚蠢。应该只使用s/[[:^xdigit:]]+//g/s/m选项没有影响。 - Sinan Ünür
4个回答

10

它有什么写得不好的地方吗?它可能需要更好的变量名称,但我不知道是否可能(因为中间步骤似乎没有可命名的特征),只剩下了对 split 方法的不恰当使用。 伪代码几乎是逐字翻译。

$a = $ARGV[0];
$a =~ s/[^A-F0-9]+//simg;
@b = reverse split /(\S{2})/,$a;
$c = join "", @b;
$c .= "0000";
$d = hex($c) % 999999929;
print "$d\n";

应该是这样的

$a = $ARGV[0];                # Get a hex str from cmd line   E3:C9:D4
$a =~ s/[^A-F0-9]+//simg;     # Remove any non-hex digits     E3C9D4
@b = reverse $a =~ /(..)/sg;  # Extract "bytes"; reverse      D4, C9, E3
$c = join "", @b;             # Join them.                    D4C9E3
$c .= "0000";                 # Append two NULs               D4C9E30000
$d = hex($c) % 999999929;     # Convert from hex to number and modulus
print "$d\n";                 # Print the result (in decimal).

稍微更清楚一些:

$a = $ARGV[0];
$a =~ s/[^0-9A-Fa-f]+//g;
$a = join '', reverse $a =~ /(..)/sg;
$a .= "0000";
$a = hex($a);
$a %= 999999929;
print "$a\n";
这些代码片段可能存在问题。在具有32位整数的Perl中,如果输入的十六进制数超过4个数字,hex将会溢出。而具有64位整数的Perl可以处理12个十六进制数字。
你似乎是从这里获取了代码。它旨在以MAC地址作为输入,这意味着代码需要64位整数或Math::BigInt才能工作。由于您想模除64位值,因此没有绕过它的方法。
以下是一种简洁的方法,仅适用于具有64位整数的Perl:
my $mac = $ARGV[0];
$mac =~ s/[^0-9A-Fa-f]+//g;
die length($mac) != 12;

# "123456789ABC" => 0xBC9A785634120000
my $hash = unpack('Q<', pack('H*', "0000$mac"));

$hash %= 999999929;
print "$hash\n";

为了提高可移植性,最好将Math::BigInt集成到早期版本中。


添加有关可能错误的注释。 - ikegami
添加了关于要求的注释。 - ikegami
哈,原来不只有我在尝试调整这个Perl脚本!真有趣。你的帖子是最简明扼要的[你可能是一位老师],所以我将你的帖子选为被采纳的解决方案。它确实帮了我很多,谢谢。 - armani
添加了更简洁的代码,只适用于具有64位整数的Perl。 - ikegami

2
$a = $ARGV[0]; # assign first command line arg to $a
$a =~ s/[^A-F0-9]+//simg; # delete non-hex from $a
@b = reverse split /(\S{2})/,$a; # split $a by 2 non-whitespace (saving them too) to array  @b and reverse it
$c = join "", @b; # join array @b to scalar $c
$c .= "0000"; # append 4 zeros to $c
$d = hex($c) % 999999929; # get modulo
print "$d\n"; # print it

split 调用时,字符串仅包含十六进制数字。因此,split 将字符串拆分为八位字节的十六进制表示形式。 - Sinan Ünür
@SinanÜnür:做了一些编辑,希望现在更清楚了,谢谢。 - w.k
第二行其实并不可怕!哈哈...感谢您的工作。我还在仔细研究,可能以后还会有一些问题... - armani
那么,如果我的输入是 ABCDEF ,那么在分割后数组 @b 中会包含 (B,A),(D,C),(F,E) 吗? - armani
@armani: 不,列表上下文中的reverse函数是翻转元素的顺序而非元素本身:ABCDEF -> qw(EF, CD, AB)。 - w.k

2
它正在寻找一堆十六进制连接在一起的八位字节作为程序的第一个参数,并应用模数运算。
因此,如果以以下方式调用程序: $ myprog.pl A0B0
那么$c中的值将是B0A00000。因此,$d的值应该是0x396A6C8E。
这是一个特别糟糕的代码片段,由一个害怕pack和unpack的人编写。

“pack”在这里不太有用(会使代码变得更长),除非输入具有固定的宽度。 - ikegami
@ikegami 我的直觉告诉我,代码期望 $ARGV[0] 包含小端序的 16 位整数。 - Sinan Ünür
3
实际上,它期望一个MAC地址,但这也是固定宽度的。请看我的答案。 - ikegami

1
$a = $ARGV[0]; #Read in the first argument on the command line
$a =~ s/[^A-F0-9]+//simg; #Substitute non hex numbers with nothing *
@b = reverse split /(\S{2})/,$a; #What is left in $a, split by 2 non-space characters
$c = join "", @b; # put the array b into $c
$c .= "0000"; 
$d = hex($c) % 999999929; #Convert $c to an integer and % with 999999929
print "$d\n";
  • simg = i:不区分大小写;g:全局匹配;m:多行匹配;s:单行匹配;

简而言之,我们将去掉第一个十六进制数,然后反转字节顺序(每次两个十六进制数),并对结果进行模运算。


那个关于 "simg" 的解释——我在网上找不到任何信息!我猜它不是一个 “基本” 的正则表达式。 - armani
正则表达式会删除任何非十六进制字符,而不是你提出的内容。 - dgw

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接