如何在不引用每一行的情况下分配多行字符串值?

时间:2009-12-09 08:56:53

标签: delphi pascal

有没有办法在Delphi中分配多行字符串值而不必引用每一行?

编辑(具体问题):我有一些SQL查询,我想在Delphi外面测试。复制查询时,每次添加和替换引号都会产生一些开销。

9 个答案:

答案 0 :(得分:7)

这是您可以添加到IDE的“工具”菜单中的应用程序的代码,可能有所帮助。它被TeamB成员Peter Below发布了一段时间回到CodeGear新闻组之一:

program ClipToStringConst;

// Remove the dot from the line below for a console app, 
// per Rob Kennedy's comment. It works fine without being
// a console app.
{.$APPTYPE CONSOLE}
uses
  Windows,
  Classes,
  Sysutils,
  APIClipboard;

const
  cIndent = '  '; // 2 spaces
  cSingleQuote = '''';
  EndChar : array [Boolean] of Char = ('+',';');

procedure Process;
var
  SL: TStringlist;
  i, max: Integer;
begin
  if ClipboardHasFormat( CF_TEXT ) then
  begin
    SL := TStringlist.Create;
    try
      SL.Text := ClipboardAsString;
      max := SL.count-1;
      for i:= 0 to max do
        SL[i] := cIndent +
                 AnsiQuotedStr( TrimRight(SL[i])+#32, cSingleQuote ) +
                 EndChar[i = max];
      StringToClipboard( SL.Text );
    finally
      SL.Free;
    end; { Finally }
  end;
end;

begin
  try
    Process;
  except
    on E: Exception do
      ShowException( E, ExceptAddr );
  end;
end.

在测试完成后,只需在SQL管理工具中选择文本并将其复制到剪贴板即可。切换到Delphi代码编辑器,将插入点放在您希望显示常量文本的位置,从“工具”菜单中选择“剪贴板到常量”或​​您调用的任何内容,然后按Ctrl + V将其粘贴到编辑器中。

这是一个非常方便的小工具。你也可以修改它以相反的方式工作(ConstantToClipboard)来删除源格式并恢复原始SQL,尽管我还没有打算这样做。

编辑:错过了一个单元(APIClipboard)。显然,这需要是一个单独的单元。再次,感谢Peter Below:

{== 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编辑器,文本编辑器或其他任何内容中准备文本:

SELECT 
  lname,
  fname,
  dob
FROM
  employees

选择所有文本,然后使用Ctrl + C复制到剪贴板。

切换到IDE的代码编辑器,运行ClipboardToStringConst应用程序(使用您添加的“工具”菜单项或其他任何您想要的方法)。将编辑器的光标(插入点)放在要显示常量文本的位置,然后按Ctrl + V粘贴文本。

const
  MySQLText = |            // The pipe indicates the insertion point.

结果:

const
  MySQLText =   'SELECT '+
  '  lname, '+
  '  fname, '+
  '  dob '+
  'FROM '+
  '  employees ';

答案 1 :(得分:4)

你的意思是这样的吗?

myStr := 'first line'#13#10'secondline'#13#10'thirdline';

答案 2 :(得分:3)

我们遇到了同样的问题,最后我们创建了一个小型IDE插件(与现有解决方案合并)。这会创建两个额外的菜单项(复制和粘贴额外)。其中一个将剪贴板的格式化内容粘贴到代码编辑器,另一个反向执行相同的操作(将选择的内容复制到剪贴板并删除额外的字符)。

要使用它:

  1. 在Delphi中创建新包
  2. 添加到&#34; designide&#34;要求部分(并删除其他任何东西)
  3. 创建新单位,然后复制代码
  4. 构建并安装
  5. 示例代码:

    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.
    

    我知道代码不完美,但它有效: - )

答案 3 :(得分:2)

如果没有引号,则无法在多行上定义字符串:

const
  myString = 'this is a long string that extends' +
             'to a second line';

虽然,你可以创建一个字符串失控的字符,如:

const 
  myString = #83#84#82#73#78#71;

但这并不归因于可读代码。

答案 4 :(得分:2)

在Delphi&gt; = 2007的版本中,如果您在多行中输入带引号的字符串,如果您不自行关闭引号,它将自动在下一行添加结束引号和+'。

这不是问题的解决方案,但它确实有助于加快长字符串的输入。

答案 5 :(得分:2)

简短的回答是否定的,不能做到。 (我知道这不是你想听到的。)

然而Andreas Hausladen确实开发了一种能够实现这一目标的扩展。我用谷歌搜索但找不到它。我认为这是在他的DLangExtensions包中,他已经在2007年底放弃了支持。 :(

答案 6 :(得分:2)

您可以考虑将SQL放在表单或数据模块上的TQuery组件中。

这解决了复制/粘贴问题,但它引入了其他问题(例如查询的两个版本之间的差异更严重)。

答案 7 :(得分:2)

我很惊讶没有人提到的资源。虽然第一次实现很痛苦,但是一旦你完成它就可以实现从文件中检索长多行字符串而不会有太多麻烦。我在这里找到的随机说明:http://www.delphibasics.info/home/delphibasicssnippets/usingresourcefileswithdelphi

答案 8 :(得分:0)

使用GExperts

  • 启用 GExperts->编辑专家->将字符串粘贴为
  • 分配快捷方式

enter image description here