В PHP5 также введены абстрактные (abstract) классы и методы. Абстрактный метод представляет собой только объявление метода и не предусматривает его реализации. Класс, содержащий абстрактные методы, должен быть объявлен абстрактным.
Пример:
abstract class AbstractClass {
abstract public function test();
}
class ImplementedClass extends AbstractClass {
public function test() {
echo "Вызван метод ImplementedClass::test().\n";
}
}
$o = new ImplementedClass;
$o->test();
?>
Создание экземпляра абстрактного класса невозможно.
Существующие программы, не испольующие собственные классы или функции, названные “abstract”, будут работать без каких-либо изменений.
Февраль
29,
2008
— Рубрика: PHP
Метки: PHP5, Абстрактные классы
В PHP5 также введены частные (private) и защищенные (protected) методы.
Пример:
class Foo {
private function aPrivateMethod() {
echo "Вызван метод Foo::aPrivateMethod().\n";
}
protected function aProtectedMethod() {
echo "Вызван метод Foo::aProtectedMethod().\n";
$this->aPrivateMethod();
}
}
class Bar extends Foo {
public function aPublicMethod() {
echo “Вызван метод Bar::aPublicMethod().\n”;
$this->aProtectedMethod();
}
}
$o = new Bar;
$o->aPublicMethod();
?>
Существующие программы, не испольующие собственные классы или функции, названные “public”, “protected” или “private”, будут работать без каких-либо изменений.
Февраль
28,
2008
— Рубрика: PHP
Метки: PHP5, защищенные методы
В PHP5 введены частные (private) и защищенные (protected) переменные-элементы класса (member variables), что позволяет определять область их видимости.
Пример:
Защищенные (protected) переменные-элементы класса доступны методам (methods) класса, в котором они объявлены, и методам производного от него класса. Частные (private) элементы класса доступны только методам класса, в котором они объявлены.
class MyClass {
private $Hello = "Hello, World!\n";
protected $Bar = "Hello, Foo!\n";
protected $Foo = "Hello, Bar!\n";
function printHello() {
print "MyClass::printHello() " . $this->Hello;
print “MyClass::printHello() ” . $this->Bar;
print “MyClass::printHello() ” . $this->Foo;
}
}
class MyClass2 extends MyClass {
protected $Foo;
function printHello() {
MyClass::printHello(); /* Выведет значение */
print “MyClass2::printHello() ” . $this->Hello; /* Ничего не выведет */
print “MyClass2::printHello() ” . $this->Bar; /* Не выведет значение (переменная не определена)*/
print “MyClass2::printHello() ” . $this->Foo; /* Выведет значение */
}
}
$obj = new MyClass(); print $obj->Hello; /* Ничего не выведет */
print $obj->Bar; /* Ничего не выведет */
print $obj->Foo; /* Ничего не выведет */
$obj->printHello(); /* Выведет значение */
$obj = new MyClass2();
print $obj->Hello; /* Ничего не выведет */
print $obj->Bar; /* Ничего не выведет */
print $obj->Foo; /* Ничего не выведет */
$obj->printHello();
?>
Февраль
24,
2008
— Рубрика: PHP
Метки: класс, элементы
Итак, вы много раз видели, как дынные выводятся постранично и вы всегда хотели сделать так на своем сайте. Как это сделать с файлом я уже описывал. Сегодня я расскажу как сделать постраничный вывод из MySQL.
Для начала немного теории. Что надо сделать? Сначала, надо проверить выбрана ли страница просмотра. Потом надо подсчитать откуда выводить записи. Делается это просто: текущую страницу умножаем на количество записей на страницу. Наконец, надо сделать SQL-запрос к базе данных и вывести результат. И в конце концов написать нивигацию.
Приступим. Выполните SQL-запрос:
CREATE TABLE tbl_books (
book_id int(11) NOT NULL auto_increment,
book_name varchar(50) NOT NULL default ”,
book_cash float NOT NULL default ‘0′,
PRIMARY KEY (book_id)
) TYPE=MyISAM;
INSERT INTO tbl_books VALUES (1, ‘Книга номер 1′, ‘2′);
INSERT INTO tbl_books VALUES (2, ‘Книга номер 2′, ‘3′);
INSERT INTO tbl_books VALUES (3, ‘Книга номер 3′, ‘4′);
INSERT INTO tbl_books VALUES (4, ‘Книга номер 4′, ‘5′);
INSERT INTO tbl_books VALUES (5, ‘Книга номер 5′, ‘6′);
INSERT INTO tbl_books VALUES (6, ‘Книга номер 6′, ‘7′);
INSERT INTO tbl_books VALUES (7, ‘Книга номер 7′, ‘8′);
INSERT INTO tbl_books VALUES (8, ‘Книга номер 8′, ‘9′);
INSERT INTO tbl_books VALUES (9, ‘Книга номер 9′, ‘10′);
INSERT INTO tbl_books VALUES (10, ‘Книга номер 10′, ‘11′);
INSERT INTO tbl_books VALUES (11, ‘Книга номер 11′, ‘12′);
INSERT INTO tbl_books VALUES (12, ‘Книга номер 12′, ‘13′);
INSERT INTO tbl_books VALUES (13, ‘Книга номер 13′, ‘14′);
INSERT INTO tbl_books VALUES (14, ‘Книга номер 14′, ‘15′);
INSERT INTO tbl_books VALUES (15, ‘Книга номер 15′, ‘16′);
Я предлагаю все оформить в виде функций обработчиков. Создадим функцию page(), которая обрабатывает данные о запрошенном номере страницы:
function page()
{
if(empty($_GET["page"])){
$page = 0;
} else {
if(!is_numeric($_GET["page"])) die("Неправильный формат номера страницы!");
$page = $_GET["page"];
}
return $page;
}
Что она делает? Проверяет выбрана ли страница и если в ней присутствуют буквы, то выводит сообщение об ошибке. Далее сделаем SQL-запрос:
function sql_query($onpage, $page, $table)
{
$begin = $page*$onpage; // откуда начинать
$sql = "SELECT * FROM ".$table." LIMIT ".$begin.", ".$onpage;
$result = mysql_query($sql) or die(mysql_error());
return $result;
}
Функция навигации:
function navigation($onpage, $page, $table)
{
$return = null;
$count = mysql_query("SELECT COUNT(*) FROM tbl_books") or die(mysql_error());
$count = mysql_fetch_array($count);
$count = $count[0];
$pages = $count/$onpage;
if($page!==0){
$prev = "<“;
} else {
$prev = “<";
}
if($page
$next = ">“;
} else {
$next = “>”;
}
for($i=0;$i<$pages;$i++)
{
if($i==$page){
$return.="[".($i+1)."]";
} else {
$return.="[".($i+1)."]“;
}
}
return $prev.$return.$next;
}
С функциями покончили! Как ими пользоваться?
$onpage = 4; // записей на страницу
$table = “tbl_books”; // из какой таблицы
mysql_connect(”localhost”, “root”, “pass”) or die(mysql_error()); // коннект к БД
mysql_select_db(”test”); // выбор БД
$page = page(); // определяем страницу
$result = sql_query($onpage, $page, $table); // sql - запрос
while($data = mysql_fetch_array($result)) // цикл вывода
{
// шаблон вывода нужно изменить
// $data["название_поля"] - содержание поля
echo $data["book_id"].” “.$data["book_name"].”
“;
echo $data["book_cash"].”$
“;
}
$navigation = navigation($onpage, $page, $table); // определим навигацию
echo $navigation; // выведем ее
mysql_close(); // отключение от БД
?>
Февраль
23,
2008
— Рубрика: MySQL
Метки: MySQL, БД, Постраничный вывод
В этой статье я приведу примеры простейших скриптов на PHP. Такие скрипты можно увидеть на многих сайтах интернета. Всего я подготовил 4 примера.
1. Время, затраченное на генерацию php-скрипта
// Определяем текущее
время.
list($msec,$sec)=explode(chr(32),microtime());
$HeadTime=$sec+$msec;
//
На этом месте должен быть ваш PHP код.
// Можно сделать простой
инклудинг:
// include("script.inc.php");
// Определяем ещё раз
текущее время.
list($msec,$sec)=explode(chr(32),microtime());
//
Выводим время затраченное на выполнение скрипта
// с 4 знаками после запятой
(точки).
echo "Страница сгенерировалась за
".round(($sec+$msec)-$HeadTime,4)." сек.";
?>
2. Подсветка php-кода
highlight_file("script.php");
?>
3. Авторизация
Вход :
enter.php :
foreach
(file(passw/passw) as $k)
(if (substr($k, 0, -2)=="$login
$pass")($result=1
))
if ($result!=1){Несанкционированный
доступ
exit;}
session_start();
session_register("authorize");
$authorize=1;
?>
На
каждой странице защищёной зоны ЕЩЁ ДО ТЕГА т.е. в самом
верху(!)
надо поставить код:
session_start();
if
($authorize!=1){Несанкционированный доступ
exit;}
Всё что после -
будет выполнено только если пароль был введён.
Страница
выхода:
session_start();
session_destroy();
>
4. Генератор пароля
// пример генератора паролей
// created by ne_tot
print
"
“;
$symb =
“qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890!@#$%^&*()_+#№-=<>`”;
$count
= strlen($symb)-1;
if(empty($lenght)) {exit();}
else
{
foreach
($lenght as $digit)
print “Сгенерирован новый пароль из $digit
символов - “;
for($i=0; $i <= ($digit-1); $i++)
{
$rand
= rand(0, $count);
print "
color=red>$symb[$rand]“;
}
print “
width=100%>“;
}
?>
Конечно скрипты не оригинальные и подобных им существует великое множество, зато они помогут начинающим изучать великий и могучий язык PHP.
Февраль
21,
2008
— Рубрика: PHP
Метки: PHP, изучать, могучий язык
Ниже представлен пример создания новой таблицы в Excel 2000:
uses
ComObj, ActiveX;
var
Row, Col: integer;
DestRange: OleVariant;
Excel: Variant;
begin
Excel := CreateOleObject(’Excel.Application.9′);
Excel.Visible := True;
Excel.WorkBooks.Add; //Создать новую таблицу
//Можно помещать текст и значения в диапазон ячеек
//Поместить слово тест в диапазон ячеек
Excel.ActiveSheet.Range['A2', 'B3'].Value := ‘Тест’;
//Или число
Excel.ActiveSheet.Range['A4', 'B5'].Value := 42;
//А вот так задаётся формула
Excel.ActiveSheet.Range['A10', 'A11'].Formula := ‘=RAND()’;
//Можно задавать номера ячеек и столбцов
Excel.ActiveSheet.Cells.Item[1, 1].Value := ‘Первая ячейка’;
Row:=1;
Col:=3;
Excel.ActiveSheet.Cells.Item[Row, Col].Value := ‘Другая ячейка’;
//Можно скопировать данный из одного диапазона ячеек в другой
DestRange := Excel.Range['D6', 'F10'];
Excel.Range['A1', 'C5'].Copy(DestRange);
//Можно задавать параметры шрифта в определённой ячейке
Excel.Range['A2', 'A2'].Font.Size := 20;
Excel.Range['A2', 'A2'].Font.FontStyle := ‘Bold’;
Excel.Range['A2', 'A2'].Font.Color := clFuchsia;
Excel.Range['A2', 'A2'].Font.Name := ‘Arial’;
//Можно ещё и так изменить цвет диапазона ячеек
Excel.Range['B2', 'C6'].Interior.Color := RGB(223, 123, 123);
end;
Далее представлен пример открытия и закрытия таблицы:
uses
ComObj, ActiveX;
var
Excel: Variant;
WBk : OleVariant;
SaveChanges: OleVariant;
begin
Excel := CreateOleObject(’Excel.Application.9′);
Excel.Visible := True;
//Открыть существующую таблицу
WBk := Excel.WorkBooks.Open(’C:\Test.xls’);
…
WBk.Close(SaveChanges := True);
Excel.Quit;
end;
Февраль
19,
2008
— Рубрика: Delphi
Метки: OLE, диапазон ячеек
Это можно сделать с помощью следующей процедуры :
procedure TForm1.BitBtnFromExcelOnClick(Sender: TObject);
var
WorkBk : _WorkBook;
WorkSheet : _WorkSheet;
K, R, X, Y : Integer;
IIndex : OleVariant;
RangeMatrix : Variant;
NomFich : WideString;
begin
NomFich := ‘C:\MyDirectory\NameOfFile.xls’;
IIndex := 1;
XLApp.Connect;
// Открываем файл Excel
XLApp.WorkBooks.Open(NomFich,EmptyParam,EmptyParam,EmptyParam,EmptyParam, EmptyParam,EmptyParam,EmptyParam,EmptyParam,
EmptyParam,EmptyParam,
EmptyParam,EmptyParam,0);
WorkBk := XLApp.WorkBooks.Item[IIndex];
WorkSheet := WorkBk.WorkSheets.Get_Item(1) as _WorkSheet;
// Чтобы знать размер листа (WorkSheet), т.е. количество строк и количество
// столбцов, мы активируем его последнюю непустую ячейку
WorkSheet.Cells.SpecialCells(xlCellTypeLastCell,EmptyParam).Activate;
// Получаем значение последней строки
X := XLApp.ActiveCell.Row;
// Получаем значение последней колонки
Y := XLApp.ActiveCell.Column;
// Определяем количество колонок в TStringGrid
GenericStringGrid.ColCount := Y;
// Сопоставляем матрицу WorkSheet с нашей Delphi матрицей
RangeMatrix := XLApp.Range['A1',XLApp.Cells.Item[X,Y]].Value;
// Выходим из Excel и отсоединяемся от сервера
XLApp.Quit;
XLApp.Disconnect;
// Определяем цикл для заполнения TStringGrid
K := 1;
repeat
for R := 1 to Y do
GenericStringGrid.Cells[(R - 1),(K - 1)] := RangeMatrix[K,R];
Inc(K,1);
GenericStringGrid.RowCount := K + 1;
until
K > X;
// Unassign the Delphi Variant Matrix
RangeMatrix := Unassigned;
end;
Февраль
16,
2008
— Рубрика: Delphi
Метки: Excel, Получение данных
В Delphi 5 и выше, для обмена данными между Вашим приложением и Excel можно использовать компонент TExcelApplication, доступный на Servers Page в Component Palette.
Создаем новый проект. На форму кладем TStringGrid, заполняем его некоторыми данными и добавляем две кнопки, с названиями “To Excel” и “From Excel”. Так же на форму кладем компонент TExcelApplication и в свойстве “Name” указываем “XLApp”, а свойство “ConnectKind” меняем на “ckNewInstance”.
Когда нам необходимо работать с Excel, то обычно мы открываем ExcelApplication, затем открываем WorkBook и в конце используем WorkSheet.
Итак, несомненный интерес представляет для нас листы (WorkSheets) в книге (WorkBook). Давайте посмотрим как всё это работает.
Посылка данных в Excel.
Это можно сделать с помощью следующей процедуры :
procedure TForm1.BitBtnToExcelOnClick(Sender: TObject);
var
WorkBk : _WorkBook; // определяем WorkBook
WorkSheet : _WorkSheet; // определяем WorkSheet
I, J, K, R, C : Integer;
IIndex : OleVariant;
TabGrid : Variant;
begin
if GenericStringGrid.Cells[0,1] <> ” then
begin
IIndex := 1;
R := GenericStringGrid.RowCount;
C := GenericStringGrid.ColCount;
// Создаём массив-матрицу
TabGrid := VarArrayCreate([0,(R - 1),0,(C - 1)],VarOleStr);
I := 0;
// Определяем цикл для заполнения массива-матрицы
repeat
for J := 0 to (C - 1) do
TabGrid[I,J] := GenericStringGrid.Cells[J,I];
Inc(I,1);
until
I > (R - 1);
// Соединяемся с сервером TExcelApplication
XLApp.Connect;
// Добавляем WorkBooks в ExcelApplication
XLApp.WorkBooks.Add(xlWBatWorkSheet,0);
// Выбираем первую WorkBook
WorkBk := XLApp.WorkBooks.Item[IIndex];
// Определяем первый WorkSheet
WorkSheet := WorkBk.WorkSheets.Get_Item(1) as _WorkSheet;
// Сопоставляем Delphi массив-матрицу с матрицей в WorkSheet
Worksheet.Range['A1',Worksheet.Cells.Item[R,C]].Value := TabGrid;
// Заполняем свойства WorkSheet
WorkSheet.Name := ‘Customers’;
Worksheet.Columns.Font.Bold := True;
Worksheet.Columns.HorizontalAlignment := xlRight;
WorkSheet.Columns.ColumnWidth := 14;
// Заполняем всю первую колонку
WorkSheet.Range['A' + IntToStr(1),'A' + IntToStr(R)].Font.Color := clBlue;
WorkSheet.Range['A' + IntToStr(1),'A' + IntToStr(R)].HorizontalAlignment := xlHAlignLeft;
WorkSheet.Range['A' + IntToStr(1),'A' + IntToStr(R)].ColumnWidth := 31;
// Показываем Excel
XLApp.Visible[0] := True;
// Разрываем связь с сервером
XLApp.Disconnect;
// Unassign the Delphi Variant Matrix
TabGrid := Unassigned;
end;
end;
Февраль
14,
2008
— Рубрика: Delphi
Метки: VCL
Думаю, вы уже заметили, что один из переключателей вариантов имеет название, состоящее из зловещего сочетания букв - DDE. Первое, что вы можете почувствовать при виде этих букв, - это застоялый запах плесени и старения. Да, DDE - пережиток старого доброго времени (а, кажется, это было совсем недавно - “как молоды мы были”), времени DOS-резидентов, командной строки, капитана Нортона и первых (не смотря на их номер - 3.0) “форточек”. Черт, тут жизнь уже проходит, а DDE все еще жив. И слава Богу! Ведь, мы решим “главную” проблему - сохранение содержимого буфера обмена. Вот так:
procedure TForm1.ToDDE(ISheet: IxlWorksheet);
var xlDDE: TDDEClientConv;
i: integer;
IR1, IR2, IRange: IxlRange;
Buff: string;
begin
Buff := ”;
tblCust.First;
while not tblCust.EOF do begin
for i := 0 to tblCust.Fields.Count - 1 do begin
Buff := Buff + FieldToStr(tblCust.Fields[i]);
if i < (tblCust.Fields.Count - 1) then
Buff := Buff + #9;
end;
tblCust.Next;
if not tblCust.EOF then
Buff := Buff + #10;
end;
IDispatch(IR1) := ISheet.Cells.Item[StartRow, StartColumn];
IDispatch(IR2) := ISheet.Cells.Item[StartRow + tblCust.RecordCount - 1,
StartColumn + tblCust.Fields.Count - 1];
IRange := ISheet.Range[IR1, IR2];
xlDDE := TDDEClientConv.Create(Self);
try
if xlDDE.SetLink(’EXCEL’, ISheet.Name) then
xlDDE.PokeData(OLEVariant(IRange).Address[ReferenceStyle:=xlR1C1], PChar(Buff));
finally
xlDDE.Free;
end;
end;
Все реже я встречаю книги, где было бы описано взаимодействие приложений посредством DDE. Многие и вовсе думают, что DDE уже давно умер. Но, нет, “жив курилка”. И, более того, это одно из самых быстрых решений по передаче данных в Excel. И, что самое интересное, Excel по-прежнему поддерживает DDE и все DDE-команды, которые стали доступны со времен версии 4.0. И по-прежнему будет несказанно рад тот счастливчик, который обнаружит в непроходимых джунглях Сети файл под названием “excel40macro.hlp”, ибо там он найдет все, что нужно для быстрой и качественной работы с Excel. Это вам не интерфейсы “пользовать”. Но, вернемся к исходному тексту.
В этой процедуре я, как было описано выше, прохожу по всей таблице, собирая в строковый буфер значения полей, разделенные символом табуляции. Записи же разделяются символом перевода строки. Затем я получаю интерфейс на область, куда необходимо поместить данные из таблицы. Это первая часть кода. Далее - самое интересное.
Переменная xlDDE используется для доступа к Excel посредством DDE. Если опустить теорию, напрямую обратившись к практике, то можно увидеть следующий алгоритм ее использования. Во-первых, создается экземпляр класса TDDEClientConv. Во-вторых, вызовом SetLink происходит соединение через DDE с запущенным Excel. SetLink возвращает true, если это соединение успешно. А далее происходит вызов метода PokeData, одним из параметров которого является строковый буфер Buff. Второй параметр - это адрес области в формате R1C1. Вот и все. Думаю, это работает и у вас. Скорость сравнима с CSV через буфер обмена. Плюс, здесь буфер обмена мы совсем не используем. Но!
Попробуйте несколько раз подряд быстро нажать кнопку “Send data” с этим вариантом передачи данных. У меня Excel просто виснет. Точнее, он что-то делает, загружая на все сто процессор. Благо, Windows NT безболезненно позволяет снять задачу. Тем, у кого Win9x, повезло меньше и, видимо, им придется перезагрузиться. Почему это происходит? Меня смутила вот эта строка в реализации TDDEClientConv:
hdata := DdeClientTransaction(Pointer(hszDat), DWORD(-1), FConv, hszItem,
FDdeFmt, XTYP_POKE, TIMEOUT_ASYNC, nil);
Точнее параметр TIMEOUT_ASYNC, позволяющий передавать данные асинхронно. Вот и сыплется Excel, не выдерживая реализации DDE-клиента от Borland/Inprise. Впрочем, Borland тоже не причем. Для себя я создал потомка класса TDDEClientConv, добавив ему новый метод xlPokeData, в котором просто заменил эту строку на:
Const xddeTransactionTimeOut = 100000;
…
hdata := DdeClientTransaction(Pointer(hszDat), DWORD(-1), Conv, hszItem,
CF_XLTABLE, XTYP_POKE, xddeTransactionTimeOut, nil);
…
И все в порядке - работает.
Я не стану описывать подробности взаимодействия процессов через DDE по, думаю, понятным вам причинам. Все это давно описано в классике жанра - документации по Delphi. Тем не менее, по-прежнему остается проблема со строкой “0001″. И останется она при этом варианте нерешенной, так как здесь используются строковые представления всех значений полей. И где же выход, спросите вы? Выход прост.
Из всего вышесказанного я бы хотел сделать один вывод. Если есть желание получить максимальное быстродействие, то необходимо использовать DDE. Впрочем, для меня это уже аксиома. Единственное, чего здесь не достает, так это такого формата, чтобы…
Чтобы в нем использовались native представления данных, где число было бы не строкой, а привычным набором из четырех (восьми) байт. Ведь, напомню, целые и вещественные числа и даты - это, в конечном счете, вещественное число в Excel. Для себя я решение нашел - Fast Table Format. Но это уже не из этой статьи…
Демо-Проект - SampleExcel3 (в архиве с данной статьёй).
Февраль
11,
2008
— Рубрика: Delphi
Метки: DDE, SampleExcel, Table Format
Совсем недавно мне пришло очередное гневное послание на тему буфера обмена - Clipboard. Объясню. XL Report очень долго использовал только буфер обмена для передачи данных из приложения в Excel. Дело в том, что при таком варианте (а я его здесь опишу) достигается практически максимальная скорость переноса данных. Дело в том, что в буфер обмена “кладется” длинная строка, содержащая строковые значения полей набора данных (AsString), разделенные символом табуляции. Записи отделяются друг от друга переводом строки (#10). Собственно, этот формат известен в научных кругах как CSV (разделитель между значениями). Долгое время это меня устраивало, пока XL Report использовался только нашими разработчиками и ограниченным кругом клиентов фирмы. Но тут мы решили выложить это решение в Сеть. И…
Каюсь. Я никак не беспокоился за сохранность содержимого буфера обмена. Понятное дело, это не очень правильно. Но, так было. Так есть и сейчас. Правда, по другим причинам. Для того, чтобы “выжать” из Excel максимальное быстродействие, приходится использовать определенные методы и свойства его интерфейсов. А их использование не оставляет ничего, кроме как уничтожение содержимого буфера обмена. В общем, сейчас я покажу максимально возможное (в этой статье!!!) решение по переносу данных - CSV. Итак:
procedure TForm1.ToCSV(ISheet: IxlWorksheet);
var i: integer;
IR1, IR2: IxlRange;
Buff: String;
begin
Buff := ”;
tblCust.First;
while not tblCust.EOF do begin
for i := 0 to tblCust.Fields.Count - 1 do begin
Buff := Buff + FieldToStr(tblCust.Fields[i]);
if i < (tblCust.Fields.Count - 1) then
Buff := Buff + #9;
end;
tblCust.Next;
if not tblCust.EOF then
Buff := Buff + #10;
end;
BufferToClipboard(Buff);
try
IDispatch(IR1) := ISheet.Cells.Item[StartRow, StartColumn];
IDispatch(IR2) := ISheet.Cells.Item[StartRow + tblCust.RecordCount - 1,
StartColumn + tblCust.Fields.Count - 1];
OLEVariant(ISheet.Range[IR1, IR2]).PasteSpecial;
finally
Clipboard.Clear;
end;
end;
Как я и писал выше, в строковый буфер Buff собирается вся таблица. Строковые значения полей я разделяю символом табуляции, а в “конце” записи добавляю перевод строки. Все значения я заключаю дополнительно в двойные кавычки. Затем вызовом процедуры BufferToClipboard я помещаю содержимое этой переменной в буфер обмена и делаю “хитрый” вызов PasteSpecial для области, в которую будут помещены данные. Вот и все?! Нет, есть еще проблемы.
Во-первых, процедура BufferToClipboard - вещь не стандартная. Она создана как альтернатива методу SetTextBuf класса TClipboard. Наверняка, все знают что в VCL доступна глобальная переменная Clipboard, экземпляр класса TClipboard, инкапсулирующего свойства и методы доступа к этому самому буферу обмена. И, собственно, вызов SetTextBuf позволяет поместить строку в буфер. Но!
SetTextBuf помещает в буфер обмена текст в формате CF_TEXT - обычный текст с однобайтовым представлением символов, что не есть хорошо. Точнее, это совсем не хорошо, если вы работаете с “русскими буквами” на разных операционных системах от MS, причем, разных с точки зрения локализации. Именно тогда и возникают у пользователей вопросы при попытке прочитать некий набор “закорючек”, отдаленно напоминающих письмена племени зибару. Поэтому я предпочитаю UNICODE, вставка которого в буфер обмена и реализована в этой процедуре. Ее текст я не буду приводить в этой статье, так как это потребует лишних для этого объяснений. Сама процедура присутствует в исходных текстах проекта-примера. Поэтому знатоков этого дела отсылаю к ним, а таким начинающим, как и я, просто рекомендую ее использовать в последующих своих разработках (при необходимости).
UNICODE - это первая проблема, которая была решена. Но при использовании CSV есть и другие. И, главная, из них - “3/7″. Без вмешательства в содержимое поля (аналогично добавлению одиночной кавычки при вариантах) ее нельзя обойти никак. Я добавляю пробел. Да, тривиального пробела вполне хватает для решения этой проблемы. И замеченная вами функция FieldToStr как раз это и делает.
По поводу “хитрого” PasteSpecial. При “правильном” вызове метода PasteSpecial интерфейса Range я столкнулся с неразрешимыми проблемами. Поэтому я привел к варианту область и заставил ОС и сам Excel самостоятельно разбираться с тем, что и с какими параметрами я вызываю. Часто, при разработке с библиотеками типов от MS такой ход экономит время и, главное, сохраняет здоровье и нормальное состояние нервной системы. Не используйте его все время, но и не пренебрегайте им. Итак, при использовании CSV и буфера обмена мы достигли “громаднейшей” скорости передачи данных, обойдя при этом несколько проблем.
Недостатки, которые заметят критики и просто опытные разработчики. Переданное строковое значение “0001″ предстает перед изумленным пользователем числом 1. Это можно обойти только через предварительное форматирование конкретной ячейки в текстовый формат. Подчеркиваю, предварительно, то есть перед переносом данных. И, по-прежнему, содержимое буфера обмена не сохраняется. Более того, оно затем нагло очищается вызовом Clipboard.Clear. “Мы его теряем!”
Февраль
9,
2008
— Рубрика: Delphi
Метки: Clipboard, PasteSpecial, UNICODE