有没有办法在 Delphi 中分配多行字符串值而不必引用每一行?
编辑(具体问题):我有一些 SQL 查询,我想在 Delphi 之外进行测试。复制查询时,每次添加和替换引号都会产生一些开销。
这是您可以添加到 IDE 的工具菜单中的应用程序的代码,它可能会有所帮助。它是由 TeamB 成员彼得在一段时间后发布到 CodeGear 新闻组之一的:
程序 ClipToStringConst; // 从控制台应用程序的下面一行中删除点, // 根据 Rob Kennedy 的评论。它可以正常工作而无需 // 一个控制台应用程序。 {.$APPTYPE 控制台} 用途 视窗, 类, 系统工具, API剪贴板; 常量 c缩进 = ' '; // 2 个空格 cSingleQuote = ''''; EndChar : 数组 [Boolean] 的 Char = ('+',';'); 程序 过程; 变量 SL:TStringlist; i, max:整数; 开始 如果 ClipboardHasFormat( CF_TEXT ) 那么 开始 SL := TStringlist.Create; 尝试 SL.Text := ClipboardAsString; 最大值:= SL.count-1; for i:= 0 to max do SL[i] := cIndent + AnsiQuotedStr( TrimRight(SL[i])+#32, cSingleQuote ) + EndChar[i = max]; StringToClipboard(SL.Text); 最后 SL.免费; 结尾; { 最后 } 结尾; 结尾; 开始 尝试 过程; 除了 E:例外 ShowException(E, exceptAddr); 结尾; 结尾。
测试完后在SQL管理工具中选中文本,复制到剪贴板即可。切换到 Delphi 代码编辑器,将插入点放置在要显示常量文本的位置,选择“Clipboard To Const”或从“工具”菜单中选择的任何名称,然后按 Ctrl+V 将其粘贴到编辑器中。
这是一个非常方便的小工具。您也可以修改它以相反的方式工作 (ConstantToClipboard) 以删除源格式并恢复为原始 SQL,尽管我还没有费心这样做。
编辑:错过了一个单元(APIClipboard)。显然,这需要是一个单独的单元。再次感谢下面的彼得:
{== Unit APIClipboard =================================================}
{: Clipboard access routines using only API functions
@author Dr. Peter Below
@desc Version 1.0 created 5 Juli 2000<BR>
Current revision 1.0<BR>
Last modified 5 Juli 2000<P>
This unit provides simply clipboard access routines that do not rely on
the VCL Clipbrd unit. That unit drags in Dialogs and Forms and a major
part of the VCL as a consequence, not appropriate for simple console
or non-form programs. This unit uses only API routines, the only VCL
units used are Classes (for exceptions and streams) and SysUtils.
}
{=====================================================================}
unit APIClipboard;
interface
uses
Windows, Classes;
procedure StringToClipboard( const S: String );
function ClipboardAsString: String;
procedure CopyDataToClipboard( fmt: DWORD; const data; datasize: Integer;
emptyClipboardFirst: Boolean = true );
procedure CopyDataFromClipboard( fmt: DWORD; S: TStream );
function ClipboardHasFormat( fmt: DWORD ): Boolean;
implementation
uses
Sysutils;
type
{: This is an internal exception class used by the <see unit=APIClipboard> }
EClipboardError = class( Exception )
public
constructor Create( const msg: String );
end;
resourcestring
eSystemOutOfMemory =
'could not allocate memory for clipboard data.';
eLockfailed =
'could not lock global memory handle.';
eSetDataFailed =
'could not copy data block to clipboard.';
eCannotOpenClipboard =
'could not open the clipboard.';
eErrorTemplate =
'APIClipboard: %s'#13#10+
'System error code: %d'#13#10+
'System error message: %s';
{-- EClipboardError.Create --------------------------------------------}
{: Creates a new EclipboardError object
@Param msg is the string to embed into the error message
@Precondition none
@Postcondition none
@desc Composes an error message that contains the passed message and the
API error code and matching error message. The CreateFmt constructor
inherited from the basic Exception class is used to do the work.
Created 5.7.2000 by P. Below
}{---------------------------------------------------------------------}
constructor EClipboardError.Create( const msg: String );
begin { Create }
CreateFmt( eErrorTemplate,
[msg, GetLastError, SysErrorMessage(GetLastError)] );
end; { EClipboardError.Create }
{-- DataToClipboard ---------------------------------------------------}
{: Copies a block of memory to the clipboard in a given format
@Param fmt is the clipboard format to use
@Param data is an untyped const parameter that addresses the data to copy
@Param datasize is the size of the data, in bytes
@Precondition The clipboard is already open. If not an EClipboardError
will result. This precondition cannot be asserted, unfortunately.
@Postcondition Any previously exisiting data of this format will have
been replaced by the new data, unless datasize was 0 or we run into an
exception. In this case the clipboard will be unchanged.
@desc Uses API methods to allocate and lock a global memory block of
approproate size, copies the data to it and submits the block to the
clipboard. Any error on the way will raise an EClipboardError
exception.<BR>
Created 5.7.2000 by P. Below
@Raises EClipboardError
}{---------------------------------------------------------------------}
procedure DataToClipboard( fmt: DWORD; Const data; datasize: Integer );
var
hMem: THandle;
pMem: Pointer;
begin { DataToClipboard }
if datasize <= 0 then
Exit;
hMem := GlobalAlloc( GMEM_MOVEABLE or GMEM_SHARE or GMEM_ZEROINIT, datasize );
if hmem = 0 then
raise EClipboardError.Create( eSystemOutOfMemory );
pMem := GlobalLock( hMem );
if pMem = nil then
begin
GlobalFree( hMem );
raise EClipboardError.Create( eLockFailed );
end;
Move( data, pMem^, datasize );
GlobalUnlock( hMem );
if SetClipboardData( fmt, hMem ) = 0 then
raise EClipboardError( eSetDataFailed );
// Note: API docs are unclear as to whether the memory block has
// to be freed in case of failure. Since failure is unlikely here
// lets blithly ignore this issue for now.
end; { DataToClipboard }
{-- DataFromClipboard -------------------------------------------------}
{: Copies data from the clipboard into a stream
@Param fmt is the clipboard format to look for
@Param S is the stream to copy to
@precondition S <> nil
@postcondition If data was copied the streams position will have moved
@desc Tries to get a memory block for the requested clipboard format.
Nothing
further is done if this fails (because the format is not available or
the clipboard is not open, we treat neither as error here), otherwise
the memory handle is locked and the data copied into the stream. <P>
Note that we cannot determine the actual size of the data originally
copied to the clipboard, only the allocated size of the memory block!
Since GlobalAlloc works with a granularity of 32 bytes the block may be
larger than required for the data and thus the stream may contain some
spurious bytes at the end. There is no guarantee that these bytes will
be 0. <P>
If the memory handle obtained from the clipboard cannot be locked we
raise an <see class=EClipboardError> exception.
Created 5.7.2000 by P. Below
@Raises EClipboardError
}{---------------------------------------------------------------------}
procedure DataFromClipboard( fmt: DWORD; S: TStream );
var
hMem: THandle;
pMem: Pointer;
datasize: DWORD;
begin { DataFromClipboard }
Assert( Assigned( S ));
hMem := GetClipboardData( fmt );
if hMem <> 0 then
begin
datasize := GlobalSize( hMem );
if datasize > 0 then
begin
pMem := GlobalLock( hMem );
if pMem = nil then
raise EclipboardError.Create( eLockFailed );
try
S.WriteBuffer( pMem^, datasize );
finally
GlobalUnlock( hMem );
end;
end;
end;
end; { DatafromClipboard }
{-- CopyDataToClipboard -----------------------------------------------}
{: Copies a block of memory to the clipboard in a given format
@Param fmt is the clipboard format to use
@Param data is an untyped const parameter that addresses the data to copy
@Param datasize is the size of the data, in bytes
@Param emptyClipboardFirst determines if the clipboard should be emptied,
true by default
@Precondition The clipboard must not be open already
@Postcondition If emptyClipboardFirst is true all prior data will be
cleared from the clipboard, even if datasize is <= 0. The clipboard
is closed again.
@desc Tries to open the clipboard, empties it if required and then tries to
copy the passed data to the clipboard. This operation is a NOP if
datasize <= 0. If the clipboard cannot be opened a <see
class=EClipboardError>
is raised.
Created 5.7.2000 by P. Below
@Raises EClipboardError
}{---------------------------------------------------------------------}
procedure CopyDataToClipboard( fmt: DWORD; const data; datasize: Integer;
emptyClipboardFirst: Boolean = true );
begin { CopyDataToClipboard }
if OpenClipboard( 0 ) then
try
if emptyClipboardFirst then
EmptyClipboard;
DataToClipboard( fmt, data, datasize );
finally
CloseClipboard;
end
else
raise EclipboardError.Create( eCannotOpenClipboard );
end; { CopyDataToClipboard }
{-- StringToClipboard -------------------------------------------------}
{: Copies a string to clipboard in CF_TEXT clipboard format
@Param S is the string to copy, it may be empty.
@Precondition The clipboard must not be open already.
@Postcondition Any prior clipboard content will be cleared, but only
if S was not empty. The clipboard is closed again.
@desc Hands the brunt of the work off to <See routine=CopyDataToClipboard>,
but only if S was not empty. Otherwise nothing is done at all.<BR>
Created 5.7.2000 by P. Below
@Raises EClipboardError
}{---------------------------------------------------------------------}
procedure StringToClipboard( const S: String );
begin
if Length(S) > 0 Then
CopyDataToClipboard( CF_TEXT, S[1], Length(S)+1);
end; { StringToClipboard }
{-- CopyDataFromClipboard ---------------------------------------------}
{: Copies data from the clipboard into a stream
@Param fmt is the clipboard format to look for
@Param S is the stream to copy to
@Precondition S <> nil<P>
The clipboard must not be open already.
@Postcondition If data was copied the streams position will have moved.
The clipboard is closed again.
@desc Tries to open the clipboard, and then tries to
copy the data to the passed stream. This operation is a NOP if
the clipboard does not contain data in the requested format.
If the clipboard cannot be opened a <see class=EClipboardError>
is raised.
Created 5.7.2000 by P. Below
@Raises EClipboardError
}{---------------------------------------------------------------------}
procedure CopyDataFromClipboard( fmt: DWORD; S: TStream );
begin { CopyDataFromClipboard }
Assert( Assigned( S ));
if OpenClipboard( 0 ) then
try
DataFromClipboard( fmt , S );
finally
CloseClipboard;
end
else
raise EclipboardError.Create( eCannotOpenClipboard );
end; { CopyDataFromClipboard }
{-- ClipboardAsString -------------------------------------------------}
{: Returns any text contained on the clipboard
@Returns the clipboards content if it contained something in CF_TEXT
format, or an empty string.
@Precondition The clipboard must not be already open
@Postcondition The clipboard is closed.
@desc If the clipboard contains data in CF_TEXT format it is copied to a
temp memory stream, zero-terminated for good measure and copied into
the result string.
Created 5.7.2000 by P. Below
@Raises EClipboardError
}{---------------------------------------------------------------------}
function ClipboardAsString: String;
const
nullchar: Char = #0;
var
ms: TMemoryStream;
begin { ClipboardAsString }
if not IsClipboardFormatAvailable( CF_TEXT ) then
Result := EmptyStr
else
begin
ms:= TMemoryStream.Create;
try
CopyDataFromClipboard( CF_TEXT , ms );
ms.Seek( 0, soFromEnd );
ms.WriteBuffer( nullChar, Sizeof( nullchar ));
Result := PChar( ms.Memory );
finally
ms.Free;
end;
end;
end; { ClipboardAsString }
{-- ClipboardHasFormat ------------------------------------------------}
{: Checks if the clipboard contains data in the specified format
@Param fmt is the format to check for
@Returns true if the clipboard contains data in this format, false
otherwise
@Precondition none
@Postcondition none
@desc This is a simple wrapper around an API function.
Created 5.7.2000 by P. Below
}{---------------------------------------------------------------------}
function ClipboardHasFormat( fmt: DWORD ): Boolean;
begin { ClipboardHasFormat }
Result := IsClipboardFormatAvailable( fmt );
end; { ClipboardHasFormat }
end.
样品用途:
在 SQL 编辑器、文本编辑器或其他任何工具中准备文本:
选择 姓名, 名称, 出生日期 从 雇员
选择所有文本,然后使用 Ctrl+C 复制到剪贴板。
切换到 IDE 的代码编辑器,运行 ClipboardToStringConst 应用程序(使用您添加的工具菜单项,或您想要的任何其他方式)。将编辑器的光标(插入点)放在您希望出现常量文本的位置,然后按 Ctrl+V 粘贴文本。
常量 MySQL文本 = | // 管道表示插入点。
结果:
常量 MySQLText = '选择'+ 'lname, '+ ' fname, '+ '多布'+ '来自'+ ' 雇员 ';
你的意思是这样的?
myStr := 'first line'#13#10'secondline'#13#10'thirdline';
我们遇到了同样的问题,最后我们创建了一个小型 IDE 插件(与现有解决方案合并)。这会创建两个额外的菜单项(额外的复制和粘贴)。其中一个将剪贴板的格式化内容粘贴到代码编辑器,另一个反向执行相同的操作(将选择的内容复制到剪贴板并删除多余的字符)。
要使用这个:
示例代码:
unit ClipboardWizard;
interface
uses
Windows, SysUtils, Classes, ToolsAPI,
{$ifdef VER280} // XE7
VCL.Menus
{$else}
Menus
{$endif};
type
TClipboardWizard = class(TInterfacedObject, IOTAWizard)
private
FMainMenuItem, FCopyMenuItem, FPasteMenuItem: TMenuItem;
// Formatting
function GetFormattedString: string;
function RemoveUnneededChars(const Value: string): string;
// Menu events
procedure CopyToClipboard(Sender: TObject);
procedure PasteFromClipboard(Sender: TObject);
public
// TObject
constructor Create;
destructor Destroy; override;
// IOTANotifier
procedure AfterSave;
procedure BeforeSave;
procedure Destroyed;
procedure Modified;
// IOTAWizard
function GetIDString: string;
function GetName: string;
function GetState: TWizardState;
procedure Execute;
end;
procedure Register;
implementation
uses
Vcl.Clipbrd, System.StrUtils;
procedure Register;
begin
RegisterPackageWizard(TClipboardWizard.Create);
end;
// Formatting
function TClipboardWizard.RemoveUnneededChars(const Value: string): string;
var
List: TStringList;
q: integer;
s : string;
begin
if Trim(Value) <> '' then
begin
List := TStringList.Create;
try
List.Text := Value;
for q := 0 to List.Count - 1 do
begin
s := Trim(List[q]);
if Length(s) > 0 then
if s[1] = '''' then
s := Copy(s, 2, Length(s));
s := TrimLeft(ReverseString(s));
if Length(s) > 0 then
if s[1] = '+' then
s := TrimLeft(Copy(s, 2, Length(s)));
if Length(s) > 0 then
if s[1] = ';' then
s := TrimLeft(Copy(s, 2, Length(s)));
if Length(s) > 0 then
if s[1] = '''' then
s := TrimLeft(Copy(s, 2, Length(s)));
s := StringReplace(s, '''''', '''', [rfReplaceAll]);
List[q] := ReverseString(s)
end;
Result := List.Text;
finally
List.Free;
end;
end
else
Result := '';
end;
procedure TClipboardWizard.CopyToClipboard(Sender: TObject);
begin
with BorlandIDEServices as IOTAEditorServices do
if Assigned(TopView) then
Clipboard.AsText := RemoveUnneededChars(TopView.Block.Text);
end;
function TClipboardWizard.GetFormattedString: string;
const
FSingleQuote = '''';
Indent: array [boolean] of string = (' ', '');
EndChar: array [boolean] of string = (' +', ';');
var
List: TStringlist;
q: Integer;
begin
if Clipboard.HasFormat(CF_TEXT) then
begin
List := TStringlist.Create;
try
List.Text := Clipboard.AsText;
for q := 0 to List.Count - 1 do
List[q] := Indent[q <> 0] + AnsiQuotedStr(TrimRight(List[q]) + #32, FSingleQuote) +
EndChar[q = (List.Count - 1)];
Result := List.Text;
finally
List.Free;
end;
end;
end;
procedure TClipboardWizard.PasteFromClipboard(Sender: TObject);
begin
with BorlandIDEServices as IOTAEditorServices do
if Assigned(TopView) then
begin
TopView.Buffer.EditPosition.InsertText(GetFormattedString);
TopView.Paint; // invalidation
end;
end;
{ Anything else }
constructor TClipboardWizard.Create;
var
NTAServices : INTAServices;
begin
NTAServices := BorlandIDEServices as INTAServices;
// Main Menu
FMainMenuItem := TMenuItem.Create(nil);
FMainMenuItem.Caption := 'Clibrd Extra' ;
NTAServices.MainMenu.Items.Add(FMainMenuItem);
// Sub Menus
FCopyMenuItem := TMenuItem.Create(nil);
FCopyMenuItem.Caption := 'Copy to clipboard';
FCopyMenuItem.OnClick := Self.CopyToClipboard;
FMainMenuItem.Add(FCopyMenuItem);
FPasteMenuItem := TMenuItem.Create(nil);
FPasteMenuItem.Caption := 'Paste from clipboard';
FPasteMenuItem.OnClick := Self.PasteFromClipboard;
FMainMenuItem.Add(FPasteMenuItem);
end;
destructor TClipboardWizard.Destroy;
begin
if Assigned(FPasteMenuItem) then
FreeAndNil(FPasteMenuItem);
if Assigned(FCopyMenuItem) then
FreeAndNil(FCopyMenuItem);
if Assigned(FMainMenuItem) then
FreeAndNil(FMainMenuItem);
inherited;
end;
{ IOTANotifier }
procedure TClipboardWizard.AfterSave;
begin
end;
procedure TClipboardWizard.BeforeSave;
begin
end;
procedure TClipboardWizard.Destroyed;
begin
end;
procedure TClipboardWizard.Modified;
begin
end;
{ IOTAWizard }
function TClipboardWizard.GetIDString: string;
begin
Result := 'Clipboard.Wizard7';
end;
function TClipboardWizard.GetName: string;
begin
Result := 'Clipboard Wizard7';
end;
function TClipboardWizard.GetState: TWizardState;
begin
Result := [];
end;
procedure TClipboardWizard.Execute;
begin
end;
end.
我知道代码并不完美,但它可以工作:-)
您可以考虑将您的 SQL 放在表单或数据模块上的 TQuery 组件中。
这解决了复制/粘贴问题,但它引入了其他问题(例如查询的两个版本之间的差异更糟)。
您不能在没有引号的情况下在多行上定义字符串:
const
myString = 'this is a long string that extends' +
'to a second line';
虽然,您可以使字符串失去控制字符,例如:
const
myString = #83#84#82#73#78#71;
但这并不归因于可读代码。
在 Delphi >= 2007 的版本中,如果您在多行中输入带引号的字符串,如果您自己不关闭引号,它将自动在下一行添加右引号和 + '。
这不是解决问题的方法,但它确实有助于加快输入长字符串的速度。
简短的回答是否定的,它不能完成。(我知道这不是你想听到的。)
然而, Andreas Hausladen确实开发了一个能够做到这一点的扩展。我用谷歌搜索但找不到它。我认为这是在他的 DLangExtensions 包中,他已经在 2007 年底放弃了对它的支持。:(
我很惊讶没有人提到资源。虽然第一次实现很痛苦,但是一旦你完成了它,你就可以实现从文件中检索长的多行字符串而不会太麻烦。我在这里找到的随机说明:http: //www.delphibasics.info/home/delphibasicssnippets/usingresourcefileswithdelphi
我迟到了,但如果 GExperts 我没有选择:
快速解决方案:使用 IDE 宏记录器...
复制文本(开始录制 SHIFT + STRG + R)
按 [Pos1] ['] [End] [' + sLineBreak +] [将行改为向下](停止录制 SHIFT + STRG + R)(重播击键 SHIFT + STRG + P)重复直到最后一行...删除+ 太多了...
部分完成;
' 对于字符串的转义不是以这种方式完成的......