regex 从多行字符串的中间提取一个数字?

cyej8jka  于 2023-06-07  发布在  其他
关注(0)|答案(4)|浏览(160)

我正在编写一个C++程序,我需要从TIF文件中读取一段元数据。元数据是一个字符串,如下所示:

<GDALMetadata>
  <Item name="BANDWIDTH"></Item>
  <Item name="CENTER_FILTER_WAVELENGTH"></Item>
  <Item name="DATA_SET_ID">&amp;quot;LRO-L-LOLA-4-GDR-V1.0&amp;quot;</Item>
  <Item name="FILTER_NAME"></Item>
  <Item name="INSTRUMENT_ID">&amp;quot;LOLA&amp;quot;</Item>
  <Item name="INSTRUMENT_NAME">&amp;quot;LUNAR ORBITER LASER ALTIMETER&amp;quot;</Item>
  <Item name="MISSION_NAME"></Item>
  <Item name="NOTE"></Item>
  <Item name="PRODUCER_INSTITUTION_NAME">&amp;quot;GODDARD SPACE FLIGHT CENTER&amp;quot;</Item>
  <Item name="PRODUCT_CREATION_TIME">2017-09-15</Item>
  <Item name="START_TIME">2009-07-13T17:33:17</Item>
  <Item name="STOP_TIME">2016-11-29T05:48:19</Item>
  <Item name="OFFSET" sample="0" role="offset">1737400</Item>
  <Item name="SCALE" sample="0" role="scale">0.5</Item>
</GDALMetadata>

我需要提取scale值(在本例中为0.5)。我的第一个尝试是使用regex,如下所示:

float scale = 1;
std::regex rgx("*<Item name=\"SCALE\"*>(.*?)</Item>*");
std::smatch match;       
if (std::regex_search(metadata.begin(), metadata.end(), match, rgx)) {
    scale = static_cast<float>(std::atof(match.str().c_str()));
};

这不起作用,我不知道为什么。我对正则表达式非常缺乏经验。
显然这看起来像HTML,但因为我只需要这一个特定的领域,我想它应该是简单的尝试直接提取。

mtb9vblg

mtb9vblg1#

我认为使用std::regex是对元数据未来格式的过多假设,因为它是XML文本。XML可以被打乱,包含break和不同的顺序。
我倾向于使用能够解析和处理XML的库,如libxml2或boost::property_tree
Link
以下示例分析元数据并打印比例。

#include <string>
#include <iostream>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>

std::string metadata = R"(
<GDALMetadata>
  <Item name="BANDWIDTH"></Item>
  <Item name="CENTER_FILTER_WAVELENGTH"></Item>
  <Item name="DATA_SET_ID">&amp;quot;LRO-L-LOLA-4-GDR-V1.0&amp;quot;</Item>
  <Item name="FILTER_NAME"></Item>
  <Item name="INSTRUMENT_ID">&amp;quot;LOLA&amp;quot;</Item>
  <Item name="INSTRUMENT_NAME">&amp;quot;LUNAR ORBITER LASER ALTIMETER&amp;quot;</Item>
  <Item name="MISSION_NAME"></Item>
  <Item name="NOTE"></Item>
  <Item name="PRODUCER_INSTITUTION_NAME">&amp;quot;GODDARD SPACE FLIGHT CENTER&amp;quot;</Item>
  <Item name="PRODUCT_CREATION_TIME">2017-09-15</Item>
  <Item name="START_TIME">2009-07-13T17:33:17</Item>
  <Item name="STOP_TIME">2016-11-29T05:48:19</Item>
  <Item name="OFFSET" sample="0" role="offset">1737400</Item>
  <Item name="SCALE" sample="0" role="scale">0.5</Item>
</GDALMetadata>)";

using namespace boost::property_tree;

int main() {
    std::istringstream input( metadata );
    ptree tree;
    read_xml(input, tree);
    auto items = tree.get_child("GDALMetadata", ptree());
    for (const auto& f: items) {
        auto p = f.second;
        std::string name = p.get<std::string>("<xmlattr>.name", "");
        if ( name=="SCALE" ) { 
            std::cout << "Scale: "<< p.data() << std::endl;
        }
    }
}

结果

Program stdout
Scale: 0.5

Godbolt:https://godbolt.org/z/K94EW9YMf

qq24tv8q

qq24tv8q2#

你的正则表达式字符串文字应该是这样的:
"<Item name=\"SCALE\"[^>]*>(.*?)<\\/Item>"
IOW,去掉前面和后面的*,你不需要它们。使用[^>]*而不仅仅是*来忽略"SCALE"之后的所有内容,但不包括>。并且需要在</Item>中转义/(在正则表达式本身中,而不是在字符串字面量中)。
也就是说,match.str()将返回与正则表达式匹配的整个子字符串,而不是您期望的(.*?)组中的值。因此,std::atof()将接收到无效字符串并失败。要只提取组值,请改用match[1].str()
最后,考虑使用std::stof()而不是atof()
试试这个:

float scale = 1;
std::regex rgx("<Item name=\"SCALE\"[^>]*>(.*?)<\\/Item>");
std::smatch match;       
if (std::regex_search(metadata.cbegin(), metadata.cend(), match, rgx)) {
    scale = std::stof(match[1].str());
}

Online Demo

zaq34kh6

zaq34kh63#

你可以找到两个分隔符role="scale"></Item>之间的子字符串,首先你可以删除role="scale">之前的所有</Item>示例,这样子字符串就可以正常工作,然后使用metadata.substr()metadata.find()找到role="scale"></Item>之间的子字符串。

#include <string>

float scale = 1;
while(metadata.find("</Item>") < metadata.find("role=\"scale\">"){
  metadata.replace(metadata.find("</Item>"), 7, "");
}
if(metadata.find("role=\"scale\">") != string::npos && metadata.find("</Item>") != string::npos){
  scale = stof(metadata.substr(metadata.find("role=\"scale\">") + 13, metadata.find("</Item>") - metadata.find("role=\"scale\">") - 13));
}
igsr9ssn

igsr9ssn4#

我会用老办法。读取字符串,直到找到带有单词“scale”的字符串,然后更详细地解析这个字符串:

std::istringstream metadata_stream(metadata_string);
std::string metadata_text_line;
bool found = false;
while (std::getline(metadata_text_line, metadata_stream))
{
    if (metadata_text_line.find("SCALE") != std::string::npos)
    {
        static const char    key_text[] = "\"scale\">";
        std::string::size_type position = metadata_text_line.find(key_text);
        if (position != std::string::npos)
        {
             std::string::npos value_start_position = (position + sizeof(key_text) - 1U);
             end_position = metadata_text_line.find(value_start_position, "<");
             std::string scale_text = metadata_text_line.substr(value_start_position,
                  end_position - value_startOposition);
             //...
        }
    }
}

这段代码提出了一个总体思路或解决方案;可能会有问题

相关问题