非常复杂的正则表达式

时间:2009-12-28 13:10:34

标签: regex encryption

我一直在努力编写我需要的正则表达式。基本上,我有一个由两种不同类型的数据组成的长字符串:

  1. [A-f0-9] {32}
  2. [A-ZA-Z0-9 =] {X}
  3. 问题是,x在特定实例中只是常量:如果在一种情况下,它恰好是12,那么该特定数据集将为12,但下次运行正则表达式时,它可能需要为15或者例如45。我在每个类型(2)之间有一个不可预测的类型(1)。我的目标是“收获”所有类型(2)的数据。

    例如,我可以使用以下形式的字符串:

    [a-f0-9]{192}
    [a-zA-Z0-9=]{11}
    [a-f0-9]{96}
    [a-zA-Z0-9=]{11}
    [af-0-9]{160}
    [a-zA-Z0-9=]{11}
    

    (全部放在一起,没有划界) 。我需要它返回一个由[a-zA-Z0-9 =]字符集的33个字符组成的字符串。这个数字的事实 每个子串中的字符在实例中是不变的(在上面的情况下它是11,但它可能很容易已经是13)是至关重要的,因为它包含较小的字符集,否则将无法知道哪一个字符串开始,另一端结束。

    我一直试图让它工作近一个月,我接近撕裂了我的头发。我不是特别擅长正则表达式......

    示例数据:

    3c21e03a10b9415fb3e1067ea75f8205
    c8dc9900a5089d31e01241c7a947ed7e
    d5f8cd6bb86ebef6d7d104c84ae6e8a7
    e23c99af9c9d6d0294d8b51094c39021
    4bb4af7e61760735ba17c29e8f542a66
    875da91e90863f1ddb7e149297fc59af
    cf5de951fb65d06d2927aab7b9b54830
    e2d935616a54c381c2f38db3731d5a37
    SGVsbG8gbXk
    6dd11d15c419ac219901f14bdd999f38
    0ad94e978ad624d15189f5230e5435a9
    2dc19fe95e583e7d593dd52ae7e68a6e
    465ffa6074a371a8958dad3ad271181a
    23310939b981b4e56f2ecee26f82ec60
    fe04bef49be47603d1278cc80673b226
    gbmFtZSBpcy
    3c21e03a10b9415fb3e1067ea75f8205
    c8dc9900a5089d31e01241c7a947ed7e
    d5f8cd6bb86ebef6d7d104c84ae6e8a7
    e23c99af9c9d6d0294d8b51094c39021
    BvbGl2ZXIga
    4bb4af7e61760735ba17c29e8f542a66
    875da91e90863f1ddb7e149297fc59af
    cf5de951fb65d06d2927aab7b9b54830
    e2d935616a54c381c2f38db3731d5a37
    G9vcmF5IQ==
    

    我想提取“SGVsbG8gbXkgbmFtZSBpcyBvbGl2ZXIgaG9vcmF5IQ ==”。

14 个答案:

答案 0 :(得分:8)

这是你的幸运日!这个问题一般无法解决,但我相信以下内容几乎总能为现实生活中的典型数据提供正确答案:

<?php

$s = '
3c21e03a10b9415fb3e1067ea75f8205
c8dc9900a5089d31e01241c7a947ed7e
d5f8cd6bb86ebef6d7d104c84ae6e8a7
e23c99af9c9d6d0294d8b51094c39021
4bb4af7e61760735ba17c29e8f542a66
875da91e90863f1ddb7e149297fc59af
cf5de951fb65d06d2927aab7b9b54830
e2d935616a54c381c2f38db3731d5a37
SGVsbG8gbXk
6dd11d15c419ac219901f14bdd999f38
0ad94e978ad624d15189f5230e5435a9
2dc19fe95e583e7d593dd52ae7e68a6e
465ffa6074a371a8958dad3ad271181a
23310939b981b4e56f2ecee26f82ec60
fe04bef49be47603d1278cc80673b226
gbmFtZSBpcy
3c21e03a10b9415fb3e1067ea75f8205
c8dc9900a5089d31e01241c7a947ed7e
d5f8cd6bb86ebef6d7d104c84ae6e8a7
e23c99af9c9d6d0294d8b51094c39021
BvbGl2ZXIga
4bb4af7e61760735ba17c29e8f542a66
875da91e90863f1ddb7e149297fc59af
cf5de951fb65d06d2927aab7b9b54830
e2d935616a54c381c2f38db3731d5a37
G9vcmF5IQ==
';
$s = preg_replace('/\r?\n/', '', $s);

for ($i = 1; $i < 20; ++$i) {
    $pattern = "/^(([a-f0-9]{32})+([a-zA-Z0-9=]{{$i}})?)+$/";
    if (preg_match($pattern, $s)) {
        $pattern = "/(?:[a-f0-9]{32})+([a-zA-Z0-9=]{{$i}})/";
        $matches = array();
        preg_match_all($pattern, $s, $matches);
        print_r(join('', $matches[1]));
        break;
    }
}

在这种情况下输出:

SGVsbG8gbXkgbmFtZSBpcyBvbGl2ZXIgaG9vcmF5IQ==

我相信代码可以改进,但我相信你很高兴得到一些有用的东西。我认为这与你上面描述的“火箭筒”方法类似,但我老实说不认为有更好的方法。另请注意,首先要从小猜测开始,以尽量减少错误匹配的可能性。正则表达式中的术语顺序对于在可能有多个选择时增加正确选择的可能性也很重要(尽量尝试先匹配,贪婪,然后最简单匹配)。

答案 1 :(得分:5)

我不相信正则表达式是解决此问题的正确工具。

困扰我的一件事是范围[a-f0-9]包含在范围[a-zA-Z0-9 =]中,并且由于没有分隔符且记录的长度是可变的,两个记录之间的边界看起来很模糊。

您可能有一个heuristic用于通过在数据中查找模式来确定记录的开始和结束位置,然后您可以使用此模式应用正则表达式,但正则表达式不太可能有用你首先要揭开这种模式。

答案 2 :(得分:3)

我不认为您的“数据类型”定义得足以使问题在所有情况下都能解决,无论您是否使用正则表达式。

因为,从你的例子来看,类型1可以连续多次出现,类型2看起来像类型1,因为字符集重叠,我看不出如何分辨所有情况,即使你知道X(从问题来看,我不确定你这么做。)

作为一个原始的例子,给定一个2000字母“a”的重复字符串,你怎么能分辨出类型1和2?

如果有任何可能的东西让你将数据放入明确的分隔符中,那么就这样做。否则,你将不得不使用启发式来消除歧义,我认为regexp不是正确的工具。

答案 3 :(得分:2)

您正在从十六进制字符串之间解析的数据似乎是Base64。您所描述的实际问题似乎无法解决您给出的限制(不能假设任何长度等)。

但是你应该注意的重要事情是base64字符集还包含字符'+'和'/'。 '='字符是填充,因为整个(在您的情况下,连接)base64编码位的长度始终是4个字符的偶数倍。

答案 4 :(得分:2)

正如其他一些答案所说,我认为正则表达式不在这里,或者至少不是最初的。您需要从算法方法开始。原因如下:你无法确定x的价值。您可以做的最好的事情就是对每个类型2的块执行x的数据估计。然后,您需要一种机制来根据所有估计值猜测x的最可能值(可能使用像爬山一样的东西)。之后,您可以应用正则表达式或只是取出适当长度的块。

答案 5 :(得分:1)

如果你知道每个字段的大小,我只想使用substr。

$a = substr($line,192,11);
$b = substr($line,299,11);
$c = substr($line,380,11);

或使用str_split并将该行转换为数组并从数组中构建子字符串。

答案 6 :(得分:1)

你走错了路径IMO。该模式是一个hex-str编码数据,其中包含base64编码部分。这个十六进制数据应该意味着可以用来确定何时“需要”数据开始。此外,如果您正在使用的原始数据被拆分为具有相同长度的行,那么这也应该意味着什么。您应该“理解”数据,而不是使用无脑的RegExp模式来匹配它,这似乎是不可能的。

答案 7 :(得分:0)

如何确定这个神奇的 x

  • 如果您事先知道每个数据集的 x ,只需使用正则表达式,并在每次调用之前将x替换为实际值(在大多数语言中,您可以组成任意字符串并将其用作正则表达式。
  • 如果您不知道 x ,那么我看不出有什么答案,因为无法单独从输入数据中确定(正如您所指出的那样)。

编辑:

从您的评论中,2)似乎适用: x 事先未知。

正如所指出的那样,对于给定的输入数据,通常会有多个解决方案。

您可以编写一个程序来提取满足您标准的所有子字符串。如果给定输入只有一个解决方案,那么你很幸运;否则你将不得不决定你最喜欢哪一个。

要提取子字符串,一个想法(可能不是最优的)就是遍历 x 的所有合理值,并尝试使用每个x 的正则表达式。如果匹配,则找到一个解决方案。如果多个 x 匹配,则有多个解决方案。

可能有更高效的方法可以做到这一点,但是如果你的 x 的上限相当低,这应该是可行的。 (显然,数据大小 - 32始终是 x 的上限,因此原则上总是有效。)

答案 8 :(得分:0)

如何处理以下内容:

([a-f0-9]*([a-zA-Z0-9=]*))*

然后只连接([a-zA-Z0-9=]*)的匹配项。

你能指望每次[a-zA-Z0-9=]*部分长度相同吗?或者你必须验证它?如果你每次必须验证长度,那么这个问题就不能用正则表达式解决(即它不是常规语言,而是无上下文语言至少)。

答案 9 :(得分:0)

这可能是你要匹配的最后一个字符串是否以“==”结束?

如果没有,你可以首先匹配'=='的线条,计算它的大小,然后用它作为你的x来抓住你想要抓住的其他线条。

答案 10 :(得分:0)

如果你不知道你将拥有多少类型(1)以及它们的长度,我真的认为你不能收获你所有的类型(2)。

最好的解决方案是逐行解析字符串并为每行应用正则表达式。如果它匹配类型(2),则将其连接到结果字符串。

如果您的字符串没有被行分割,请先修改preg_replace来解析它。

答案 11 :(得分:0)

或者您可以通过正则表达式检查允许的字符,然后通过属性/函数检查字符串长度。听起来你正在制造比它们应该更复杂的东西。

答案 12 :(得分:-1)

为什么不这样做:

^[a-zA-Z0-9]+==$

^[a-zA-Z0-9]+[=]+$

答案 13 :(得分:-1)

看起来你并不真正关心字符串的内容,所以这应该做。当然你必须知道要使用的号码。另外我假设数据全部在一行(我假设你只是为了澄清新行)

^。{192}({11})。{96}({11})。{160}({11})。* $

然后你只需要合并匹配中的最后一个元素。

==已添加

好的,因为大写似乎是你需要提取的指标。

你需要做的是首先获得一个UpperCase char的所有出现,获得比每个位置小32的倍数,然后使用子串来提取你想要的内容。你怎么再次获得11?