I don’t know much about Object Pascal
or Delphi, but I will try to provide a reasonable response.
Security with the original file
The reason for this is to prevent two applications from accessing the same file, Excel creates a LOCK in the file and this causes some locks and even when it is not with the LOCK, there may be conflict if your software is working the XLS at the same time as in Excel is clicked Ctrl+B (Save).
To avoid conflicts the best way is to generate a copy of the file, for this I used this function (source: http://www.swissdelphicenter.ch/torry/showcode.php?id=385):
function GetTempFile(const Extension: string): string;
var
Buffer: array[0..MAX_PATH] of Char;
begin
repeat
GetTempPath(SizeOf(Buffer) - 1, Buffer);
GetTempFileName(Buffer, '~', 0, Buffer);
Result := ChangeFileExt(Buffer, Extension);
until not FileExists(Result);
end;
Selecting the XLS file
In case I chose to select both XLS and XLSX, because I believe it is more likely to use the latest format (mainly if it is a software for Windows), this way it is possible to give support for both formats (depending on the installed version of Excel):
var
selectedFile: String;
PromptForFileName(selectedFile, 'Documento do Excel (*.xls, *.xlsx)|*.xls; *.xlsx', '', 'Selecione...', 'C:\', False)
Reading the XLS file
I created a reading function (with password and no password) combined with the function GetTempFile
, she returns Boolean
and the parameter var handler
returns the file opened. To open a file without password do something like openExcel(handler; 'arquivo.xls'; '');
, password-protected openExcel(handler; 'arquivo.xls'; 'senha');
function openExcel(var handler: Variant; selectedFile: String; pass: String): Boolean;
begin
EndExcel();
tmpFile := GetTempFile('.~tp');
CopyFile(pchar(selectedFile), pchar(tmpFile), true);
xlsExcel := CreateOLEObject('Excel.Application');
xlsExcel.Visible := False;
try
handler := xlsExcel.WorkBooks.Open(selectedFile, Password := password, ReadOnly := True);
Result := True;
except
showmessage('excepet');
EndExcel();
handler := Unassigned;
Result := False;
end;
end;
Closing the XLS
Note that the process EXCEL.EXE
does not end, even if it closes the application, because it is a ActiveX
, ie it runs separately, for this I created a function called EndExcel
to finish is always necessary and clean the variable.
procedure EndExcel();
begin
if VarIsEmpty(xlsExcel) = False then
begin
xlsExcel.Quit;
xlsExcel := Unassigned;
end;
if tmpFile <> '' then
begin
DeleteFile(tmpFile);
end;
end;
Authenticating the XLS
Authentication will sometimes be necessary and is a requirement you mentioned, for this I created an event that works combined with the openExcel
, to call it just run authExcel('arquivo.xls');
. Will open a prompt
to select a file, the function itself will test if password is needed, if it is necessary then a prompt will appear asking to enter such password, follows the function:
function authExcel(fileStr: String) : Variant;
var
password: String;
buttonSelected: Integer;
xlsTmp: Variant;
begin
password := InputBox('Autenticação', 'Senha do arquivo', '');
if password = '' then
begin
ShowMessage('Nenhuma senha digitada');
Result := Unassigned;
end
else
begin
if openExcel(xlsTmp, fileStr, password) then
begin
Result := xlsTmp
end
else
begin
buttonSelected := MessageDlg('Senha inválida, tentar novamente?', mtCustom, [mbYes, mbCancel], 0);
if buttonSelected = mrYes then
begin
Result := authExcel(fileStr);
end
else
begin
ShowMessage('Cancelado');
Result := Unassigned;
end;
end;
end;
end;
Searching in the first column
I was gonna make a array
that would bring all results, but I chose to create function with search for "joker" (if necessary) or by keyword, this search is done in the column you want, but as the case is to search in the first then I added a parameter to change as needed, this function returns a multidimensional array of this type array of array of string
The function is like this:
findInExcel(xls: Variant; col: Integer; query: String; limitCols: Integer): TStringArrayArray
col
refers to the column you wish to view, query
will be the research and limitCols
is to avoid copying extra columns right, note also that after using findInExcel
used the EndExcel()
, but remove this part if you still need the handler
(xls: Variant), following examples of use:
Search in the first column, containing the word a
and no right column limit (the function itself will try to limit):
findInExcel(handler, 1, 'a', 0);
EndExcel();
Search in the first column, which contains anything (the function will try to avoid empty columns) and with limit of 4 columns to the right:
findInExcel(handler, 1, '*', 4);
EndExcel();
Search in the second column, starting with the letter A (for example A0001
, A0002
) and with a limit of 5 columns to the right:
findInExcel(handler, 2, 'A*', 5);
EndExcel();
Search in the first column, ending with the letter B (for example 0001B
, 2B
) and no right column limit (the function itself will try to limit):
findInExcel(handler, 1, '*B', 0);
EndExcel();
How to configure the file pas
:
- Add to
uses
the ComObj
and StrUtils
for the functions to function
- Add to
type
this TStringArrayArray = Array of Array of String;
- Add the global variables
xlsExcel: Variant;
and tmpFile: String;
Example of use (tested in Delphi7):
I’m not going to talk about Firebird, because I don’t know what its structure/ modeling looks like, so I’m going to assume that the problem is getting the Excel data only.
In the example each time a loop is made in the first for
is returned one Excel row and each time a loop is made in the second for
a column is returned:
for i := 0 to High(encontrados) do
begin
for j := 0 to High(encontrados[i]) do
begin
if encontrados[i][j] <> '' then //Evita dados em branco
ShowMessage('Linha: ' + IntToStr(i) + ' - Coluna: ' + IntToStr(j) + ' = ' + encontrados[i][j]);
end;
end;
For an xls file like this:
+----+----+----+----+
| a1 | a2 | a3 | a4 |
+----+----+----+----+
| b1 | b2 | b3 | b4 |
+----+----+----+----+
The example will return this (note that in arrays the numbers always start from 0
, if you need to resolve this in your database just add +1
):
Row: 0 - Column: 0 = a1
Row: 0 - Column: 1 = a2
Row: 0 - Column: 2 = A3
Row: 0 - Column: 3 = A4
Row: 1 - Column: 0 = B1
Row: 1 - Column: 1 = B2
Row: 1 - Column: 2 = B3
Row: 1 - Column: 3 = B4
Full example:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComObj, StrUtils;
type
TStringArrayArray = Array of Array of String;
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
tmpFile: String;
selectedFile: String;
password: String;
xlsExcel: Variant;
xlsDocument: Variant;
consultarEm: Integer;
encontrados: TStringArrayArray;
implementation
{$R *.dfm}
procedure EndExcel();
begin
if VarIsEmpty(xlsExcel) = False then
begin
xlsExcel.Quit;
xlsExcel := Unassigned;
end;
if tmpFile <> '' then
begin
DeleteFile(tmpFile);
end;
end;
function GetTempFile(const Extension: String): String;
var
Buffer: array[0..MAX_PATH] of Char;
begin
repeat
GetTempPath(SizeOf(Buffer) - 1, Buffer);
GetTempFileName(Buffer, '~', 0, Buffer);
Result := ChangeFileExt(Buffer, Extension);
until not FileExists(Result);
end;
function openExcel(var handler: Variant; selectedFile: String; pass: String): Boolean;
begin
EndExcel();
tmpFile := GetTempFile('.~tp');
CopyFile(pchar(selectedFile), pchar(tmpFile), true);
xlsExcel := CreateOLEObject('Excel.Application');
xlsExcel.Visible := False;
try
handler := xlsExcel.WorkBooks.Open(selectedFile, Password := password, ReadOnly := True);
Result := True;
except
showmessage('excepet');
EndExcel();
handler := Unassigned;
Result := False;
end;
end;
function authExcel(fileStr: String) : Variant;
var
password: String;
buttonSelected: Integer;
xlsTmp: Variant;
begin
password := InputBox('Autenticação', 'Senha do arquivo', '');
if password = '' then
begin
ShowMessage('Nenhuma senha digitada');
Result := Unassigned;
end
else
begin
if openExcel(xlsTmp, fileStr, password) then
begin
Result := xlsTmp
end
else
begin
buttonSelected := MessageDlg('Senha inválida, tentar novamente?', mtCustom, [mbYes, mbCancel], 0);
if buttonSelected = mrYes then
begin
Result := authExcel(fileStr);
end
else
begin
ShowMessage('Cancelado');
Result := Unassigned;
end;
end;
end;
end;
function findInExcel(xls: Variant; col: Integer; query: String; limitCols: Integer) : TStringArrayArray;
var
pages: Integer;
currentPage: Integer;
currentLine: Integer;
currentCol: Integer;
found: Integer;
lines: Integer;
cols: Integer;
cells: Integer;
dataRow: String;
resultados: TStringArrayArray;
coringa: Boolean;
coringaReverse: Boolean;
findAll: Boolean;
begin
if col < 1 then
begin
ShowMessage('Coluna para consulta não especificada');
Result := null;
end
else
begin
pages := xls.Worksheets.Count;
if pages > 0 then
begin
cells := 0;
found := 0;
cols := 0;
currentPage := 0;
findAll := '*' = query;
coringa := AnsiPos('*', query) > 0;
coringaReverse := AnsiPos('*', query) = 1;
query := StringReplace(query, '*', '', [rfReplaceAll, rfIgnoreCase]);
while currentPage < pages do
begin
currentLine := 0;
lines := xls.Worksheets[currentPage + 1].UsedRange.Rows.Count;
while currentLine < lines do
begin
dataRow := xls.Worksheets[currentPage + 1].Cells[currentLine + 1, col];
if coringaReverse then
dataRow := ReverseString(dataRow);
if (findAll and (dataRow <> '')) or (coringa and (AnsiPos(query, dataRow) = 1)) or (dataRow = query) then
begin
cells := xls.Worksheets[currentPage + 1].UsedRange.Cells.Count;
found := found + 1;
if cols < cells then
cols := cells;
if (limitCols > 0) and (cells > limitCols) then
cols := limitCols;
if cells > 0 then
SetLength(resultados, found, cols);
currentCol := 0;
while (currentCol < cols) and (cells > 0) do
begin
dataRow := xls.Worksheets[currentPage + 1].Cells[currentLine + 1, currentCol + 1];
resultados[found - 1][currentCol] := xls.Worksheets[currentPage + 1].Cells[currentLine + 1, currentCol + 1];
currentCol := currentCol + 1;
end;
end;
currentLine := currentLine + 1;
end;
currentPage := currentPage + 1;
end;
Result := resultados;
end
else
begin
ShowMessage('Não há páginas neste documento');
end;
end;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
EndExcel();
//Remova esta parte se quiser
if MessageDlg('Deseja realmente sair?', mtCustom, [mbYes, mbCancel], 0) = mrCancel then
Action := caNone;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
i: Integer;
j: Integer;
begin
if PromptForFileName(selectedFile, 'Documento do Excel (*.xls, *.xlsx)|*.xls; *.xlsx', '', 'Selecione...', 'C:\', False) then
begin
if openExcel(xlsDocument, selectedFile, '') = False then
xlsDocument := authExcel(selectedFile);
if VarIsEmpty(xlsDocument) = False then
begin
consultarEm := StrToInt(InputBox('Infomarção', 'Qual coluna gostaria de consultar', '1'));
if consultarEm > 0 then
begin
encontrados := findInExcel(xlsDocument, consultarEm, '*', 4);
EndExcel();
if High(encontrados) = -1 then
begin
ShowMessage('Não foram encontrados resultados');
end
else
begin
for i := 0 to High(encontrados) do
begin
for j := 0 to High(encontrados[i]) do
begin
if encontrados[i][j] <> '' then //Evita dados em branco
ShowMessage('Linha: ' + IntToStr(i) + ' - Coluna: ' + IntToStr(j) + ': ' + encontrados[i][j]);
end;
end;
end;
end
else
begin
ShowMessage('Coluna inválida');
end;
end;
end
else
begin
ShowMessage('Nenhum arquivo selecionado');
end;
end;
end.
@Guilhermenascimento Must appear a request for the password. Really CSV has no password, I will change the question because I need a file with password.
– Giovani
@Giovaniracipaganini Is it necessary to show the window to enter the password? or can it be programmatically? Another thing, you want to take the value of just one line?
– stderr
@Qmechanic73 I want just one row, searching for the content that column 1 presents in all lines. When the password is indifferent, because anything I can adjust later. .
– Giovani
@Giovaniracipaganini But the password can be entered via code? You want to take the value of the line X spine Y right? When you say: i want only one row, searching for the content that column 1 presents in all lines. Do you already know the column and row that have to catch right? if possible explain this part better. = D
– stderr
@Qmechanic73 The password can be entered via code yes. Example: let’s say the table is for business data. In case I’d like to know the name of the company and I have her CNPJ. Therefore, I will use a FOR (I believe) and run on all lines until I find the CNPJ I reported, and after that I will get the contents of the 'Social Reason' field'
– Giovani
@Okay Giovaniracipaganini, you already know the column of CNPJ of the company and the column of social reason?
– stderr
@Giovaniracipaganini I recently made an implementation using protected spreadsheets and data export. But I couldn’t understand your doubt, since that wasn’t exactly a question. Is your doubt the whole process? Is it like reading the protected file? How to search the content in the spreadsheet? Could you clarify?
– adamasan
@Qmechanic73 already yes
– Giovani
@Arthurdeandrade My doubt is as much as reading the protected files as fetching the data.
– Giovani
@Guilhermenascimento just entered here: http://chat.stackexchange.com/rooms/21137/discussion-between-user3465632-and-guilherme-nascimento
– Giovani
http://chat.stackexchange.com/rooms/21232/abrir-arquivo-xls-protegido-por-senha-em-delphi-e-salvar-dados-em-tabela-firebi
– Guilherme Nascimento