Форум программистов, компьютерный форум, киберфорум
C++: COM, OLE, ActiveX
Войти
Регистрация
Восстановить пароль
 
 
Рейтинг 4.86/35: Рейтинг темы: голосов - 35, средняя оценка - 4.86
125 / 118 / 57
Регистрация: 07.04.2011
Сообщений: 738
1

Импорт диапазона из Excel в вариантный массив

05.05.2015, 21:20. Просмотров 7202. Ответов 34
Метки нет (Все метки)

Всем доброго времени суток.Ребята,подскажите пожалуйста,как из Екселя выдернуть сразу диапазон ячеек со значениями и запихнуть в вариантный массив?3 дня уже курю гугл - и все никак не выйдет.В какую сторону хоть копать?
Могу создать вариантный массив,считать каждую ячейку в экселе отдельно,и запихнуть в данный массив.Но при объеме данных свыше 50 000 строк чтение довольно долго выходит.А эти данные еще и обработать надо.
ПЫ.СЫ.Готовый код не прошу - просто пните в нужном направлении наброском кода.Что и куда копать.
Спасибо!
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
05.05.2015, 21:20
Ответы с готовыми решениями:

Как сделать вариантный импорт рисунков?
Разрешите у Вас спросить такую вещь: мне следует сделать следующее. Пользователь выбирает в...

Как лучше записать вариантный массив в текстовой (.txt) файл
Имеется массив A:variant MyArray := VarArrayCreate(, VarDouble); Как писать тоже знаю, примерно...

Чтение данных из диапазона (Excel) в массив (Delphi)
Прочитав пару статей на тему быстрой обработки данных excel в delphi, решил считывать данные...

Excel: создать новый массив по возрастанию элементов выделенного диапазона рабочей таблицы
Разработать подпрограмму создания нового массива по возрастанию элементов выделенного диапазона...

34
Модератор
3326 / 2111 / 339
Регистрация: 13.01.2012
Сообщений: 8,202
06.05.2015, 08:45 2
как теоретический вариант - выделить диапазон и скопировать как текст в буфер или еще как-нибудь и потом колдовать с этим содержимым как со строкой
0
125 / 118 / 57
Регистрация: 07.04.2011
Сообщений: 738
07.05.2015, 09:29  [ТС] 3
А скопировать в буфер куда?В массив.По сути ранж в массив - это и будет чтение в буфер.Я могу читать поячеечно и запихивать в вариантный массив каждую ячейку индивидуально.Я попробовал - чтение 50000 ячеек и занесение в массив заняло 6!!! минут.В дельфях,си билдере,вба,и даже на фокспро можно сразу взять ранж,и присвоить массиву.Примеров на вба,дельфях - куча.На плюсах нет нормальных примеров.
Возникла идея для *овнокода - через плюсы запустить макрос для vba,который считает сразу весь ранж в массив,а потом уже оперировать с этим массивом.Но это уже изврат.Есть же куда более простое решение.

Добавлено через 2 часа 24 минуты
Нашел пока временное решение с чтением и записью ранжа.
10 столбцов*65536 строк = 655360 ячеек с данными скопировались с 1 листа и были записаны на другой лист,расположенный в той же книге за 45 секунд.Долго.Когда будет нормальный код - отпишусь.Вдруг пригодится кому-нибудь

Добавлено через 12 часов 34 минуты
Вот так получилось считать сразу весь диапазон в переменную
C++
1
2
3
Excel::RangePtr pRange;
         pRange = OldSheet->UsedRange;
         _variant_t vItem = pRange->GetValue();
Переменная содержит все значения диапазона.Теперь нужно из каждого индекса доставать значение,и обрабатывать.
По сути vItem не является вариантным массивом.Но надо обращаться к каждому индексу отдельно.Создаю вариантый массив так:

C++
1
2
3
4
5
6
VARIANT v;
        v.vt = VT_ARRAY | VT_VARIANT;
        SAFEARRAYBOUND sab[2];
        sab[0].lLbound = 1; sab[0].cElements = 10;//строк
        sab[1].lLbound = 1; sab[1].cElements = 13;//столбцов
        v.parray = SafeArrayCreate(VT_VARIANT, 2, sab);
Как скопировать значения из vItem в вариантный массив для обращения по индексам?Или как-то можно обратиться к каждому индексу vItem.
0
Модератор
3326 / 2111 / 339
Регистрация: 13.01.2012
Сообщений: 8,202
07.05.2015, 09:35 4
Цитата Сообщение от OverDozero Посмотреть сообщение
Переменная содержит все значения диапазона
что то я сомневаюсь. то есть переменная конечно же позволяет получить доступ к значениям диапазона и в этом смысле она содержит все значения но сама переменная скорее всего является объектом, а не сейв аррэй

Добавлено через 34 секунды
...или нет? что внутри vItem? какого он типа?
0
125 / 118 / 57
Регистрация: 07.04.2011
Сообщений: 738
07.05.2015, 09:59  [ТС] 5
С мсдн "Объект _variant_t инкапсулирует тип данных VARIANT. Этот класс управляет выделением и освобождением ресурсов и осуществляет необходимые вызовы функций VariantInit и VariantClear."
https://msdn.microsoft.com/ru-... 5h94e.aspx
По сути это все,что хочешь ты.Я так понимаю.Любой тип данных.У меня могут быть и числа,и строки в книге.И все разом надо считать с переменную.
https://rsdn.ru/article/com/varsafearr.xml - еще про тип вариант
Должен быть способ выдернуть из vItem значения.Если нет - придется возвращаться к чтению поячеечно и занесению в массив.
Нагуглил такой код:
C++
1
VARIANT *pvData = (VARIANT *)(vItem.parray->pvData); //  прямой указатель на данные в массиве варианта
На выходе pvData имеет значение первой ячейки vItem.Если есть значение первой ячейки,значит должно быть значение и второй.
Непонятно обращение к vItem.parray. vItem объявлена как _variant_t,а обращение идет как к SAFEARRAY
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct tagVARIANT
    {
    union 
        {
        struct __tagVARIANT
            {
            VARTYPE vt;
            WORD wReserved1;
            WORD wReserved2;
            WORD wReserved3;
            union 
                {               
                SAFEARRAY *parray;
}
Ну или же я сильно заблуждаюсь насчет вариантных массивов=)
0
Модератор
3326 / 2111 / 339
Регистрация: 13.01.2012
Сообщений: 8,202
07.05.2015, 11:39 6
Цитата Сообщение от OverDozero Посмотреть сообщение
По сути это все,что хочешь ты
в варианте может быть тупо объект вызов методов которого приведет к нужному результату. а могут быть и сами данные так как это нужно вам. посмотрите какого типа vItem - что у него в поле которое отвечает за тип.
0
125 / 118 / 57
Регистрация: 07.04.2011
Сообщений: 738
11.05.2015, 17:50  [ТС] 7
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
VARIANT v;
        v.vt = VT_ARRAY | VT_VARIANT;
        SAFEARRAYBOUND sab[2];
        sab[0].lLbound = 1; sab[0].cElements = 3;//строк
        sab[1].lLbound = 1; sab[1].cElements = 3;//столбцов
        v.parray = SafeArrayCreate(VT_VARIANT, 2, sab);     
        Excel::RangePtr pRange;
         pRange = OldSheet->UsedRange;
         _variant_t vItem;
         VariantInit(&vItem);
        vItem = pRange->GetValue();
        
        long ncols = (vItem.parray)->rgsabound[0].cElements;//3 столбца
        long nrows = (vItem.parray)->rgsabound[1].cElements;//3 строки
 
        VARIANT *GetElem = new VARIANT [nrows*ncols];
        HRESULT hr;
        for(i=0; i<nrows; i++)
        {
            for(j=0; j<ncols; j++)
            {
                long indi[] = {i+1,j+1};
            
            hr =    SafeArrayGetElement(vItem.parray, indi, &GetElem[i,0]);
 
                    // VariantChangeType(GetElem,GetElem,0,VT_BSTR);
 
                SafeArrayPutElement(v.parray, indi, GetElem);}}
Вот что вышло.Читает диапазон из екселя в переменную,и записывает в другой массив.
Вопрос:Как выдернуть значение переменной до SafeArrayPutElement(v.parray, indi, GetElem); из GetElem?Например получить значение как тип wchar_t или bstr_t,обработать и только потом уже записать в другой массив.Почему - то,в любом случае,у меня в переменной содержится и выводится адрес,хотя присваиваю значение через указатель.Как это правильно сделать?
0
Модератор
3326 / 2111 / 339
Регистрация: 13.01.2012
Сообщений: 8,202
12.05.2015, 15:39 8
Цитата Сообщение от OverDozero Посмотреть сообщение
Вопрос:Как выдернуть значение переменной до SafeArrayPutElement(v.parray, indi, GetElem); из GetElem?Например получить значение как тип wchar_t или bstr_t,обработать и только потом уже записать в другой массив.Почему - то,в любом случае,у меня в переменной содержится и выводится адрес,хотя присваиваю значение через указатель.Как это правильно сделать?
чего чего? судя по всему у вас настоящий массив. если вам нужно что то с ним делать - делайте. в том коде который вы описали вы считываете значения из него в промежуточный массив, а из него записываете в массив результата. причем как-то странно - промежуточный массив одномерный, считывание идет в элемент [i,0] что просто невозможно так как в языке C++ используется [i][j] или раз уж он одномерный то имитацией двухмерного массива через [i * ncols + j], а запись вообще идет всего промежуточного массива махом, причем на каждом новом элементе... или я что то не так понял? если вам это все не надо - делайте то что надо - берите данные из массива и делайте с ними свои действия. для развлечения альтернативный код
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
    Variant app = CreateOleObject("Excel.Application");
    Variant wbs = app.OlePropertyGet("Workbooks");
    Variant wb = wbs.OleFunction("Open", "D:\\test.xls");
    Variant wss = wb.OlePropertyGet("Worksheets");
    Variant ws = wss.OlePropertyGet("Item", 1);
    Variant cell1 = ws.OlePropertyGet("Cells", 1, 1);
    Variant cell2 = ws.OlePropertyGet("Cells", 2, 2);
 
    AnsiString s;
 
    Variant range = ws.OlePropertyGet("Range", cell1, cell2);
    s += IntToStr(range.Type()) + "\r\n"; //0x9 = VT_DISPATCH
 
    Variant array = range.OlePropertyGet("Value");
    s += IntToStr(array.Type()) + "\r\n"; //0x200C = VT_ARRAY | VT_VARIANT
 
    for (int i = array.ArrayLowBound(1); i <= array.ArrayHighBound(1); i++)
    {
        for (int j = array.ArrayLowBound(2); j <= array.ArrayHighBound(2); j++)
        {
            Variant value = array.GetElement(i, j);
            s += IntToStr(value.Type()) + "\t" + (AnsiString)value + "\r\n"; //тут я делаю то что мне надо - мне надо вывести тип и значение приведенное к строке
        }
    }
 
    Memo1->Text = s;
 
    app.OleProcedure("Quit");
1
125 / 118 / 57
Регистрация: 07.04.2011
Сообщений: 738
12.05.2015, 20:11  [ТС] 9
Вот результат vItem после присваивания ранжа из экселя(в вложениях)
По сути это же двумерный массив,ну или очень странный одномерный,с индексами 1,1 и тд.И значит к его элементам надо обращаться как к двумерному.Что я и попытался сделать в коде,приведенном выше.
C++
1
 hr =    SafeArrayGetElement(vItem.parray, indi, &GetElem[i,0]);
- загуглил часть кода,и переделал под себя.
И мне не понятно почему при указании hr = SafeArrayGetElement(vItem.parray, indi, &GetElem[i,j]); по GetElem возвращаются только значения 1,4,7 и все.Если hr = SafeArrayGetElement(vItem.parray, indi, &GetElem[i,0]); указано так,то возвращаются значения 1,2,3,4,5,6,7,8,9 - то есть нормальные значение,которые забиты в ячейках.hr = SafeArrayGetElement(vItem.parray, indi, &GetElem[i,1]); или 2,или 100 - мусор возвращается.Работает только 0.
Потом я SafeArrayGetElement пытаюсь получить ЗНАЧЕНИЕ ЭЛЕМЕНТА для дальнейшей обработки.Умножить на 10 например и добавить в v.parray
Все проблема в том что я не могу получить значение этого элемента.При SafeArrayGetElement(vItem.parray, indi, &GetElem[i,0]) при пошаговом дебаге получаю по GetElem значение 1,и могу его добавить в SafeArrayPutElement(v.parray, indi, GetElem);.И там тоже добавится значение 1.А вот умножить полученное значение на 10 - не выходит.Вот как это сделать?Именно получить значение элемента, - число,строка и тд
Полный код программы:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include "stdafx.h"
#include "windows.h"
#include <clocale>
#include "fstream"
 
#import "C:\\Program Files (x86)\\Common Files\\Microsoft Shared\\OFFICE14\\MSO.DLL" auto_rename no_namespace
#import "C:\\Program Files (x86)\\Common Files\\Microsoft Shared\\VBA\\VBA6\\VBE6EXT.OLB" auto_rename no_namespace
#import "C:\\Program Files (x86)\\Microsoft Office\\Office14\\EXCEL.EXE" auto_rename
 
long int i,j;//переменные циклов
 
 
int main(int argc, char* argv[])    
{
    std::setlocale(LC_ALL,"");
 
        HRESULT hRes = E_FAIL;  
 
            CoInitialize(NULL);
 
            Excel::_ApplicationPtr pApp;
    
        hRes = pApp.CreateInstance("Excel.Application");
    
    if ( FAILED(hRes) )     
    {
        MessageBox(NULL, L"Excel не найден!", L"Ошибка", MB_OK);  
 
        return 1;
    }
 
    Excel::_WorkbookPtr OldBook;//книга 
    OldBook = pApp->Workbooks->Open("C:\\1.xls");//открыть книгу
 
    //отключение событий
    pApp->PutDisplayAlerts(0,FALSE);
    pApp->PutEnableEvents(FALSE);
        
            Excel::_WorksheetPtr OldSheet;//лист исходный
 
                OldSheet = OldBook->Worksheets->GetItem(1);// выбрать первый лист
 
            OldSheet -> Activate();
 
        //10 строк роу 15 столбцов кол
        VARIANT v;
        v.vt = VT_ARRAY | VT_VARIANT;
        SAFEARRAYBOUND sab[2];
        sab[0].lLbound = 1; sab[0].cElements = 3;//строк
        sab[1].lLbound = 1; sab[1].cElements = 3;//столбцов
        //v.parray = SafeArrayCreate(VT_VARIANT, 2, sab);
        v.parray = SafeArrayCreate(VT_VARIANT, 2, sab);
                
        Excel::RangePtr pRange;
         pRange = OldSheet->UsedRange;
         _variant_t vItem;
         VariantInit(&vItem);
 
            vItem = pRange->GetValue();
            long ncols = (vItem.parray)->rgsabound[0].cElements;//3 столбца
            long nrows = (vItem.parray)->rgsabound[1].cElements;//3 строки
 
            VARIANT *GetElem = new VARIANT [nrows*ncols];
            HRESULT hr;
            for(i=0; i<nrows; i++)
            {
                for(j=0; j<ncols; j++)
                {
                    long indi[] = {i+1,j+1};
 
                    hr =    SafeArrayGetElement(vItem.parray, indi, &GetElem[i,0]);
 
                    // VariantChangeType(GetElem,GetElem,0,VT_BSTR);
 
                    SafeArrayPutElement(v.parray, indi, GetElem);
                }
            }       
            pApp->Quit();
        }
Во вложениях книга на которой я проверяю.
0
Миниатюры
Импорт диапазона из Excel в вариантный массив  
Вложения
Тип файла: xls 1.xls (25.0 Кб, 4 просмотров)
Модератор
3326 / 2111 / 339
Регистрация: 13.01.2012
Сообщений: 8,202
12.05.2015, 22:44 10
Цитата Сообщение от OverDozero Посмотреть сообщение
И мне не понятно
и мне не понятно как это вообще работает и зачем так написано. вот как работает массив (arg в примере имеет тип VARIANT)
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
    SAFEARRAY *sa = V_ARRAY(&arg);
 
    long i_min, i_max;
    SafeArrayGetLBound(sa, 1, &i_min);
    SafeArrayGetUBound(sa, 1, &i_max);
 
    long j_min, j_max;
    SafeArrayGetLBound(sa, 2, &j_min);
    SafeArrayGetUBound(sa, 2, &j_max);
 
    for (unsigned i = i_min; i <= i_max; i++)
    {
        for (unsigned j = j_min; j <= j_max; j++)
        {
            long indexes[2] = {i, j};
 
            VARIANT v;
            VariantInit(&v);
 
            SafeArrayGetElement(sa, indexes, &v);
 
            if (V_VT(&v) == VT_R8) //в ячейке значение типа VT_R8 типичного для вычислений в Excel
            {
                double x = V_R8(&v);
                //делаем что-то с x...
            }
            else if (V_VT(&v) == VT_BSTR) //в ячейке текст
            {
                BSTR x = V_BSTR(&v);
                //делаем что-то с x...
            }
            else //в ячейке что-то ужасное - пытаемся получить из этого текст
            {
                VARIANT tmp;
                VariantInit(&tmp);
 
                HRESULT hr = VariantChangeType(&tmp, &v, 0, VT_BSTR);
                if (hr != S_OK)
                {
                    //не вышло
                }
                else
                {
                    BSTR x = V_BSTR(&tmp);
                    //делаем что-то с x...
                }
 
                VariantClear(&tmp);
            }
 
            VariantClear(&v);
        }
    }
0
19 / 18 / 1
Регистрация: 10.01.2017
Сообщений: 944
19.02.2019, 12:49 11
OverDozero, а если Вам не жалко, не могли бы поделится кодом, как Вы читаете данные ексель по одной ячейке ?
0
125 / 118 / 57
Регистрация: 07.04.2011
Сообщений: 738
21.02.2019, 20:07  [ТС] 12
Цитата Сообщение от Optimus11 Посмотреть сообщение
OverDozero, а если Вам не жалко, не могли бы поделится кодом, как Вы читаете данные ексель по одной ячейке ?
Все что нашел=). На чтение и запись есть наработки. При желании думаю разберетесь!
Удачи.
2
Вложения
Тип файла: rar Desktop.rar (3.6 Кб, 9 просмотров)
19 / 18 / 1
Регистрация: 10.01.2017
Сообщений: 944
21.02.2019, 22:01 13
Огромное Спасибо!
0
19 / 18 / 1
Регистрация: 10.01.2017
Сообщений: 944
18.03.2019, 19:20 14
Здравствуйте,

Подскажите пожалуйста, вот создается вариант массив:

C++
1
2
3
4
5
6
7
8
VARIANT massiv;
    arr.vt = VT_ARRAY | VT_VARIANT;
    {
        SAFEARRAYBOUND sab[2];
        sab[0].lLbound = 1; sab[0].cElements = 1;
        sab[1].lLbound = 1; sab[1].cElements = 1;
        massiv.parray = SafeArrayCreate(VT_VARIANT, 2, sab);
    }
Не могу понять назначение вот этих команд:

C++
1
2
sab[0].lLbound = 1; sab[0].cElements = 1;
sab[1].lLbound = 1; sab[1].cElements = 1;
То есть вроде логично предположить, что эти строки устанавливают размеры массива по минимальной и максимальной величине за пределы которого записать данные нельзя.
Но:
1)Если установить как указано выше, то есть везде по 1, это никак не влияет на запись в такой массив данных к примеру в 5 столбец 8 строку. Все записывается и выводится.
2)Если убрать вообще эти строки, то тоже не заметил влияние, так же без проблем записываются данные в созданные массив.
0
Модератор
3326 / 2111 / 339
Регистрация: 13.01.2012
Сообщений: 8,202
19.03.2019, 05:59 15
Optimus11, почему бы просто не почитать про этот тип? Например начать где-то тут https://rsdn.org/article/com/varsafearr.xml
0
19 / 18 / 1
Регистрация: 10.01.2017
Сообщений: 944
19.03.2019, 18:58 16
Прочитал, но ответа не нашел.
0
Модератор
3326 / 2111 / 339
Регистрация: 13.01.2012
Сообщений: 8,202
19.03.2019, 19:44 17
Optimus11, где код демонстрирующий ваш парадокс?
0
19 / 18 / 1
Регистрация: 10.01.2017
Сообщений: 944
19.03.2019, 21:15 18
Здравствуйте,

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
...
VARIANT arr;
    arr.vt = VT_ARRAY | VT_VARIANT;
    {
        SAFEARRAYBOUND sab[2];
        //sab[0].lLbound = 1; sab[0].cElements = 1; 
        //sab[1].lLbound = 1; sab[1].cElements = 1;
        arr.parray = SafeArrayCreate(VT_VARIANT, 2, sab);
    }
 
    AutoWrap_1(DISPATCH_PROPERTYGET, &arr, pXlRange, (LPOLESTR)L"Value", 0); //  pXlRange - может содержать в себе диапазон любого размера
 
    long iUBound;
    SafeArrayGetUBound(arr.parray, 1, &iUBound);
 
    long lUBound;
    SafeArrayGetLBound(arr.parray, 1, &lUBound);
То есть строки:
sab[0].lLbound = 1; sab[0].cElements = 1;
sab[1].lLbound = 1; sab[1].cElements = 1;

Ни как не влияют на создание и заполнение массива arr, зачем тогда они нужны ?
Если с них снять // комментирование, то все тоже самое, все заполняется любым кол-ом данных.

Добавлено через 1 час 18 минут
А ннет, я очень ошибся, создается одномерный массив из любого числа элементов, а вот уже двумерный и более начинает косячится.
0
19 / 18 / 1
Регистрация: 10.01.2017
Сообщений: 944
20.03.2019, 08:43 19
C++
1
2
long iUBound;
SafeArrayGetUBound(arr.parray, 1, &iUBound);
Некорректно определяет размер массива, грубо говоря по ширине. По LBound - определяет верно.
0
Модератор
3326 / 2111 / 339
Регистрация: 13.01.2012
Сообщений: 8,202
20.03.2019, 19:04 20
Optimus11, это вопрос или утверждение и в любом случае где данные над которыми производится тест?
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
20.03.2019, 19:04

Заказываю контрольные, курсовые, дипломные и любые другие студенческие работы здесь.

импорт выделенного диапазона
добрый день!у меня есть таблица в Еxcel ee нужно импортировать, но только тот диапазон который был...

Экспорт-импорт данных Excel-Access-Excel
Доброе время суток уважаемые форумчане. Приходит информация в экселевской таблице. Скидываю её в...

Обработка Excel и импорт из Excel в Access
Прошу совета профессионалов. С Excel-м ранее дел не имел. Задание: файлы Excel из одной папки...

Excel -> Access -> Excel, импорт - экспорт
Добрый день! Есть табель учета рабочего времени в таблице Excel, задача - переместить данные со...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2021, vBulletin Solutions, Inc.