2011-04-20 71 views
8

这是一个原始的OFX文件,因为它来自M个组(无后顾之忧,那里有什么敏感的,我切出的所有交易的中间部分)如何解析这个OFX文件?

开放式金融交易所(OFX)是 数据 - (OFC)和Intuit的开放 交换文件格式来交换 财务信息 来自微软的开放财务 。

现在我需要解析这个。我已经看到了question,但这不是重复的,因为我对如何做到这一点感兴趣。

我相信我可以找出一些可以完成这项工作的聪明的正则表达式,但这很丑陋,容易出错(如果格式改变了,某些字段可能会丢失,格式化/空格不同等等) ..)

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>20110420000000[+1:CET] 
      <LANGUAGE>ENG 
     </SONRS> 
    </SIGNONMSGSRSV1> 
    <BANKMSGSRSV1> 
     <STMTTRNRS> 
      <TRNUID>1 
      <STATUS> 
       <CODE>0 
       <SEVERITY>INFO 
      </STATUS> 
      <STMTRS> 
       <CURDEF>EUR 
       <BANKACCTFROM> 
        <BANKID>20404 
        <ACCTID>02608983629 
        <ACCTTYPE>CHECKING 
       </BANKACCTFROM> 
        <BANKTRANLIST> 
        <DTSTART>20110207 
        <DTEND>20110419 
        <STMTTRN> 
         <TRNTYPE>XFER 
         <DTPOSTED>20110205000000[+1:CET] 
         <TRNAMT>-6.12 
         <FITID>C74BD430D5FF2521 
         <NAME>unbekannt 
         <MEMO>BILLA DANKT 1265P K2 05.02.UM 17.49 
        </STMTTRN> 
        <STMTTRN> 
         <TRNTYPE>XFER 
         <DTPOSTED>20110207000000[+1:CET] 
         <TRNAMT>-10.00 
         <FITID>C74BE0F90A657901 
         <NAME>unbekannt 
         <MEMO>AUTOMAT 13177 KARTE2 07.02.UM 10:22 
        </STMTTRN> 
............................. goes on like this ........................ 
        <STMTTRN> 
         <TRNTYPE>XFER 
         <DTPOSTED>20110418000000[+1:CET] 
         <TRNAMT>-9.45 
         <FITID>C7A5071492D14D29 
         <NAME>unbekannt 
         <MEMO>HOFER DANKT 0408P K2 18.04.UM 18.47 
        </STMTTRN> 
       </BANKTRANLIST> 
       <LEDGERBAL> 
        <BALAMT>1992.29 
        <DTASOF>20110420000000[+1:CET] 
       </LEDGERBAL> 
      </STMTRS> 
     </STMTTRNRS> 
    </BANKMSGSRSV1> 
</OFX> 

我目前使用此代码,给了我希望的结果:

<? 

$files = array(); 
$files[] = '***_2011001.ofx'; 
$files[] = '***_2011002.ofx'; 
$files[] = '***_2011003.ofx'; 

system('touch file.csv && chmod 777 file.csv'); 
$fp = fopen('file.csv', 'w'); 

foreach($files as $file) { 
    echo $file."...\n"; 
    $content = file_get_contents($file); 

    $content = str_replace("\n","",$content); 
    $content = str_replace(" ","",$content); 

    $regex = '|<STMTTRN><TRNTYPE>(.+?)<DTPOSTED>(.+?)<TRNAMT>(.+?)<FITID>(.+?)<NAME>(.+?)<MEMO>(.+?)</STMTTRN>|'; 


    echo preg_match_all($regex,$content,$matches,PREG_SET_ORDER)." matches... \n"; 


    foreach($matches as $match) { 
     echo "."; 
     array_shift($match); 
     fputcsv($fp, $match); 
    } 
    echo "\n"; 
} 
echo "done.\n"; 
fclose($fp); 

这实在是太丑了,如果这是一个有效的XML文件,我会亲手杀了自己对于这一点,但如何做得更好?

+3

男孩,这种格式*发臭!*我很惊讶它还没有在dailywtf功能。 – 2011-04-20 15:00:52

+0

我敢打赌,他们在微软有内部指导,以使外部程序员获得业务优势令人恐惧:D – 2011-04-20 15:16:40

+2

包括内部评估表:您今天违反了多少标准?你滥用了多少外部格式?你偷了多少开放式软件作为我们自己的产品? – 2011-04-20 15:17:22

回答

4

考虑到该文件不是XML 甚至SGML ,您的代码似乎很好。你唯一能做的就是试着制作一个更通用的类SAX解析器。也就是说,你只需简单地通过一个输入流(block可以是任何东西,例如一行或简单的一定数量的字符)。然后,每次遇到<ELEMENT>时调用回调函数。你甚至可以像创建一个解析器类那样奇怪,在那里你可以注册监听特定元素的回调函数。

它会更通用,更不“丑”(对于“丑陋”的某些定义),但它会更多的代码来维护。如果你需要解析这个文件格式很多(或者有很多不同的变化),做得很好,并且很好。如果您的发布代码是唯一的地方,那么只需KISS

+0

是的,这实际上是我的第一个想法,通过线循环和根据标签occourence移动内部指针.. 。我敢打赌,他们有内部的指导方针,让外人难以获得商业优势:D – 2011-04-20 14:54:40

+1

@Joe:实际上,我需要纠正自己。根据维基百科OFX实际上是有效的SGML。所以,你应该可以使用任何标准的SGML解析器来解析这些文件(可能假设你有DTD)。 – 2011-04-20 15:04:26

0
// Load Data String  
    $str = file_get_contents($fLoc); 
    $MArr = array(); // Final assembled master array 
// Fetch all transactions 
    preg_match_all("/<STMTTRN>(.*)<\/STMTTRN>/msU",$str,$m); 
    if (!empty($m[1])) { 
     $recArr = $m[1]; unset($str,$m); 
     // Parse each transaction record 
     foreach ($recArr as $i => $str) { 
      $_arr = array(); 
      preg_match_all("/(^\s*<(?'key'.*)>(?'val'.*)\s*$)/m",$str,$m); 
      foreach ($m["key"] as $i => $key) { 
       $_arr[$key] = trim($m["val"][$i]); // Reassemble array key => val 
      } 
      array_push($MArr,$_arr); 
     } 
    } 
    print_r($MArr);