文本解析查找具有表示基本单位(例如平方米、sq.m、m^2、m2 等)的不同字符串的数据

Text parsing looking for data with different strings expressing the base units (ex. square meters, sq.m, m^2, m2, etc.)

我正在尝试抓取存储在各种 txt 文件中的许多住宅物业的面积编号(平方米或平方英尺)。

面积几乎总是通过指定单位来表示,无论是在公制系统中(长度以米为基本单位表示)还是在英国帝国系统中(长度以英尺为基本单位表示)通过给出一个数字按单位。

面积单位有多种字符串表示,例如"square meters"可以显示为"sqm"、"sq.m"、"sq m"、"square m"、"sq.meters"、"m^2"、"m2"等(不同字母的大小写也可以变化)。

我拥有的一些 TXT 示例(我只复制了包含我感兴趣的数据的行,清理了其余部分):

1)

...
Approximate Gross Internal Area = 40.1 sq m / 432 sq ft Re’
...

2)

...
Total area: approx. 37.3 sq. metres (402.0 sq. feet)
...

3)

...
Approx. Gross Internal Area *
413Ft’-38.37M’
...

我的目标是解析每个 txt 文件,获取平方米(或平方英尺)数并存储它。

我已经开始研究 Python 常规 Expressions/RegEx、模式匹配、文本处理和文本解析工具,但我决定搁置研究,看看是否有人曾经有一个相似的目标。

您认为解决此特定任务的最有效方法是什么?通过使用正则表达式、文本解析或什么?

我很愿意使用其他脚本语言(PERL、Ruby 等),如果它们更适合的话。

如果我没理解错的话,你想构建一个文本解析器来执行数据库插入(我想)。

我认为这是一项简单的任务,可以用您建议的所有语言实现。他们之间没有特定的收益(偏好问题除外)。

我建议您采用符合以下标准的语言:

  1. 您的生产服务器是否提供解释器?
  2. 您打算永远维护此代码,还是打算在不久的将来由其他人来维护它?
      • 选择您觉得更舒服的语言
    • 其他人
      • 选择一种在您所在地区更容易找到工作人员的语言。

顺便说一下,我会选择 Python。它随处可用。

您可以使用 Marpa::R2, a Perl interface to Marpa,一个通用的 BNF 解析器。

数据可以在BNF中描述为this ::= that~运算符定义词法规则)。

解析器returns 一种数据结构([ id, child1, child2 ... ] 格式的数组),可以从中提取区域。

您还可以将 semantic actions 定义为 Perl sub 在同一个或单独的包中处理数据。

下面是一个基于您的数据的示例脚本及其输出。

脚本:

use 5.010;
use strict;
use warnings;

use Data::Dumper;
$Data::Dumper::Indent = 1;
$Data::Dumper::Terse = 1;
$Data::Dumper::Deepcopy = 1;

use Marpa::R2;

my $g = Marpa::R2::Scanless::G->new( { source => \(<<'END_OF_SOURCE'),

    :default ::= action => [ name, value]
    lexeme default = action => [ name, value] latm => 1

    <area data>      ::= <area data item>+
    <area data item> ::= area
    <area>           ::= float unit

    float ~ int '.' int
    float ~ int '.'
    float ~ int
    int ~ [\d]+

    unit ~ 'sqm' | 'sq.m' | 'sq m' | 'square m' | 'sq.meters' | 'm^2' | 'm2'

    :discard ~ whitespace
    whitespace ~ [\s]+

END_OF_SOURCE
} );

my $input = <<EOI;
40.2 sq m
40 sqm
40. sq.m
40.2 sq m
40.2 square m
40.2 sq.meters
40.2 m^2
40.2 m2
EOI

say Dumper ${ $g->parse( $input ) };

输出:

[
  'area data',
  [
    'area data item',
    [
      'area',
      [
        'float',
        '40.2'
      ],
      [
        'unit',
        'sq m'
      ]
    ]
  ],
  [
    'area data item',
    [
      'area',
      [
        'float',
        '40'
      ],
      [
        'unit',
        'sqm'
      ]
    ]
  ],
  [
    'area data item',
    [
      'area',
      [
        'float',
        '40.'
      ],
      [
        'unit',
        'sq.m'
      ]
    ]
  ],
  [
    'area data item',
    [
      'area',
      [
        'float',
        '40.2'
      ],
      [
        'unit',
        'sq m'
      ]
    ]
  ],
  [
    'area data item',
    [
      'area',
      [
        'float',
        '40.2'
      ],
      [
        'unit',
        'square m'
      ]
    ]
  ],
  [
    'area data item',
    [
      'area',
      [
        'float',
        '40.2'
      ],
      [
        'unit',
        'sq.meters'
      ]
    ]
  ],
  [
    'area data item',
    [
      'area',
      [
        'float',
        '40.2'
      ],
      [
        'unit',
        'm^2'
      ]
    ]
  ],
  [
    'area data item',
    [
      'area',
      [
        'float',
        '40.2'
      ],
      [
        'unit',
        'm2'
      ]
    ]
  ]
]

虽然使用正则表达式没有错。它们以您提到的所有语言提供。

BNF 可以更多readable/maintainable,这在处理非结构化文本时很好。

OP 通过电子邮件要求此答案。

如果您能够通过 IronPython 或类似工具使用 .NET 库,那么您可能会在 Units.NET 中找到用于解析您的区域字符串的方法。或者简单地复制其解析正则表达式并根据您的需要进行修改。

Units.NET 可以解析大多数常用单位的值+单位字符串表示,包括公制和英制。

示例:

double a = Area.Parse("5 m²").SquareMeters; // 5.0
Area.Parse("0.092903m²").SquareFeet;        // ~1.0
Area.Parse("50000 cm²").ToString();         // "5 m²", sq.m is default unit
Area.Parse("1ft²").SquareMeters;            // 0.092903
Area.Parse("1 in²").SquareCentimeters;      // 6.4516

Area.json for all area units and their abbreviations. Please note that SquareMeter currently only supports "m²", but you can add more abbreviations like "m^2", "sq.m", "sqm" and so on at runtime via UnitSystem.MapUnitToAbbreviation() and we happily accept pull requests to add more unit abbreviations to the library. See example

这是我为解决该问题而编写的 Python RegEx 代码,以备不时之需:

unit_pattern = re.compile(r"[+-]? *((?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?)\s*(square meters|square metres|square meter|square metre|square m|square mt|sqmt|sq mt|sq.mt|sq. mt|sqm|sq m|sq.m|sq. m|meters2|metres2|meter2|metre2|mt2|m2)", re.IGNORECASE)
for i, line in enumerate(open('/Users/USERNAME/text.txt')):
    for match in re.finditer(unit_pattern, line):
        print 'Found on line %s: %s' % (i+1, match.groups())

应用于以下 txt 文件:

eleventh Floor
Approx. 37.3 sq. metres (402.0 sq. feet)

Living
Room

7.06m x 4.04m
(23'2" x 13'3")

Bedroom
Area

Shower
Room

Total area: approx. 37.3 sq. metres (402.0 sq. feet)

For illustrative purposes only — Not to scale.
Plan produced using P|anUp.

给出以下输出:

Found on line 2: ('37.3', 'sq. m')
Found on line 16: ('37.3', 'sq. m')