Участник
Регистрация: 28.11.2005
Адрес: Москва
|
Цитата:
Сообщение от _and
Импорт Excel при последовательном чтении из ячеек работает очень медленно (у меня ~500 ячеек в секунду, таблица из строк в 52 ячейки). Хотелось бы считывать данные построчно - скажем, считывать строку в массив и потом обрабатывать. Такое возможно?
Есть чудесный пример импорта данных через ADO. В частности, я таким образом переделывал импорт номенклатуры из Excel, в результате скорость возрасла чуть ли не на порядок. А если его еще обернуть в красивую формочку, то вообще красота получится. Я для импорта номенклатуры использовал такой вот заточенный под указанную формочку класс:
PHP код:
class Uni_ImportInventFromExcelADO
{
COM cnnExcel, rstExcel, flds; // ADO: Connection, Recordset, Fields
LanguageId languageId;
int iMaxFields, nStartRow, nCountLines;
str strFileName, strSheetName;
}
anytype adoFieldValue(int _iIdx, boolean _asString = true)
{
COM fld; // ADO: Field
;
fld = flds.Item(_iIdx);
return (_asString ? this.adoStrValueFromExcel(fld.Value(), fld.Type(), true) : this.adoValueFromExcel(fld.Value(), fld.Type()));
}
str adoStrValueFromExcel(COMVariant _val, int _type, boolean bDblRound = false)
{
#CCADO
switch (_type)
{
case #adDouble: return num2str(_val.double(),-1,(bDblRound ? 0 : -1),1,0);
case #adCurrency: return num2str(_val.currency(),-1,-1,1,0);
case #adDate: return date2str(_val.date(),123,2,2,2,2,4);
case #adBoolean: return int2str(_val.boolean());
case #adVarWChar,
#adLongVarWChar: return _val.bStr();
}
return '';
}
anytype adoValueFromExcel(COMVariant _val, int _type)
{
#CCADO
switch (_type)
{
// constants for types recognized by ADO for Excel
case #adDouble: return _val.double();
case #adCurrency: return _val.currency();
case #adDate: return _val.date();
case #adBoolean: return _val.boolean();
case #adVarWChar,
#adLongVarWChar: return _val.bStr();
}
return '';
}
void finalize()
{
rstExcel.Close();
cnnExcel.Close();
}
// get Excel book active sheet name to use as a table name in select
protected str getExcelSheetName()
{
ComExcelDocument_RU Excel;
COM comDoc, comApp, Sheet;
if(strSheetName) // if we've been already called successfully...
return strSheetName;
try
{
Excel = new ComExcelDocument_RU();
if(Excel.open(strFileName, false)) // Excel - don't show up
{
comDoc = Excel.getComDocument();
comApp = comDoc.Application();
Sheet = comApp.ActiveSheet();
strSheetName = Sheet.name();
Excel.quitApplication(true); // true: Excel - don't ask to save the file
Excel.finalize();
}
}
catch (Exception::Error)
{
Excel.quitApplication(true);
Excel.finalize(); // get lost anyway
}
catch (Exception::Internal)
{
Excel.quitApplication(true); // emulate try/finally
Excel.finalize();
}
return strSheetName;
}
int getLinesReadCount()
{
return nCountLines;
}
int getRecordCount()
{
#define.MaxRecordWeSupposeToRead(10000)
int i = #MaxRecordWeSupposeToRead;
;
if(rstExcel && rstExcel.RecordCount()>=0)
i = rstExcel.RecordCount();
return i;
}
void new(str _fileName , int _nStartRow)
{
rstExcel = null;
cnnExcel = null;
nCountLines = 0;
strSheetName= '';
strFileName = _fileName;
nStartRow = _nStartRow;
languageId = CompanyInfo::languageId();
}
boolean openFile()
{
#CCADO
// we need at least this number of columns
#define.ExcelIdxMax(8)
#localmacro.ADODBExcelConnectionString
"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + %1 + ";Extended Properties='Excel 8.0;HDR=Yes;IMEX=1'"
#endmacro
boolean ok;
;
ok = (this.getExcelSheetName() != ''); // first try to open file via COM
if(ok)
{
cnnExcel = new COM("ADODB.Connection");
cnnExcel.connectionString(#ADODBExcelConnectionString(strFileName));
cnnExcel.Open();
rstExcel = new COM("ADODB.Recordset"); // #adOpenStatic to be able to read RecordCount
rstExcel.Open("SELECT * FROM [" + strSheetName + "$]", cnnExcel, #adOpenForwardOnly);
// check if we have enough fields to read
flds = rstExcel.Fields();
iMaxFields = flds.Count() - 1;
if(iMaxFields < #ExcelIdxMax)
ok = false;
}
return ok;
}
boolean importNext()
{
InventTable inv;
;
if(rstExcel.EOF()) // no more data
return false;
// import using this.adoFieldValue()
rstExcel.MoveNext();
nCountLines++;
return true;
}
Соотв., в формочке сначала идет вызов openFile(), для статистики берется общее количество строк getRecordCount() и вызывается importNext(), пока этот метод не вернет false. Градусник на форме можно обновлять по данным getLinesReadCount().
По поводу получения количества записей из ADODB.RecordSet. Для типа курсора adOpenForwardOnly rstExcel.RecordCount() всегда будет возвращать 0. Чтобы получать реальное число записей, надо открывать RecordSet с курсором adOpenStatic, но тогда на большом файле получатся ощутимые тормоза при открытии и лишний расход памяти.
Последний раз редактировалось gl00mie; 08.06.2006 в 14:41.
|