Code: Select all
/*
This test program will export Excel-Sheet to Work Area DBF
*/
#INCLUDE "inkey.ch"
#INCLUDE "dcdir.ch"
#INCLUDE "appevent.ch"
#INCLUDE "xbp.ch"
#INCLUDE "dcprint.ch"
#INCLUDE "dcmsg.ch"
#INCLUDE "dll.ch"
#include "dccursor.ch"
#Include "thread.ch"
#INCLUDE "class.CH"
#INCLUDE "dmlb.CH"
#INCLUDE "fileio.CH"
#INCLUDE "dctree.CH"
#INCLUDE "dcicon.CH"
#INCLUDE "dcdialog.CH"
#INCLUDE "outlook.CH"
#pragma Library("ascom10.lib")
#pragma library( "dclip1.lib" )
#pragma library( "dclip2.lib" )
#pragma library( "dclipx.lib" )
#pragma library( "xbtbase1.lib" )
#pragma library( "xbtbase2.lib" )
#pragma library( "xppui2.lib" )
FUNCTION Main()
SET COLLATION TO SYSTEM // Руссификация
*SET COLLATION TO ASCII // Руссификация
PUBLIC cExcelFile := 'Inp_data'
PUBLIC cDbaseFile := cExcelFile
MsgBox('Преобразование Excel-файла в DBF-файл"')
IF LEN(ALLTRIM(cExcelFile)) = 0
MsgBox('Задайте в качестве параметра имя конвертируемого Excel-файла без расширения: "XLS-DBF FileName"')
RETURN NIL
ENDIF
***** В будущем сделать определение по расширению какой Excel: 2003 или 2010
***** Проверить, как будет работать для 2010
CLOSE ALL
cExcelFile = DC_CurPath() + '\' + ALLTRIM(cExcelFile) + '.xlsx'
LC_Excel2WorkArea( cExcelFile )
RETURN nil
* ---------------
PROC appsys ; RETURN
* -------------
FUNCTION LC_Excel2WorkArea( cExcelFile )
LOCAL lStatus := .f., oExcel, cPath, oSheet, oBook, aValues, i, j, ;
aStru, xValue
LOCAL oProgressm, oDialogm, lOk
#if XPPVER > 1900000
// Create the "Excel.Application" object
oExcel := CreateObject("Excel.Application")
IF Empty( oExcel )
DC_WinAlert( "Excel is not installed" )
RETURN .f.
ENDIF
#else
DC_WinAlert('This feature is available in Xbase++ 1.9 and later only!')
RETURN .f.
#endif
oExcel:Visible := .f.
// Load a Workbook from an .XLS file
// Get path from ini file
IF !File(cExcelFile)
DC_WinAlert( 'File does not exist:' + Chr(13) + cExcelFile )
RETURN lStatus
ENDIF
oScrn := DC_WaitOn( 'Открытие XLS-файла' )
oBook := oExcel:Workbooks:Open(cExcelFile)
oSheet := oBook:activeSheet
aValues := oBook:workSheets(1):usedRange:value // Загрузка 1-го листа Excel-книги
DC_Impl(oScrn)
*DC_DebugQout( { aValues[1], aValues[2], aValues[3] } )
*DC_DebugQout( { aValues } )
*MsgBox('Stop')
********* Сформировать файл Inp_name.txt // Наименования классификационных и описательных шкал
aInp_name := aValues[1] // Имена колонок 1-го листа Excel-книги
N_Rec = LEN(aValues) // Количество строк с данными в xls-файле
N_Col = LEN(aInp_name) // Количество колонок с данными в xls-файле
*MsgBox(STR(N_rec,15))
CrLf = CHR(13)+CHR(10) // Конец строки (абзаца) (CrLf)
mCol_name = ""
FOR j=1 TO N_Col
mNameJ = ALLTRIM(aInp_name[j])
mNameJ = UPPER(SUBSTR(mNameJ,1,1)) + SUBSTR(mNameJ,2) // Сделать первые символы заголовков колонок большими, а остальные оставить как есть
mCol_name = mCol_name + mNameJ + CrLf
NEXT
StrFile(mCol_name, 'Inp_name.txt')
// Создание пустой dbf-таблицы для записи в нее данных из 1-го листа xls-файла ******************
// Определение структуры для dbf-таблицы по значениям всех строк (тип и формат данных в колонках)
// На основе просмотра ВСЕГО xls-файла сделать массивы описания полей и для создания структуры брать MAX из каждого массива
// В dbf-таблицу переносить данные, начиная со 2-й строки xls-листа
// 1-ю строку с наименованиями полей использовать вместо Inp_name.txt или чтобы автоматически его делать
aStructure := {} // Массив структуры dbf-таблицы
PRIVATE aFieldName[N_col] // Наименования колонок для базы данных Inp_data.dbf
PRIVATE aFieldType[N_Col] // Тип значений в j-й колонке (если есть хотя бы одно текстовое, то текстовое)
PRIVATE aFieldSize[N_Col] // Максимальный размер значения в j-й колонке в символах
PRIVATE aFieldInt [N_Col] // Максимальное количество значащих цифр до десятичной точки в значениях j-й колонки
PRIVATE aFieldDeci[N_Col] // Максимальное количество значащих цифр после десятичной точки в значениях j-й колонки
PRIVATE aFlag_neg [N_Col] // Флаг наличия отрицательных значений в j-й колонке
AFILL(aFieldType, "X" )
AFILL(aFieldSize, 1 )
AFILL(aFieldInt , 1 )
AFILL(aFieldDeci, 0 )
AFILL(aFlag_neg , .F. )
FOR j=1 TO N_Col
aFieldName[j] = "N"+ALLTRIM(STR(j, 15))
NEXT
***** Отображение стадии исполнения в кратком варианте *****************************************
nMax = N_Col + N_Rec - 1
nTime = 0
@ 4,5 DCPROGRESS oProgressm SIZE 70,1.1 MAXCOUNT nMax COLOR GRA_CLR_CYAN PERCENT EVERY 100
DCREAD GUI TITLE 'Идет преобразование XLS-файла в базу данных !!!' PARENT @oDialogm FIT EXIT
oDialogm:show()
DC_GetProgress(oProgressm,0,nMax)
************************************************************************************************
FOR j=1 TO N_Col // Цикл по колонкам
FOR i=2 TO N_Rec // Цикл по строкам
mFieldType = VALTYPE(aValues[i,j])
DO CASE
CASE mFieldType = "C" // **************************************************************************
aFieldType[j] = "C" // Тип значений в j-й колонке (если есть хотя бы одно текстовое, то текстовое)
aFieldSize[j] = MAX(aFieldSize[j], LEN(ALLTRIM(aValues[i,j])))
aFieldDeci[j] = 0
CASE mFieldType = "N" // **************************************************************************
IF aValues[i,j] < 0
aFlag_neg[j] = .T.
ENDIF
IF aFieldType[j] <> "C"
aFieldType[j] = mFieldType
ENDIF
mVal = DC_XtoC(aValues[i,j])
REMRIGHT(mVal,"0") // Убрать подряд идущие нули справа до 1-й значащей цифры
Pos = AT('.', MVal)
DO CASE
CASE Pos = 0 // Целое число
aFieldInt [j] = MAX(aFieldInt [j], LEN(ALLTRIM(mVal))) // Определить число значащих цифр ДО запятой без ведущих нулей
aFieldDeci[j] = 0
// Если отрицательное, то символ на знак
aFieldSize[j] = MAX(aFieldSize[j], aFieldInt[j] + IF(aFlag_neg[j],1,0))
CASE Pos > 0 // Число с дробной частью
aFieldInt [j] = Pos - 1 // Определить число значащих цифр ДО запятой без ведущих нулей
mFieldDeci = LEN(ALLTRIM(mVal)) - aFieldInt [j] - 1
aFieldDeci[j] = MAX(aFieldDeci[j], mFieldDeci)
// Если с дробной частью, то плюс символ на точку
// Если отрицательное, то символ на знак
aFieldSize[j] = MAX(aFieldSize[j], aFieldInt[j] + aFieldDeci[j] + 1 + IF(aFlag_neg[j],1,0))
ENDCASE
CASE mFieldType = "D" // **************************************************************************
aFieldType[j] = "C"
aFieldSize[j] = 8
aFieldDeci[j] = 0
ENDCASE
NEXT
AADD(aStructure, { aFieldName[j] , aFieldType[j], aFieldSize[j], aFieldDeci[j] }) // Добавить строку в структуру dbf-таблицы
DC_GetProgress(oProgressm, ++nTime, nMax)
NEXT
*DC_DebugQout( { aStructure } )
*MsgBox('Stop')
cDbaseName = cDbaseFile
cDbaseFile = cDbaseFile + '.dbf'
DbCreate( cDbaseFile , aStructure )
***** Цикл переноса данных из xls-листа в dbf-таблицу
CLOSE ALL
cDbaseName = SUBSTR(cDbaseFile, 1, AT('.',cDbaseFile)-1)
USE (cDbaseName) EXCLUSIVE NEW;ZAP
SELECT (cDbaseName)
FOR i=2 TO N_Rec
DBAPPEND()
FOR j=1 TO N_Col
// Преобразовывать значения с типами числа и даты в текст, если тип поля текст
DO CASE
CASE VALTYPE(aValues[i,j]) = 'C' .AND. aFieldType[j] = 'C'
FIELDPUT(j, aValues[i,j]) // Просто занести
CASE VALTYPE(aValues[i,j]) = 'N' .AND. aFieldType[j] = 'N'
FIELDPUT(j, aValues[i,j]) // Просто занести
CASE VALTYPE(aValues[i,j]) = 'N' .AND. aFieldType[j] = 'C'
FIELDPUT(j, STR(aValues[i,j], aFieldSize[j], aFieldDeci[j] )) // Преобразовать число к текстовому виду
CASE VALTYPE(aValues[i,j]) = 'D' .AND. aFieldType[j] = 'C'
FIELDPUT(j, DTOC(aValues[i,j])) // Преобразовать дату к текстовому виду
ENDCASE
NEXT
DC_GetProgress(oProgressm, ++nTime, nMax)
NEXT
* IF Valtype(xValue) == 'N' .AND. aStru[j,2] == 'C'
* xValue := DC_XtoC(xValue)
* ELSEIF aStru[j,2] == 'D' .AND. Valtype(xValue) == 'N'
* xValue := Str(xValue)
* xValue := StoD(xValue)
* ELSEIF aStru[j,2] == 'D' .AND. Valtype(xValue) == 'C'
* xValue := Ctod(xValue)
* ELSEIF aStru[j,2] == 'C' .AND. Valtype(xValue) == 'D'
* xValue := DtoS(xValue)
*ENDIF
* MsgBox(DC_XtoC(000123.12345000)) // Надо убрать подряд идущие нули справа до 1-й значащей цифры
DC_GetProgress(oProgressm,nMax,nMax)
oDialogm:Destroy()
oBook:close()
oBook:destroy()
// Quit Excel
oExcel:Quit()
oExcel:Destroy()
RETURN lStatus
* -------------
Can be to eat possibility of access to information on formats and types of data of the xls-file not to learn these parameters by the analysis of directly values. It would be very good since values can be much and it can take noticeable time.
Still I didn't lick into shape definition of formats of numerical values with fractional part.
In the future I want to make option of this function which can be started from a command line with parameter - the name xls or the xlsx-file.
For some reason doesn't write in headings of windows in Russian.