Как разобрать этот файл OFX?

Это оригинальный файл ofx, так как он получен из m bank (не беспокойтесь, нет ничего деликатного, я вырезал среднюю часть со всеми транзакциями)

Open Financial Exchange (OFX) - это формат потока данных для обмена финансовой информацией, созданный на основе форматов Microsoft Open Financial Connectivity (OFC) и Open Intuit.

Теперь мне нужно разобрать это. Я уже видел этот вопрос, но это не обман, потому что мне интересно, как это сделать.

я уверен, что смогу найти некоторые умные регулярные выражения, которые бы справились с этой работой, но это безобразно и уязвимо для ошибок (если формат изменен, некоторые поля могут отсутствовать, форматирование / пробелы различаются и т. д. и т. д.)

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-файл, я бы лично за это убил себя, но как это сделать лучше?

2 ответа

Решение

Ваш код выглядит нормально, учитывая, что файл не является XML или даже SGML. Единственное, что вы можете сделать, это попытаться сделать более общий SAX-подобный парсер. То есть вы просто просматриваете входной поток по одному блоку за раз (где блоком может быть что угодно, например, строка или просто заданное количество символов). Затем вызывайте функцию обратного вызова каждый раз, когда вы сталкиваетесь с <ELEMENT>, Вы даже можете пойти так же, как создать класс парсера, где вы можете зарегистрировать функции обратного вызова, которые слушают определенные элементы.

Это будет более общий и менее "уродливый" (для некоторого определения "уродливый"), но это будет больше кода для поддержки. Приятно делать и приятно иметь, если вам нужно много раз анализировать этот формат файла (или в разных вариациях). Если ваш опубликованный код - единственное место, где вы делаете это, просто ПОЦЕЛУЙ.

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;
// 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);
Другие вопросы по тегам