【问题标题】:How to parse a OFX (Version 1.0.2) file in PHP?如何在 PHP 中解析 OFX(版本 1.0.2)文件?
【发布时间】:2013-03-22 01:09:51
【问题描述】:

我从Citibank 下载了一个OFX 文件,该文件的DTD 定义在http://www.ofx.net/DownloadPage/Files/ofx102spec.zip(文件OFXBANK.DTD),OFX 文件似乎是SGML 有效。 我正在尝试使用 PHP 5.4.13 的 DomDocument,但我收到了几个警告并且文件未解析。我的代码是:

$file = "source/ACCT_013.OFX";
$dtd = "source/ofx102spec/OFXBANK.DTD";
$doc = new DomDocument();
$doc->loadHTMLFile($file);
$doc->schemaValidate($dtd);
$dom->validateOnParse = true;

OFX 文件开头为:

OFXHEADER:100
DATA:OFXSGML
VERSION:102
SECURITY:NONE
ENCODING:USASCII
CHARSET:1252
COMPRESSION:NONE
OLDFILEUID:NONE
NEWFILEUID:NONE

<OFX>
<SIGNONMSGSRSV1>
<SONRS>
<STATUS>
<CODE>0
<SEVERITY>INFO
</STATUS>
<DTSERVER>20130331073401
<LANGUAGE>SPA
</SONRS>
</SIGNONMSGSRSV1>
<BANKMSGSRSV1>
<STMTTRNRS>
<TRNUID>0
<STATUS>
<CODE>0
<SEVERITY>INFO
</STATUS>
<STMTRS>
<CURDEF>COP
<BANKACCTFROM> ...

我愿意在服务器 (Centos) 中安装和使用任何程序来从 PHP 调用。

PD:这个课程 http://www.phpclasses.org/package/5778-PHP-Parse-and-extract-financial-records-from-OFX-files.html 不适合我。

【问题讨论】:

    标签: php xml domdocument sgml ofx


    【解决方案1】:

    我用这个:

    $source = utf8_encode(file_get_contents('a.ofx'));
    
    //add end tag
    $source = preg_replace('#^<([^>]+)>([^\r\n]+)\r?\n#mU', "<$1>$2</$1>\n", $source);
    
    //skip header
    $source = substr($source, strpos($source,'<OFX>'));
    
    //convert to array
    $xml = simplexml_load_string($source);
    $array = json_decode(json_encode($xml),true);
    
    print_r($array);
    

    【讨论】:

      【解决方案2】:

      最简单的 OFX​​ 解析成一个数组,可以轻松访问所有值和交易。

      function parseOFX($ofx) {
          $OFXArray=explode("<",$ofx);
          $a=array();
          foreach ($OFXArray as $v) {
              $pair=explode(">",$v);
              if (isset($pair[1])) {
                  if ($pair[1]!=NULL) {
                      if (isset($a[$pair[0]])) {
                          if (is_array($a[$pair[0]])) {
                              $a[$pair[0]][]=$pair[1];
                          } else {
                              $temp=$a[$pair[0]];
                              $a[$pair[0]]=array();
                              $a[$pair[0]][]=$temp;
                              $a[$pair[0]][]=$pair[1];
                          }
                      } else {
                          $a[$pair[0]]=$pair[1];
                      }
                  }
              }
          }
          return $a;
      }
      

      【讨论】:

        【解决方案3】:

        首先,即使 XML 是 SGML 的子集,有效的 SGML 文件也不能是格式良好的 XML 文件。 XML 更加严格,并没有使用 SGML 提供的所有功能。

        由于DOMDocument 是基于 XML(而不是 SGML)的,所以这并不完全兼容。

        在该问题旁边,请参阅 Ofexfin1.doc 中的 2.2 Open Financial Exchange Headers 它向您解释了

        Open Financial Exchange 文件的内容由一组简单的标头组成,后跟该标头定义的内容

        还有更多:

        最后一个标题后面有一个空行。然后(对于 OFXSGML 类型),SGML 可读数据以 标记开始。

        所以找到第一个空行并删除所有内容直到那里。然后通过先将 SGML 转换为 XML 将 SGML 部分加载到 DOMDocument 中:

        $source = fopen('file.ofx', 'r');
        if (!$source) {
            throw new Exception('Unable to open OFX file.');
        }
        
        // skip headers of OFX file
        $headers = array();
        $charsets = array(
            1252 => 'WINDOWS-1251',
        );
        while(!feof($source)) {
            $line = trim(fgets($source));
            if ($line === '') {
                break;
            }
            list($header, $value) = explode(':', $line, 2);
            $headers[$header] = $value;
        }
        
        $buffer = '';
        
        // dead-cheap SGML to XML conversion
        // see as well http://www.hanselman.com/blog/PostprocessingAutoClosedSGMLTagsWithTheSGMLReader.aspx
        while(!feof($source)) {
        
            $line = trim(fgets($source));
            if ($line === '') continue;
        
            $line = iconv($charsets[$headers['CHARSET']], 'UTF-8', $line);
            if (substr($line, -1, 1) !== '>') {
                list($tag) = explode('>', $line, 2);
                $line .= '</' . substr($tag, 1) . '>';
            }
            $buffer .= $line ."\n";
        }
        
        // use DOMDocument with non-standard recover mode
        $doc = new DOMDocument();
        $doc->recover = true;
        $doc->preserveWhiteSpace = false;
        $doc->formatOutput = true;
        $save = libxml_use_internal_errors(true);
        $doc->loadXML($buffer);
        libxml_use_internal_errors($save);
        
        echo $doc->saveXML();
        

        此代码示例然后输出以下(重新格式化的)XML,这也表明 DOMDocument 正确加载了数据:

        <?xml version="1.0"?>
        <OFX>
          <SIGNONMSGSRSV1>
            <SONRS>
              <STATUS>
                <CODE>0</CODE>
                <SEVERITY>INFO</SEVERITY>
              </STATUS>
              <DTSERVER>20130331073401</DTSERVER>
              <LANGUAGE>SPA</LANGUAGE>
            </SONRS>
          </SIGNONMSGSRSV1>
          <BANKMSGSRSV1>
            <STMTTRNRS>
              <TRNUID>0</TRNUID>
              <STATUS>
                <CODE>0</CODE>
                <SEVERITY>INFO</SEVERITY>
              </STATUS>
              <STMTRS><CURDEF>COP</CURDEF><BANKACCTFROM> ...</BANKACCTFROM>
        </STMTRS>
            </STMTTRNRS>
          </BANKMSGSRSV1>
        </OFX>
        

        我不知道这是否可以根据 DTD 进行验证。也许这行得通。此外,如果 SGML 没有使用同一行上的标记值写入(并且每行只需要一个元素),那么这种脆弱的转换将会中断。

        【讨论】:

        猜你喜欢
        • 2011-11-06
        • 2011-03-22
        • 2013-03-04
        • 1970-01-01
        • 1970-01-01
        • 2022-07-26
        • 2022-01-22
        • 2012-01-29
        • 2019-01-11
        相关资源
        最近更新 更多