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

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

05.05.2015, 21:20. Просмотров 7246. Ответов 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
19 / 18 / 1
Регистрация: 10.01.2017
Сообщений: 944
20.03.2019, 19:29 21
Данные находятся в екселе, заносятся в массив arr, их оттуда благополучно можно извлечь по номеру элемента, как Вы и показали в примере чуть выше, но результат работы вот этой строки всегда неверные или я что то не верно делаю:

C++
1
2
3
HRESULT hresult;
long iUBound;
hresult=SafeArrayGetUBound(arr.parray, 1, &iUBound); // статус hresult = S_OK
Но в iUBound записывается всегда значение 1, хотя нижняя граница массива больше одного и по номеру элемента оттуда можно достать данные.
Но при той же абсолютно записи SafeArrayGetLBound - число столбцов массива отображается верно.
Не знаю что не так.
0
Модератор
3326 / 2111 / 339
Регистрация: 13.01.2012
Сообщений: 8,202
20.03.2019, 19:36 22
Optimus11, судя по коду вы запрашиваете верхнюю границу первого измерения много мерного массива

Добавлено через 1 минуту
Optimus11, покажите скрин экселя и назовите какие значения вам кажется должна вернуть функция

Добавлено через 25 секунд
... и какие она реально возвращает
0
19 / 18 / 1
Регистрация: 10.01.2017
Сообщений: 944
20.03.2019, 21:26 23
Да скриншот с экрана екселя не нужен в принципе.
Вот 'nb строчки кода берут диапазон из екселя:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
...
IDispatch *pXlRange;
    {
                VARIANT parm;
        parm.vt = VT_BSTR
        parm.bstrVal = ::SysAllocString(L"A1:D10"); // Данные из этого диапазона заносятся в arr;
 
        VARIANT result;
        VariantInit(&result);
        AutoWrap_1(DISPATCH_PROPERTYGET, &result, pXlSheet, (LPOLESTR)L"Range", 1, parm);
        VariantClear(&parm);
        pXlRange = result.pdispVal;
    }
И соответственно:

long iUBound;
SafeArrayGetUBound(arr.parray, 2, &iUBound); // И соответственно iUBound получается равным = 1

long lUBound;
SafeArrayGetLBound(arr.parray, 2, &lUBound);/ И соответственно lUBound получается равным = 4, соответствует ширине диапазона.

Вот как то так получается.
0
Модератор
3326 / 2111 / 339
Регистрация: 13.01.2012
Сообщений: 8,202
22.03.2019, 07:59 24
Optimus11, иии... какие вам значения не нравятся?
0
19 / 18 / 1
Регистрация: 10.01.2017
Сообщений: 944
22.03.2019, 08:44 25
Вот это SafeArrayGetUBound(arr.parray, 2, &iUBound), где iUBound=1

Я понимаю, что я чего то не понимаю, но хотелось бы получить номер нижней границы массива по вертикали, но немогу реально понять как.
0
Модератор
3326 / 2111 / 339
Регистрация: 13.01.2012
Сообщений: 8,202
23.03.2019, 16:19 26
Optimus11, я вас не понимаю. вот есть код
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
        VARIANT addr;
        VARIANT range;
        VARIANT value;
 
        VariantInit(&addr);
        VariantInit(&range);
        VariantInit(&value);
 
        while (true) {
 
        addr.vt = VT_BSTR;
        addr.bstrVal = SysAllocString(L"B3:D6");
        BREAK_ON_FAIL(AutoWrap(DISPATCH_PROPERTYGET, &range, xls.ws.pdispVal, L"Range", 1, addr))
        BREAK_ON_FAIL(AutoWrap(DISPATCH_PROPERTYGET, &value, range.pdispVal, L"Value", 0))
 
        std::cout << V_VT(&value) << std::endl;
 
        SAFEARRAY *sa = V_ARRAY(&value);
 
        long i_min, i_max;
        SafeArrayGetLBound(sa, 1, &i_min);
        SafeArrayGetUBound(sa, 1, &i_max);
 
        std::cout << "i_min = " << i_min << std::endl;
        std::cout << "i_max = " << i_max << std::endl;
 
        long j_min, j_max;
        SafeArrayGetLBound(sa, 2, &j_min);
        SafeArrayGetUBound(sa, 2, &j_max);
 
        std::cout << "j_min = " << j_min << std::endl;
        std::cout << "j_max = " << j_max << std::endl;
 
        break;}
 
        VariantClear(&addr);
        VariantClear(&range);
        VariantClear(&value);
он выводит вот это
8204
i_min = 1
i_max = 4
j_min = 1
j_max = 3
это верно - в value находится VARIANT с типом 8204 - это VT_ARRAY | VT_VARIANT поэтому можно без страха оперировать с ним как с SAFEARRAY - у первого измерения границы (1, 4), у второго (1, 3) - это верно так как диапазон B3 : D6 содержит 4 строки и три столбца. или вы думаете что диапазон должен показать вам в качестве нижней и верхней границ номера строк / столбцов ? не должен. это не его дело.
1
19 / 18 / 1
Регистрация: 10.01.2017
Сообщений: 944
23.03.2019, 23:09 27
думаете что диапазон должен показать вам в качестве нижней и верхней границ номера строк / столбцов ? не должен. это не его дело.
Спасибо, именно так я ошибочно и думал.
0
19 / 18 / 1
Регистрация: 10.01.2017
Сообщений: 944
24.03.2019, 19:36 28
А может быть можете пожалуйста подсказать в чем разница между:

1)
C++
1
2
3
4
VT_I4   4-byte signed integer value.
VT_I8   8-byte signed integer.
и
VT_R8   64-bit IEEE floating point value.
То есть получается так, что когда я читаю данные из ексель и в ячейке ексель находится екселевской числовое значение, то когда пробегаюсь по элеменнтам массива куда были записанные данные из екселя, то в элеменете Variant срабатывает:
C++
1
2
3
if (V_VT(&arr_element) == VT_R8)
{
}
VT_I4 и VT_I8 - не срабатывает.

Но, когда создается variant массив и туда записываются данные для последующей записи в екель:
C++
1
2
3
4
5
6
7
8
9
10
11
for (int i = 1; i <= 15; i++) {
        for (int j = 1; j <= 15; j++) {
 
            VARIANT tmp;
            tmp.vt = VT_I4; 
            tmp.lVal = i * j;
 
            long indices[] = { i,j };
            SafeArrayPutElement(arr_write.parray, indices, (void *)&tmp);
        }
    }
То в variant массив записываются данные с типом VT_I4 и далее в ексель они записываются корректно, можно в массив добавить и тип VT_R8, но тогда в ексель записывается какие то совершенно другие числа.
Не пойму почему так.

2) И если можно такой вопрос: правильно ли я понимаю, что разница между VT_R8 и VT_R4 - только в разрядности виндовса ? То есть если у меня виндовс 64-разр., то условие:
C++
1
2
3
if (V_VT(&arr_element) == VT_R4)
{
}
Не сработает ? А в 32ух разр., не сработает соответственно VT_R8 ?
0
Модератор
3326 / 2111 / 339
Регистрация: 13.01.2012
Сообщений: 8,202
24.03.2019, 20:35 29
Optimus11, 1) в ячейках excel хранится VARIANT и он сам там по идее преобразует все куда надо если надо - если пишите R8 то и запишет R8 если конечно вы его в какое надо поле положите, обратно выдаёт наверное унифицированный тип - или строка или плавающая точка 2) нет, разрядности не имеет к этому отношения, R8 это C++ double, R4 это C++ float
1
19 / 18 / 1
Регистрация: 10.01.2017
Сообщений: 944
30.03.2019, 20:03 30
Я сдаюсь, пытаюсь теперь записать данные в екель, берут тот же микрософтовский пример, он конечно же работает, но записывает в ексель числа VT_I4, а мне нужно чтобы записывался тектовый формат, то есть BSTR, вот кусочек работающего кода:

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
...
// Create a 15x15 safearray of variants...
    VARIANT arr_write;
    arr_write.vt = VT_ARRAY | VT_VARIANT;
    {
        SAFEARRAYBOUND sab[2];
        sab[0].lLbound = 1; sab[0].cElements = 15;  
        sab[1].lLbound = 1; sab[1].cElements = 15; 
        arr_write.parray = SafeArrayCreate(VT_VARIANT, 2, sab);
    }
 
VARIANT tmp; 
tmp.vt = VT_BSTR;
 
string string_value = "001";
 
// Fill safearray with some values...  /// 
    for (int i = 1; i <= 15; i++) {
        for (int j = 1; 15; j++) {
 
tmp.bstrVal = ::SysAllocString(_com_util::ConvertStringToBSTR(string_value.c_str())); //Преобразование string в bstr
 
long indices[] = { i,j };
 
SafeArrayPutElement(arr_write.parray, indices, (void *)&tmp);
        }
    }
И данные записываются, но записывается, только "1", а не "001", то есть в виде в формате числа, а не текстовом формате.
Причем методом тыка мне как то удалось записать именно в текстовом формате, вроде бы я обьявил массив, как BSTR, что то типа:

C++
1
arr_write.vt = VT_ARRAY | VT_BSTR;
Но потом решил поэксперементировать и забыл, что я сделал (((
0
Модератор
3326 / 2111 / 339
Регистрация: 13.01.2012
Сообщений: 8,202
31.03.2019, 06:57 31
Optimus11, ввести цифру как текст даже руками бывает не просто) если это не принципиально - вводите ‘001
0
19 / 18 / 1
Регистрация: 10.01.2017
Сообщений: 944
31.03.2019, 08:09 32
Бывает же такое, открыл с утра Ваше сообщение и сразу вспомнил, что я делал :
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
// Create a 15x15 safearray of variants...
    VARIANT arr_write;
    arr_write.vt = VT_ARRAY | VT_BSTR;   // Тут объявляется, что VT_Array должен быть VT_BSTR
    {
        SAFEARRAYBOUND sab[2];
        sab[0].lLbound = 1; sab[0].cElements = vector_stroka_for_write.size();  
        sab[1].lLbound = 1; sab[1].cElements = vector_stolbech_for_write.size(); 
        arr_write.parray = SafeArrayCreate(VT_BSTR, 2, sab);   // А тут забыл, так же указать, что ArrayCreate будет VT_BSTR
    }
 
BSTR tmp_bstr;  // объявляем переменную из которой будет заносится данные в arr_write
 
string string_value = "001";
 
for (int i = 1; i <= 15; i++) {
        for (int j = 1; j <= 15; j++) {
 
 
tmp_bstr = _com_util::ConvertStringToBSTR(string_value.c_str());
 
///////// 
//VARIANT tmp; 
//tmp.bstrVal; // через tmp.bstrval - не работает, ошибок нет, но в екcель ничего не вносится.
/////////
 
    long indices[] = { i,j };
 
    SafeArrayPutElement(arr_write.parray, indices, tmp_bstr);
}
 }
 
AutoWrap_write(DISPATCH_PROPERTYPUT, NULL, pXlRange, (LPOLESTR)L"Value", 1, arr_write);
0
19 / 18 / 1
Регистрация: 10.01.2017
Сообщений: 944
02.04.2019, 20:40 33
Позвольте еще один вопрос с участием все той же замечательной autowrap функцией:

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
Autowrap()
 
int main()
{
CoInitialize(NULL);
CLSID clsid;
 
HRESULT hr = CLSIDFromProgID(L"Excel.Application", &clsid); 
 
IUnknown* pUnk;
hr = GetActiveObject(clsid, NULL, &pUnk);
 
IDispatch *pXlApp;
hr = pUnk->QueryInterface(IID_IDispatch, (void**)&pXlApp);
 
IDispatch *pXlSheet;
    {
        VARIANT result;
        VariantInit(&result);
        AutoWrap_get_sheet_name(DISPATCH_PROPERTYGET, &result, pXlApp, (LPOLESTR)L"ActiveSheet", 0);
        pXlSheet = result.pdispVal;
    }
 
VARIANT rngResult;
AutoWrap_get_sheet_name(DISPATCH_PROPERTYGET, &rngResult, pXlSheet, (LPOLESTR)L"Name", 0);
}
Соответсвенно в rngResult получаем имя аквтиного на данный момент листа.
Но, когда я пытаюсь применить тот же код для получения имени рабочей книги, то есть вот так:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
...
 
IDispatch *pXlBooks;
    {
        VARIANT result;
        VariantInit(&result);
        AutoWrap_get_sheet_name(DISPATCH_PROPERTYGET, &result, pXlApp, (LPOLESTR)L"Workbooks", 0);
        pXlBooks = result.pdispVal;
    }
 
 
VARIANT rngResult;
AutoWrap_get_sheet_name(DISPATCH_PROPERTYGET, &rngResult, pXlBooks, (LPOLESTR)L"Name", 0);
Почему ругается на IDispatch::GetIDsOfNames<"Names">faild e/err 0x800200006

То есть получается метод или свойство Name для или Activesheets находится, а для Workbook нет ?

Добавлено через 21 минуту
Все разобрался - ActiveWorkBook!
0
19 / 18 / 1
Регистрация: 10.01.2017
Сообщений: 944
05.04.2019, 19:47 34
Не могу понять, вот есть предположим 10 открытых файла из которых предположим нужно из ячейки "A1" считать данные.
Понятно, как считать данные из активного на данный момент файла и активного в нем листа, через autowrap:

C++
1
2
3
4
5
6
7
IDispatch *pXlsheet;
    {
        VARIANT result;
        VariantInit(&result);
        AutoWrap_Choose_Active(DISPATCH_PROPERTYGET, &result, pXlApp, (LPOLESTR)L"ActiveSheets", 0);
        pXlsheets = result.pdispVal;
    }
Или из активного файла, но другого листа в нем:
C++
1
2
3
4
5
6
{
        VARIANT result;
        VariantInit(&result);
        AutoWrap_Choose_Active(DISPATCH_PROPERTYGET, &result, pXlApp, (LPOLESTR)L"Worksheets", 1, (LPOLESTR)L"Лист2" );
        pXlsheets = result.pdispVal;
}
и соответсвенно:
C++
1
2
VARIANT rngResult;
AutoWrap(DISPATCH_PROPERTYGET, &rngResult, pXlRange, (LPOLESTR)L"Value", 0);
То есть зная название листов в активном Workbook`ё можно пройтись по ним и считать с них данные, я думал примерно так же можно пройтись и по списку WorkBooks зная имена файлов который открыты:
C++
1
2
3
4
5
6
7
IDispatch *pXlsheet;
    {
        VARIANT result;
        VariantInit(&result);
        AutoWrap_Choose_Active(DISPATCH_PROPERTYGET, &result, pXlApp, (LPOLESTR)L"WorkBooks", 1, (LPOLESTR)L"Files1" );
        pXlsheets = result.pdispVal;
    }
Но так возникает ошибка:
C++
1
IDispatch:Invoke<"WorkBooks"=0000023c> faild w/err 0x80020009
0
19 / 18 / 1
Регистрация: 10.01.2017
Сообщений: 944
07.04.2019, 20:14 35
Для этого есть метод Activate.
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
07.04.2019, 20:14

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

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

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

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

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


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

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

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