Nasıl iyi XML içine Hiyerarşik Metin Dosyası'nı dönüştürmek için normal ifadeleri kullanabilirsiniz?

4 Cevap php

Günaydın -

Ben içine bir heirarchical metin dosyası (yani, Title => Birden Başlıklar => Çoklu Alt başlıklar => Birden Keys => Çoklu Değerler olan birinin) değerlerini ayrıştırma verimli bir şekilde görmeye ilgileniyorum basit bir XML belge. Basitlik için, cevap kullanılarak yazılmış olacaktır:

  • Regex (tercihen PHP)
  • veya, PHP kodu (örn., döngüler daha verimli olsaydı)

İşte ben çalışıyorum bir Envanteri dosyasının bir örneği. Kafa = FOODS, Sub-kafa vuruşu = Type (A, B...), Keys = PRODUCT (or CODE, etc.) ve Değerleri bir daha fazla satır olabilir unutmayın.

**FOODS - TYPE A**
___________________________________
**PRODUCT**
1) Mi Pueblito Queso Fresco Authentic Mexican Style Fresh Cheese;
2) La Fe String Cheese
**CODE**
Sell by date going back to February 1, 2009
**MANUFACTURER**
Quesos Mi Pueblito, LLC, Passaic, NJ.
**VOLUME OF UNITS**
11,000 boxes
**DISTRIBUTION**
NJ, NY, DE, MD, CT, VA
___________________________________
**PRODUCT**
1) Peanut Brittle No Sugar Added;
2) Peanut Brittle Small Grind;
3) Homestyle Peanut Brittle Nuggets/Coconut Oil Coating
**CODE**
1) Lots 7109 - 8350 inclusive;
2) Lots 8198 - 8330 inclusive;
3) Lots 7075 - 9012 inclusive;
4) Lots 7100 - 8057 inclusive;
5) Lots 7152 - 8364 inclusive
**MANUFACTURER**
Star Kay White, Inc., Congers, NY.
**VOLUME OF UNITS**
5,749 units
**DISTRIBUTION**
NY, NJ, MA, PA, OH, FL, TX, UT, CA, IA, NV, MO and IN
**FOODS - TYPE B**
___________________________________
**PRODUCT**
Cool River Bebidas Naturales - West Indian Cherry Fruit Acerola 16% Juice;
**CODE**
990-10/2 10/5
**MANUFACTURER**
San Mar Manufacturing Corp., Catano, PR.
**VOLUME OF UNITS**
384
**DISTRIBUTION**
PR

Ve burada istenilen çıkış (herhangi bir XML söz dizimi hataları lütfen bağışlayın) bulunuyor:

<foods>
    <food type = "A" >
        <product>Mi Pueblito Queso Fresco Authentic Mexican Style Fresh Cheese</product>
        <product>La Fe String Cheese</product>
        <code>Sell by date going back to February 1, 2009</code>
        <manufacturer>Quesos Mi Pueblito, LLC, Passaic, NJ.</manufacturer>
        <volume>11,000 boxes</volume>
        <distibution>NJ, NY, DE, MD, CT, VA</distribution>
    </food>
    <food type = "A" >
        <product>Peanut Brittle No Sugar Added</product>
        <product>Peanut Brittle Small Grind</product>
        <product>Homestyle Peanut Brittle Nuggets/Coconut Oil Coating</product>
        <code>Lots 7109 - 8350 inclusive</code>
    <code>Lots 8198 - 8330 inclusive</code>
    <code>Lots 7075 - 9012 inclusive</code>
    <code>Lots 7100 - 8057 inclusive</code>
    <code>Lots 7152 - 8364 inclusive</code>
        <manufacturer>Star Kay White, Inc., Congers, NY.</manufacturer>
        <volume>5,749 units</volume>
        <distibution>NY, NJ, MA, PA, OH, FL, TX, UT, CA, IA, NV, MO and IN</distribution>
    </food>
    <food type = "B" >
        <product>Cool River Bebidas Naturales - West Indian Cherry Fruit Acerola 16% Juice</product>
        <code>990-10/2 10/5</code>
        <manufacturer>San Mar Manufacturing Corp., Catano, PR</manufacturer>
        <volume>384</volume>
        <distibution>PR</distribution>
    </food>
</FOODS>
<!-- and so forth -->

Şimdiye kadar, benim yaklaşım (büyük bir metin dosyası ile oldukça verimsiz olabilir) aşağıdakilerden biri olacaktır:

  1. Loops and multiple Select/Case statements, dosya bir dize tamponuna yüklenir ve her bir hat aracılığıyla döngü iken başlık / alt-başlık / anahtar hatlarından birini eşleşirse, bir xml dize değişkeni için uygun xml etiketi ekleme, bakın nerede, ve ardından dayalı xml için alt düğümü eklemek IF anahtar adı en çok (esp metin hatta biraz değiştirirse, zaman alıcı ve hata eğilimli görünüyor.) yeni olduğu ile ilgili ifadeler - VEYA

  2. Use REGEX (Regular Expressions) bulmak ve uygun xml etiketleri ile anahtar alanları yerine, bir xml kütüphane ile bunu temizlemek, ve xml dosyasını vermek. Sorun ben ancak normal ifadeler kullanmak olduğunu, bu yüzden bazı example-based yardımına ihtiyacım var.

Herhangi bir yardım veya tavsiye mutluluk duyacağız.

Teşekkürler.

4 Cevap

Bir örnek, bir başlangıç ​​noktası olarak kullanabilirsiniz. En azından ben size bir fikir verir umarım ...

<?php
define('TYPE_HEADER', 1);
define('TYPE_KEY', 2);
define('TYPE_DELIMETER', 3);
define('TYPE_VALUE', 4);

$datafile = 'data.txt';
$fp = fopen($datafile, 'rb') or die('!fopen');

// stores (the first) {header} in 'name' and the root simplexmlelement in 'element'
$container = array('name'=>null, 'element'=>null);
// stores the name for each item element, the value for the type attribute for subsequent item elements and the simplexmlelement of the current item element
$item = array('name'=>null, 'type'=>null, 'current_element'=>null);
// the last **key** encountered, used to create new child elements in the current item element when a value is encountered
$key = null;

while ( false!==($t=getstruct($fp)) ) {
  switch( $t[0] ) {
    case TYPE_HEADER:
      if ( is_null($container['element']) ) {
        // this is the first time we hit **header - subheader**
        $container['name'] = $t[1][0];
        // ugly hack, < . name . />
        $container['element'] = new SimpleXMLElement('<'.$container['name'].'/>');
        // each subsequent new item gets the new subheader as type attribute
        $item['type'] = $t[1][1];
        // dummy implementation: "deducting" the item names from header/container[name]
        $item['name'] = substr($t[1][0], 0, -1);
      }
      else {
        // hitting **header - subheader** the (second, third, nth) time 
        /*
        header must be the same as the first time (stored in container['name']).
        Otherwise you need another container element since 
        xml documents can only have one root element
        */
        if ( $container['name'] !== $t[1][0] ) {
          echo $container['name'], "!==",  $t[1][0], "\n";
          die('format error');
        }
        else {
          // subheader may have changed, store it for future item elements
          $item['type'] = $t[1][1];
        }
      }
      break;
    case TYPE_DELIMETER:
      assert( !is_null($container['element']) );
      assert( !is_null($item['name']) );
      assert( !is_null($item['type']) );
      /* that's maybe not a wise choice.
      You might want to check the complete item before appending it to the document.
      But the example is a hack anyway ...so create a new item element and append it to the container right away
      */
      $item['current_element'] = $container['element']->addChild($item['name']);
      // set the type-attribute according to the last **header - subheader** encountered
      $item['current_element']['type'] = $item['type'];
      break;
    case TYPE_KEY:
      $key = $t[1][0];
      break;
    case TYPE_VALUE:
      assert( !is_null($item['current_element']) );
      assert( !is_null($key) );
      // this is a value belonging to the "last" key encountered
      // create a new "key" element with the value as content
      // and addit to the current item element
      $tmp = $item['current_element']->addChild($key, $t[1][0]);
      break;
    default:
      die('unknown token');
  }
}

if ( !is_null($container['element']) ) {
  $doc = dom_import_simplexml($container['element']);
  $doc = $doc->ownerDocument;
  $doc->formatOutput = true;
  echo $doc->saveXML();
}
die;


/*
Take a look at gettoken() at http://www.tuxradar.com/practicalphp/21/5/6
It breaks the stream into much simpler pieces.
In the next step the parser would "combine" or structure the simple tokens into more complex things.
This function does both....
@return array(id, array(parameter)
*/
function getstruct($fp) {
  if ( feof($fp) ) {
    return false;
  }
  // shortcut: all we care about "happens" on one line
  // so let php read one line in a single step and then do the pattern matching
  $line = trim(fgets($fp));

  // this matches **key** and **header - subheader**
  if ( preg_match('#^\*\*([^-]+)(?:-(.*))?\*\*$#', $line, $m) ) {
    // only for **header - subheader** $m[2] is set.
    if ( isset($m[2]) ) {
      return array(TYPE_HEADER, array(trim($m[1]), trim($m[2])));
    }
    else {
      return array(TYPE_KEY, array($m[1]));
    }
  }
  // this matches _____________ and means "new item"
  else if ( preg_match('#^_+$#', $line, $m) ) {
    return array(TYPE_DELIMETER, array());
  }
  // any other non-empty line is a single value
  else if ( preg_match('#\S#', $line) ) {
    // you might want to filter the 1),2),3) part out here
    // could also be two diffrent token types
    return array(TYPE_VALUE, array($line));
  }
  else {
    // skip empty lines, would be nicer with tail-recursion...
    return getstruct($fp);
  }
}

baskılar

<?xml version="1.0"?>
<FOODS>
  <FOOD type="TYPE A">
    <PRODUCT>1) Mi Pueblito Queso Fresco Authentic Mexican Style Fresh Cheese;</PRODUCT>
    <PRODUCT>2) La Fe String Cheese</PRODUCT>
    <CODE>Sell by date going back to February 1, 2009</CODE>
    <MANUFACTURER>Quesos Mi Pueblito, LLC, Passaic, NJ.</MANUFACTURER>
    <VOLUME OF UNITS>11,000 boxes</VOLUME OF UNITS>
    <DISTRIBUTION>NJ, NY, DE, MD, CT, VA</DISTRIBUTION>
  </FOOD>
  <FOOD type="TYPE A">
    <PRODUCT>1) Peanut Brittle No Sugar Added;</PRODUCT>
    <PRODUCT>2) Peanut Brittle Small Grind;</PRODUCT>
    <PRODUCT>3) Homestyle Peanut Brittle Nuggets/Coconut Oil Coating</PRODUCT>
    <CODE>1) Lots 7109 - 8350 inclusive;</CODE>
    <CODE>2) Lots 8198 - 8330 inclusive;</CODE>
    <CODE>3) Lots 7075 - 9012 inclusive;</CODE>
    <CODE>4) Lots 7100 - 8057 inclusive;</CODE>
    <CODE>5) Lots 7152 - 8364 inclusive</CODE>
    <MANUFACTURER>Star Kay White, Inc., Congers, NY.</MANUFACTURER>
    <VOLUME OF UNITS>5,749 units</VOLUME OF UNITS>
    <DISTRIBUTION>NY, NJ, MA, PA, OH, FL, TX, UT, CA, IA, NV, MO and IN</DISTRIBUTION>
  </FOOD>
  <FOOD type="TYPE B">
    <PRODUCT>Cool River Bebidas Naturales - West Indian Cherry Fruit Acerola 16% Juice;</PRODUCT>
    <CODE>990-10/2 10/5</CODE>
    <MANUFACTURER>San Mar Manufacturing Corp., Catano, PR.</MANUFACTURER>
    <VOLUME OF UNITS>384</VOLUME OF UNITS>
    <DISTRIBUTION>PR</DISTRIBUTION>
  </FOOD>
</FOODS>

Maalesef için php modülü durumu ANTLR şu anda "Runtime is in alpha status." ama yine de denemeye değer olabilir ...

Bkz: http://www.tuxradar.com/practicalphp/21/5/6

Bu PHP kullanarak belirteçleri bir metin dosyasını ayrıştırmak için nasıl söyler. Bir kez çözümlü istediğiniz her şeyi içine yerleştirebilirsiniz.

Eğer kriterlere göre dosyasında belirli belirteçleri aramak gerekir:

for example: PRODUCT

Bu size XML Tag verir

Sonra 1) özel bir anlamı olabilir

1) Fıstık Gevrek ...

Bu XML etiketi ne koymak söyler.

Bu görevi yerine getirmek için en etkili yolu ise bilmiyorum ama bir derleyici bir dosya ayrıştırma yoldur ve çok doğru yapma potansiyeline sahiptir.

Regex veya PHP yerine (http://www.biglist.com/lists/xsl-list/archives/200508/msg00085.html bakınız) dosyasını okumak için XSLT 2.0 çözümlenmemiş-text () işlevini kullanın

Bir XSLT 1.0 Çözüm için başka bir ipucu burada: http://bytes.com/topic/net/answers/808619-read-plain-file-xslt-1-0-a