如何解析这个 OFX 文件?

发布于 2024-11-02 15:15:56 字数 3956 浏览 4 评论 0原文

这是原始 ofx 文件,因为它来自 m 银行(不用担心,没有什么敏感信息,我剪掉了所有交易的中间部分)

开放金融交易所(OFX)是一个 用于交换的数据流格式 演变的财务信息 来自微软的开放金融 连接 (OFC) 和 Intuit 的 Open 交换文件格式。

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

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

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 文件,我个人会为此自杀,但如何做得更好呢?

this is an original ofx file as it comes from m bank (no worries, theres nothing sensitive, i cut out the middle part with all the transactions)

Open Financial Exchange (OFX) is a
data-stream format for exchanging
financial information that evolved
from Microsoft's Open Financial
Connectivity (OFC) and Intuit's Open
Exchange file formats.

now i need to parse this. i already saw that question, but this is not a dup because i am interested in how to do this.

i am sure i could figure out some clever regexps that would do the job, but that is ugly and error vulnerable (if the format is changed, some fields may be missing, the formatting/white spaces are different etc etc...)

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>

i currently use this code which gives me the desired result:

<?

$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);

this is really ugly and if this was a valid xml file i would personally kill myself for that, but how to do it better?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

〗斷ホ乔殘χμё〖 2024-11-09 15:15:56

考虑到该文件不是 XML 甚至 SGML,您的代码看起来不错。您唯一能做的就是尝试制作一个更通用的类似 SAX 的解析器。也就是说,您只需一次一个块地浏览输入流(其中块可以是任何内容,例如一行或只是一组字符)。然后,每次遇到 时调用回调函数。您甚至可以像构建解析器类一样幻想,在其中可以注册侦听特定元素的回调函数。

它将更加通用并且不那么“丑陋”(对于“丑陋”的某些定义),但是需要维护更多的代码。如果您需要大量解析此文件格式(或许多不同的变体),那么这样做很高兴,也很高兴拥有。如果您发布的代码是您执行此操作的唯一位置,那么只需KISS

Your code seems fine, considering that the file isn't XML or even SGML. The only thing you could do is try to make a more generic SAX-like parser. That is, you simply go through the input stream one block at a time (where block can be anything, e.g. a line or simply a set amount of characters). Then, call a callback function every time you encounter an <ELEMENT>. You can even go as fanciful as building a parser class where you can register callback functions that listen to specific elements.

It will be more generic and less "ugly" (for some definition of "ugly") but it will be more code to maintain. Nice to do and nice to have if you need to parse this file format a lot (or in a lot of different variations). If your posted code is the only place you do this then just KISS.

最佳男配角 2024-11-09 15:15:56
// 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);
// 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);
北陌 2024-11-09 15:15:56
function close_tags($x)
{
    return preg_replace('/<([A-Za-z0-9.]+)>([^<\r\n]+)/', '<\1>\2</\1>', $x);
}

$ofx = file_get_contents('myfile.ofx');

$body = '<OFX>'.explode('<OFX>', $ofx)[1];   // strip the header

$xml = close_tags($body);   // make valid XML

$reader = new SimpleXMLElement($xml);

foreach($reader->xpath('//STMTTRN') as $txn):   // find and loop through all STMTTRN tags, note the double forward slash

    // get the tag contents by casting as (string) to invoke the SimpleXMLElement::__toString() method

    $trntype = (string)$txn->TRNTYPE;
    $dtposted = (string)$txn->DTPOSTED;
    $trnamt = (string)$txn->TRNAMT;
    $name = (string)$xn->NAME;
    $memo = (string)$txn->MEMO;

endforeach;
function close_tags($x)
{
    return preg_replace('/<([A-Za-z0-9.]+)>([^<\r\n]+)/', '<\1>\2</\1>', $x);
}

$ofx = file_get_contents('myfile.ofx');

$body = '<OFX>'.explode('<OFX>', $ofx)[1];   // strip the header

$xml = close_tags($body);   // make valid XML

$reader = new SimpleXMLElement($xml);

foreach($reader->xpath('//STMTTRN') as $txn):   // find and loop through all STMTTRN tags, note the double forward slash

    // get the tag contents by casting as (string) to invoke the SimpleXMLElement::__toString() method

    $trntype = (string)$txn->TRNTYPE;
    $dtposted = (string)$txn->DTPOSTED;
    $trnamt = (string)$txn->TRNAMT;
    $name = (string)$xn->NAME;
    $memo = (string)$txn->MEMO;

endforeach;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文