【问题标题】:Regular expression to match object dimensions匹配对象尺寸的正则表达式
【发布时间】:2012-01-16 01:54:13
【问题描述】:

我会把它放在那里:我对正则表达式很糟糕。我试图想出一个来解决我的问题,但我真的不太了解它们。 . .

想象以下几行句子:

  • 你好等等。大约 11 1/2" x 32"。
  • 尺寸为 8 x 10-3/5!
  • 可能在 22" x 17" 区域内的某个地方。
  • 卷很大:42 1/2" x 60 码。
  • 它们都是 5.76 x 8 帧。
  • 是的,大概有 84 厘米长。
  • 我认为是 13/19"。
  • 不,实际上可能是 86 厘米。

我想尽可能清晰地从这些句子中提取项目尺寸。在完美的世界中,正则表达式将输出以下内容:

  • 11 1/2" x 32"
  • 8 x 10-3/5
  • 22" x 17"
  • 42 1/2" x 60 码
  • 5.76 乘 8
  • 84cm
  • 13/19"
  • 86 厘米

我想象一个适用以下规则的世界:

  • 以下是有效单位:{cm, mm, yd, yards, ", ', feet},但我更喜欢考虑任意单位集的解决方案,而不是上述单位的显式解决方案。
  • 尺寸始终以数字形式描述,后面可能有也可能没有单位,可能有也可能没有小数部分或小数部分。允许单独由小数部分组成,例如,4/5"
  • 小数部分总是有一个/ 分隔分子/分母,并且可以假设部分之间没有空格(尽管如果有人考虑到这一点,那就太好了!)。
  • 维度可以是一维或二维的,在这种情况下,可以假设以下对于分隔两个维度是可以接受的:{x, by}。如果维度只是一维,它必须具有上述集合中的单位,即,22 cm 可以,.333 不是,4.33 oz 也不是。

为了向你展示我在使用正则表达式时是多么的无用(并且为了展示我至少尝试过!),我做到了这一点。 . .

[1-9]+[/ ][x1-9]

更新(2)

你们非常快速和高效!我将在下面的正则表达式中添加一些额外的测试用例:

  • 最后一个测试用例是 12 yd x。
  • 最后一个测试用例是 99 厘米。
  • 这句话中没有维度:342 / 5553 / 222。
  • 三个维度? 22" x 17" x 12 厘米
  • 这是一个产品代码:c720 与另一个数字 83 x 更好。
  • 一个独立的数字 21。
  • 体积不应匹配 0.332 盎司。

这些应该导致以下结果(# 表示不应该匹配):

  • 12 码
  • 99 厘米
  • #
  • 22" x 17" x 12 厘米
  • #
  • #
  • #

我已将下面的M42's 答案改编为:

\d+(?:\.\d+)?[\s-]*(?:\d+)?(?:\/\d+)?(?:cm|mm|yd|"|'|feet)(?:\s*x\s*|\s*by\s*)?(?:\d+(?:\.\d+)?[\s*-]*(?:\d+(?:\/\d+)?)?(?:cm|mm|yd|"|'|feet)?)?

虽然这解决了一些新的测试用例,但它现在无法匹配以下其他测试用例。它报告:

  • 11 1/2" x 32" 通过
  • (无)失败
  • 22" x 17" 通行证
  • 42 1/2" x 60 码通行证
  • (无)失败
  • 84cm 合格
  • 13/19" 传球
  • 86 厘米通过
  • 22" 通行证
  • (无)失败
  • (无)失败

  • 12 码 x 失败

  • 99 厘米失败
  • 22" x 17" [还有,但分别为 '12 cm'] 不合格
  • 通过

  • 通过

【问题讨论】:

  • 能否请您提供输入字符串以及预期的输出?
  • 当然。我在这里为您提供了一种更简单的格式:pastebin.com/txfJs8LX 非常感谢!

标签: regex parsing text nlp text-extraction


【解决方案1】:

新版本,接近目标,2次测试失败

#!/usr/local/bin/perl 
use Modern::Perl;
use Test::More;

my $re1 = qr/\d+(?:\.\d+)?[\s-]*(?:\d+)?(?:\/\d+)?(?:cm|mm|yd|"|'|feet)/;
my $re2 = qr/(?:\s*x\s*|\s*by\s*)/;
my $re3 = qr/\d+(?:\.\d+)?[\s-]*(?:\d+)?(?:\/\d+)?(?:cm|mm|yd|"|'|feet|frames)/;
my @out = (
'11 1/2" x 32"',
'8 x 10-3/5',
'22" x 17"',
'42 1/2" x 60 yd',
'5.76 by 8 frames',
'84cm',
'13/19"',
'86 cm',
'12 yd',
'99 cm',
'no match',
'22" x 17" x 12 cm',
'no match',
'no match',
'no match',
);
my $i = 0;
my $xx = '22" x 17"';
while(<DATA>) {
    chomp;
    if (/($re1(?:$re2$re3)?(?:$re2$re1)?)/) {
        ok($1 eq $out[$i], $1 . ' in ' . $_);
    } else {
        ok($out[$i] eq 'no match', ' got "no match" in '.$_);
    }
    $i++;
}
done_testing;


__DATA__
Hello blah blah. It's around 11 1/2" x 32".
The dimensions are 8 x 10-3/5!
Probably somewhere in the region of 22" x 17".
The roll is quite large: 42 1/2" x 60 yd.
They are all 5.76 by 8 frames.
Yeah, maybe it's around 84cm long.
I think about 13/19".
No, it's probably 86 cm actually.
The last but one test case is 12 yd x.
The last test case is 99 cm by.
This sentence doesn't have dimensions in it: 342 / 5553 / 222.
Three dimensions? 22" x 17" x 12 cm
This is a product code: c720 with another number 83 x better.  
A number on its own 21.
A volume shouldn't match 0.332 oz.

输出:

#   Failed test ' got "no match" in The dimensions are 8 x 10-3/5!'
#   at C:\tests\perl\test6.pl line 42.
#   Failed test ' got "no match" in They are all 5.76 by 8 frames.'
#   at C:\tests\perl\test6.pl line 42.
# Looks like you failed 2 tests of 15.
ok 1 - 11 1/2" x 32" in Hello blah blah. It's around 11 1/2" x 32".
not ok 2 -  got "no match" in The dimensions are 8 x 10-3/5!
ok 3 - 22" x 17" in Probably somewhere in the region of 22" x 17".
ok 4 - 42 1/2" x 60 yd in The roll is quite large: 42 1/2" x 60 yd.
not ok 5 -  got "no match" in They are all 5.76 by 8 frames.
ok 6 - 84cm in Yeah, maybe it's around 84cm long.
ok 7 - 13/19" in I think about 13/19".
ok 8 - 86 cm in No, it's probably 86 cm actually.
ok 9 - 12 yd in The last but one test case is 12 yd x.
ok 10 - 99 cm in The last test case is 99 cm by.
ok 11 -  got "no match" in This sentence doesn't have dimensions in it: 342 / 5553 / 222.
ok 12 - 22" x 17" x 12 cm in Three dimensions? 22" x 17" x 12 cm
ok 13 -  got "no match" in This is a product code: c720 with another number 83 x better.  
ok 14 -  got "no match" in A number on its own 21.
ok 15 -  got "no match" in A volume shouldn't match 0.332 oz.
1..15

5.76 by 8 frames 似乎很难匹配,0.332 oz 却不匹配,有时你必须匹配带单位的数字和不带单位的数字。

对不起,我不能做得更好。

【讨论】:

  • 这个匹配所有东西,包括 12 码乘 23.3 码。但是,如何改进它以避免以下情况? “12 yd x”当前与您的正则表达式匹配,但我想如果在这种情况下只匹配 12 yd 会更好。谢谢!
  • 我试图让你的答案适应一些更一般的情况,但失败了。 . .相应地更新了问题。
【解决方案2】:

许多可能的解决方案之一(应该与 nlp 兼容,因为它只使用基本的正则表达式语法):

foundMatch = Regex.IsMatch(SubjectString, @"\d+(?: |cm|\.|""|/)[\d/""x -]*(?:\b(?:by\s*\d+|cm|yd)\b)?");

会得到你的结果:)

说明:

"
\d             # Match a single digit 0..9
   +              # Between one and unlimited times, as many times as possible, giving back as needed (greedy)
(?:            # Match the regular expression below
                  # Match either the regular expression below (attempting the next alternative only if this one fails)
      \           # Match the character “ ” literally
   |              # Or match regular expression number 2 below (attempting the next alternative only if this one fails)
      cm          # Match the characters “cm” literally
   |              # Or match regular expression number 3 below (attempting the next alternative only if this one fails)
      \.          # Match the character “.” literally
   |              # Or match regular expression number 4 below (attempting the next alternative only if this one fails)
      ""          # Match the character “""” literally
   |              # Or match regular expression number 5 below (the entire group fails if this one fails to match)
      /           # Match the character “/” literally
)
[\d/""x -]        # Match a single character present in the list below
                  # A single digit 0..9
                  # One of the characters “/""x”
                  # The character “ ”
                  # The character “-”
   *              # Between zero and unlimited times, as many times as possible, giving back as needed (greedy)
(?:               # Match the regular expression below
   \b             # Assert position at a word boundary
   (?:            # Match the regular expression below
                  # Match either the regular expression below (attempting the next alternative only if this one fails)
         by       # Match the characters “by” literally
         \s       # Match a single character that is a “whitespace character” (spaces, tabs, line breaks, etc.)
            *     # Between zero and unlimited times, as many times as possible, giving back as needed (greedy)
         \d       # Match a single digit 0..9
            +     # Between one and unlimited times, as many times as possible, giving back as needed (greedy)
      |           # Or match regular expression number 2 below (attempting the next alternative only if this one fails)
         cm       # Match the characters “cm” literally
      |           # Or match regular expression number 3 below (the entire group fails if this one fails to match)
         yd       # Match the characters “yd” literally
   )
   \b             # Assert position at a word boundary
)?                # Between zero and one times, as many times as possible, giving back as needed (greedy)
"

【讨论】:

  • 哇,谢谢!它与我想象的所有情况都不完全匹配。例如,如果第一个维度以 mm、cm、yd 等结尾,则它不匹配。我想我可以弄清楚如何调整它。 :-)
  • @Edwardr 我用了你的例子,但我猜你可以扩展它:)
【解决方案3】:

这就是我在“Perl”中使用正则表达式所能得到的一切。尝试使其适应您的正则表达式风格:

\d.*\d(?:\s+\S+|\S+)

解释:

\d        # One digit.
.*        # Any number of characters.
\d        # One digit. All joined means to find all content between first and last digit.
\s+\S+    # A non-space characters after some space. It tries to match any unit like 'cm' or 'yd'.
|         # Or. Select one of two expressions between parentheses.
\S+       # Any number of non-space characters. It tries to match double-quotes, or units joined to the 
          # last number.

我的测试:

script.pl的内容:

use warnings;
use strict;

while ( <DATA> ) {
        print qq[$1\n] if m/(\d.*\d(\s+\S+|\S+))/
}

__DATA__
Hello blah blah. It's around 11 1/2" x 32".
The dimensions are 8 x 10-3/5!
Probably somewhere in the region of 22" x 17".
The roll is quite large: 42 1/2" x 60 yd.
They are all 5.76 by 8 frames.
Yeah, maybe it's around 84cm long.
I think about 13/19".
No, it's probably 86 cm actually.

运行脚本:

perl script.pl

结果:

11 1/2" x 32".
8 x 10-3/5!
22" x 17".
42 1/2" x 60 yd.
5.76 by 8 frames.
84cm
13/19".
86 cm

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-18
    • 1970-01-01
    相关资源
    最近更新 更多