Модераторы: bsa

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Как правильно парсить xlTable, парсинг xlTable 
V
    Опции темы
wallstreet
Дата 9.7.2012, 15:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 153
Регистрация: 11.8.2011

Репутация: нет
Всего: нет



Добрый день, подскажите кто знает, как правильно парсить данные в формате xlTable.
Описание формата
Цитата

Формат Fast Table Format (XlTable) предназначен для максимальной скорости передачи данных через механизм DDE в Microsoft Excel. XlTable представляет собой последовательность блоков данных, которые представляют последовательность ячеик таблицы. Каждый блок данных состоит из трех частей:
 
WORD tdt /* тип таблицы */
 WORD cb /* количество байтов для данных */
 BYTE data[cb] /* массив данных длиною cb */
 
Первый блок всегда типа tdtTable. В нем описывается количество строк, столбцов в таблице.
 Пример: 10 00 04 00 01 00 03 00 – cb = 4, в таблице 3 столбца, 1 строка.
 
Ячейки таблицы всегда «рядоориентированные».Т.е. в последовательности байтов, сначала идут данные первой ячейки в первой ряду, затем второй ячейки первого ряда, а уже затем ячейки второго, третьего ряда и т.д. Также стоит отметить, что ячейки с одинаковым типом данных, в последовательности байтов идут друг за другом в одном блоке данных.
 
Типы блоков данных:

Название  Значение
 
tdtTable   0×0010
 
tdtFloat    0×0001
 
tdtString  0×0002
 
tdtBool    0×0003
 
tdtError   0×0004
 
tdtInt      0×0005
 
tdtSkip    0×0006
 

Основным достоинством обмена данных используя DDE является то, что обмен данными происходит в режиме реального времени.

Хотелось бы услышать совет с чего начать)
На данном этапе я получаю данные в этом формате в массив типа BYTE, хотя возможно необходимо принимать их в char, создавать строку, а потом только парсить. Вобщем кто знает, проясните вопрос плз.

Добавлено через 13 минут и 1 секунду
поидее если по байтам пройтись циклом, то получится что после байта 0×0010 (tdtTable) второй будет означать кол-во байтов в данных, третий кол-во строк в экспортируемой таблице, а четвертый кол-во полей (столбцов).
Только вот может есть функция какая-нибудь для работы с байтами или нет я не знаю? 
PM MAIL   Вверх
wallstreet
Дата 10.7.2012, 13:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 153
Регистрация: 11.8.2011

Репутация: нет
Всего: нет



Думаю надо делать как-то так:
Код

void Data::Parse(BYTE DataBuf[])
{
    for(int i=0; i<256; ++i)
    {
        if(DataBuf[i] = 0x0010) //если встречаем данный флаг понимаем что в 1 байте справа 
                               // размер данных, во 2м кол-во строк, а в 3м кол-во столбцов
        {
            ListRows = DataBuf[i]>>8;  // неправильно пытаюсь считать второй байт от флага
            ListCol = DataBuf[i]>>12;     // неправильно пытаюсь считать третий байт от флага
        }

    }
}

Подскажите как это сделать правильно? Хотя бы намек на истину)

Это сообщение отредактировал(а) wallstreet - 10.7.2012, 14:16
PM MAIL   Вверх
feodorv
Дата 10.7.2012, 14:34 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2214
Регистрация: 30.7.2011

Репутация: 12
Всего: 45



Цитата(wallstreet @  10.7.2012,  14:16 Найти цитируемый пост)
Думаю надо делать как-то так:

Нет, не так. Пусть DataBuf имеет тип BYTE, но это не отменяет того факта, что в начале каждого блока стоят два WORD'а. Вы же всё равно считаете их байтами smile 
В начале DataBuf должен идти блок с типом tdtTable, к данным он не относится, лишь сообщает размеры таблицы.
256 - это что такое? Как это сочетается с числом байт в DataBuf? Кстати, где можно увидеть число байт в массиве DataBuf? Где он заканчивается???

Немного ссылок:


--------------------
Напильник, велосипед, грабли и костыли - основные инструменты программиста...
PM MAIL   Вверх
borisbn
Дата 10.7.2012, 14:46 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 4875
Регистрация: 6.2.2010
Где: Ростов-на-Дону

Репутация: 21
Всего: 135



как-то так
Код

#pragma pack( push, 1 )
struct ExcelBlock {
    unsigned short tdt;
    unsigned short cb;
    unsigned char data[ 0 ];
};
struct ExcelBlockTable {
    unsigned short tdt;
    unsigned short cb;
    unsigned short rows;
    unsigned short cols;
};
#pragma pack( pop )

bool processBlock( const unsigned char * data, unsigned int dataSize )
{
    if ( dataSize < sizeof( ExcelBlockTable ) )
    {
        return false;
    }
    const unsigned char * dataEnd = data + dataSize;
    const ExcelBlockTable * tableInfo = (const ExcelBlockTable *)data;
    if ( tableInfo->tdt != 0x10 || tableInfo->cb != 4 )
    {
        return false;
    }
    data += sizeof( ExcelBlockTable );
    int row = 0, col = 0;
    while ( data < dataEnd )
    {
        const ExcelBlock * cell = (const ExcelBlock *)data;
        unsigned int sz = sizeof( ExcelBlock ) + cell->cb;
        data += sz;
        
        switch ( tableInfo->tdt )
        {
            case 0x0001: // float
                const float * f = (const float *)cell->data;
                // excel[ row ][ col ] = *f; // положить в ячейку float
            break;
            case 0x0002: // string
                // excel[ row ][ col ] = string( cell->data ); // положить в ячейку string
            break;
            // и т.д.
        }
        
        col++;
        if ( col == tableInfo->cols )
        {
            col = 0;
            row++;
        }
    }
    return true;
}


Добавлено через 4 минуты и 44 секунды
Всегда поражался "дальновидности" мелкомягких... Это ж надо - заложились на более чем 255 типов (!!!) данных в ячейке  smile

Добавлено через 10 минут и 42 секунды
Учти, НЕ БУДЕТ компилироваться gcc, т.к. по стандарту не разрешено объявлять нулевые массивы. Студия же кушает и не ломается

Это сообщение отредактировал(а) borisbn - 10.7.2012, 14:49


--------------------
Женщины отличаются от программистов тем, что у них чары состоят из стрингов
PM MAIL Jabber   Вверх
feodorv
Дата 10.7.2012, 15:14 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2214
Регистрация: 30.7.2011

Репутация: 12
Всего: 45



borisbn
Немного сложнее))) Ячейки могут объединяться в один блок smile 
И, по-моему, не float, а double...

И ещё нужен контроль длины ячейки и выхода за пределы границы буфера:
Код

            typeTable = 0x10,    // 4 байта
            typeFloat = 0x01,    // 8 байта
            typeString = 0x02,    // 1+N байт
            typeBool = 0x03,    // 2 байта
            typeError = 0x04,    // 2 байта
            typeBlank = 0x05,    // 2 байта
            typeInt = 0x06,            // 2 байта
            typeSkip = 0x07,    // 2 байта

А то мало ли какие данные на вход попадут...


--------------------
Напильник, велосипед, грабли и костыли - основные инструменты программиста...
PM MAIL   Вверх
borisbn
Дата 10.7.2012, 15:17 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 4875
Регистрация: 6.2.2010
Где: Ростов-на-Дону

Репутация: 21
Всего: 135



feodorv, да, конечно... сложнее. Я просто дал чушку и напильник  smile

Добавлено через 50 секунд
Лень было открывать документацию и досконально изучать, а ТСу это придётся делать


--------------------
Женщины отличаются от программистов тем, что у них чары состоят из стрингов
PM MAIL Jabber   Вверх
wallstreet
Дата 20.7.2012, 12:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 153
Регистрация: 11.8.2011

Репутация: нет
Всего: нет



Благодарю за помощь. Пилю гири))
Единственно я немного не понимаю каким образом cells объединяются в блоки?!?!?!
К примеру пришел блок (ExcelBlockTable) с размерами таблицы (tableInfo->tdt), затем размер данных (tableInfo->cb) и затем сами данные. Данные заполняются по порядку, сначала первая строка (А1, B1, C1, D1....), потом вторая и тд.
Так вот вопрос по объединению ячеек в блок.
Правильно ли я понимаю,  приходят данные сначала одного типа, потом второго и так далее? Или же приходят изначально построчно данные разных типов подряд, а потом уже, когда таблица заполнена, приходят блоками только те данные которые изменились в таблице??? Моя же задача при этом собирать эти данные, формировать в блоки и вставлять в мой список по их индексу в таблице (типо A1, D3, C2 )?

Кстати некоторые импортируемые таблицы постоянно меняют размер, кол-во записей увеличивается постоянно. Отсюда следует что массивы я использовать не могу из-за нестатичности их размера. Использую двумерный список, но слабо представляю как вставлять в него значения по индексу?!?!?
PM MAIL   Вверх
wallstreet
Дата 26.7.2012, 10:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 153
Регистрация: 11.8.2011

Репутация: нет
Всего: нет



на самом деле немного не понял высказывание:
Цитата

Это ж надо - заложились на более чем 255 типов (!!!) данных в ячейке  

Если вы намекаете на 
Код

 for(int i=0; i<256; ++i)

то это размер буфера, в который динамически льются данные, мне необходимо их распарсить и уже в нормальном виде вывести в отдельный список. Да обязательные атрибуты занимают ограниченное число байт, но сколько будут занимать данные, я не знаю. Так, на глаз, больше 200 конкретно в моей таблице еще не было, поэтому и поставил с запасом.
PM MAIL   Вверх
borisbn
Дата 26.7.2012, 13:11 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 4875
Регистрация: 6.2.2010
Где: Ростов-на-Дону

Репутация: 21
Всего: 135



Цитата(wallstreet @  26.7.2012,  10:01 Найти цитируемый пост)
Если вы намекаете на 

нет. на то, что под тип данных в ячейке мелкомягкими отведено более одного байта
Цитата(wallstreet @  9.7.2012,  15:55 Найти цитируемый пост)
WORD tdt


слушай, а как ты выделяешь фон текста ? да ещё и в поле код ?


--------------------
Женщины отличаются от программистов тем, что у них чары состоят из стрингов
PM MAIL Jabber   Вверх
feodorv
Дата 27.7.2012, 01:05 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2214
Регистрация: 30.7.2011

Репутация: 12
Всего: 45



Цитата(wallstreet @  26.7.2012,  11:01 Найти цитируемый пост)
в который динамически льются данные

Эээ... Тогда нужно сохранять какое-то состояние чтения из буфера, так как MS не обязана выравнивать xlTable-данные по 256-байтной границе  smile 


Цитата(wallstreet @  20.7.2012,  13:05 Найти цитируемый пост)
каким образом cells объединяются в блоки?!?!?!

Я давал ссылки на документ с примерами данных и даже кое-какой (пусть и C#) код. Внимательный анализ кода 
Код

          for (int R = 0; R < Rows; R++) for (int C = 0; C < Cols; )

обнаружит, что во втором цикле нету изменения переменной C. Вот и вопрос на засыпку: куда оно делось? smile 

Ну и примеры данных Вам в руки:
Цитата

10 00 04 00 01 00 03 00    tdtTable, cb=4, rows=1, columns=3
02 00 10 00    tdtString, cb=16
04 45 61 73 74    cch=4, East (tdtString continued)
04 57 65 73 74    cch=4, West (tdtString continued)
05 4e 6f 72 74 68    cch=5, North (tdtString continued)

Здесь три ячейки объединены в один блок smile И не забывайте про бланковые (tdtBlank) ячейки.


Цитата(borisbn @  26.7.2012,  14:11 Найти цитируемый пост)
на то, что под тип данных в ячейке мелкомягкими отведено более одного байта

Да формат не оптимален. Но все почему-то хвалят за оптимальность (наверное, по сравнению с dbf)  smile 


--------------------
Напильник, велосипед, грабли и костыли - основные инструменты программиста...
PM MAIL   Вверх
wallstreet
Дата 30.7.2012, 18:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 153
Регистрация: 11.8.2011

Репутация: нет
Всего: нет



Благодарю за ссылку, многое для себя почрпнул. 
Вот на выходных решил переписать код на c++, который вы приводили в качестве примера на с#
В принципе приложение компилируется, но при получении данных возникает вредная ошибка 
Цитата

Unhandled exception at 0x77cdaae9 in R4T.exe: 0xC0000005: Access violation writing location 0x000000cd.

Что это за ошибка и с чем ее едят я уже начитался от и до. Только вот найти не могу нулевой указатель в который якобы
происходит попытка записи данных. С помощью breakpoint выследил построчно последнюю строку с которой ошибка не возникает.
Если опустить точку брейка ниже на строку, ошибка появляется.
Вот собственно код:
ConnDecl.h
Код

#include "stdafx.h"
#include <Windows.h>
#include <iostream>
#include <Dde.h>
#include <Ddeml.h>


#ifndef CONNDECL_HPP
#define CONNDECL_HPP

struct Variant
{
    union 
    {
        double my_double;
        unsigned char* my_pchar;
        unsigned short my_short;
    };
    enum
    {
        E_INT,
        E_DOUBLE,
        E_STRING
    } m_type;
}; 

// класс управления соединением
class Connection
{
public:
    Connection();
    ~Connection();
    BOOL On(HINSTANCE hInst,LPSTR szService, LPSTR szTopic, LPSTR szItem);
    void Off();
    BOOL ConnectionStatus() const;
private:
    HWND hwnd;
    HINSTANCE hInst;
    FARPROC lpDdeProc;
    BOOL Status;

};

#endif


ConnRealization.cpp
(участок коллбека, в котором обрабатываем сообщение об отправке нам данных)
Код

case XTYP_POKE:
        {
            if(hsz1 == hszTopic)
            {
                if(hConvApp == hConv)
                {
                    int iCount = DdeQueryString(idInst, hsz2, NULL, 0, CP_WINANSI) + 1;
                    unsigned char* bf = new unsigned char[iCount]; 
                    DdeQueryString(idInst, hsz2, (LPWSTR)*bf, iCount, CP_WINANSI);
                    std::string s = "";                        // если установить брейк поинт на эту строку, выскочет ошибка                for(int i=0; i<iCount-1; i++)
                        *s = *s + (char)bf[i];
                    
                    iCount = DdeGetData(hData, NULL, 0, 0);
                    bf = new unsigned char[iCount];
                    DdeGetData(hData, (unsigned char*)bf, iCount, 0);            // стремное приведение типа
                    short Rows, Cols;
                    std::vector<std::vector<Variant>> Tbl = ParseXTbl(bf, Rows, Cols); // тут проследить за столбцами и строками

                    for(int r=0; r<Rows; ++r)
                    {
                        std::string Name = (char*)Tbl[r][0].my_pchar;
                        double Cs = Tbl[r][0].my_double;
                    }
                }
            }
            return((HDDEDATA)NULL);
            break;
        }


реализация ParseXTbl()
Код

std::vector<std::vector<Variant>> ParseXTbl(unsigned char bf[], short Colls, short Rows)
{
    int a = 0;
    unsigned char cb = (unsigned char)(bf[a] + bf[a + 1] * 256);
    a += 2;
    if(cb==16)
    {
        a = 2;
        cb = (unsigned char)(bf[a] + bf[a + 1] * 256);
        a += 2;
        Rows = (short)(bf[a] + bf[a + 1] * 256);
        a += 2;
        Colls = (short)(bf[a] + bf[a + 1] * 256);
        a += 2;
    
        std::vector<std::vector<Variant>> Tbl(Rows, std::vector<Variant>(Colls));
        std::vector<std::vector<Variant>>::iterator it = Tbl.begin();
        Variant var;
        

        for(int R=0; R>Rows; ++R)
            for(int C=0; C>Colls;)
            {
                unsigned char Tp = (bf[a] + bf[a + 1] * 256);
                a += 2;
                cb = (unsigned char)(bf[a] + bf[a + 1] * 256);
                a += 2;
                if(Tp==1)
                {
                    for (; cb > 0; cb -= 8, a += 8, C++)
                    {
                        var.m_type = Variant::E_DOUBLE;
                        var.my_double = atof((const char*)bf); // тут нужно проследить приведение типа
                        var.my_short = Tp;
                        Tbl[R][C] = var;
                    }
                }
                else if(Tp==2)
                {
                    for (; cb > 0; cb --, C++)
                    {
                        unsigned char cb1 = bf[a];
                        a++;
                        cb -= cb1;
                        var.m_type = Variant::E_STRING;
                        var.my_pchar = bf;
                        var.my_short = Tp;
                        Tbl[R][C] = var;
                        a += cb1;
                    }

                }
            }
        return Tbl;
    }
}



наверняка наскрещивал слонов с носорогами и профи будут в "восторге" от увиденного, но сам никак не могу найти 
проблемный участок. Если кто свежим взглядом заметит, я буду признателен.
PM MAIL   Вверх
xvr
Дата 31.7.2012, 12:11 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 35
Всего: 223



Ошибки здесь:
Код

                   unsigned char* bf = new unsigned char[iCount]; 
                    DdeQueryString(idInst, hsz2, (LPWSTR)*bf, iCount, CP_WINANSI);
                    std::string s = "";
                    for(int i=0; i<iCount-1; i++)
                     *s = *s + (char)bf[i];
Судя по LPWSTR у вас Unicode версия, а дальше вся работа идет с ANSI строками, что явно неправильно.
Должно быть так:
Код

                    int iCount = DdeQueryStringA(idInst, hsz2, NULL, 0, CP_WINANSI) + 1;
                    char* bf = new unsigned char[iCount+1]; 
                    DdeQueryStringA(idInst, hsz2, bf, iCount+1, CP_WINANSI);
                    std::string s = bf;


PM MAIL   Вверх
wallstreet
Дата 31.7.2012, 15:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 153
Регистрация: 11.8.2011

Репутация: нет
Всего: нет



Цитата(xvr @  31.7.2012,  12:11 Найти цитируемый пост)
Судя по LPWSTR у вас Unicode версия, а дальше вся работа идет с ANSI строками, что явно неправильно.Должно быть так:

Код

      char* bf = new unsigned char[iCount+1]; 
      DdeQueryStringA(idInst, hsz2, bf, iCount+1, CP_WINANSI);


благодарю за участие в теме, но так, увы, не получится т.к. LPWSTR это wchar_t, а не  char.
А 2 байтный wchar_t никак использовать не могу. Сейчас поменял на:
Код

     char* bf = new char[iCount]; 
     DdeQueryString(idInst, hsz2, (LPWSTR)bf, iCount, CP_WINANSI);
     std::string s = bf;

вроде процесс пошел smile , но от явного приведения типа уйти не удалось. :(
PM MAIL   Вверх
xvr
Дата 31.7.2012, 20:43 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 35
Всего: 223



Цитата(wallstreet @  31.7.2012,  15:34 Найти цитируемый пост)
 но так, увы, не получится т.к. LPWSTR это wchar_t, а не  char.

Получится. Обратите внимание, там функция DdeQueryStringA, а не DdeQueryString

Цитата(wallstreet @  31.7.2012,  15:34 Найти цитируемый пост)
Сейчас поменял на:

А вот это работать не будет, т.к. DdeQueryString все равно будет выдавать на-гора Unicode, какой бы вы буфер ей не дали  smile 

PM MAIL   Вверх
wallstreet
Дата 31.7.2012, 23:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 153
Регистрация: 11.8.2011

Репутация: нет
Всего: нет



Цитата(xvr @  31.7.2012,  20:43 Найти цитируемый пост)
Получится. Обратите внимание, там функция DdeQueryStringA, а не DdeQueryString

сори, провтыкал
 smile 
PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "C/C++: Для новичков"
JackYF
bsa

Запрещается!

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делиться вскрытыми компонентами

  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы по реализации алгоритмов рассматриваются здесь


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, JackYF, bsa.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Для новичков | Следующая тема »


 




[ Время генерации скрипта: 0.0741 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.