代码之家  ›  专栏  ›  技术社区  ›  Tihauan

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

  •  7
  • Tihauan  · 技术社区  · 15 年前

    在Delphi中,有没有一种方法可以在不引用每一行的情况下分配多行字符串值?

    编辑(具体问题):我有一些SQL查询需要在Delphi之外进行测试。复制查询时,每次都要添加和替换引号,这有点开销。

    8 回复  |  直到 8 年前
        1
  •  7
  •   Ken White    15 年前

    下面是应用程序的代码,您可以将其添加到IDE的“工具”菜单中,这可能会有所帮助。它是由teamb成员peter发布到一个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)。显然,这需要一个单独的单元。再次感谢下面的彼得:

    {== 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 ';
    
        2
  •  4
  •   gabr    15 年前

    你的意思是这样的?

    myStr := 'first line'#13#10'secondline'#13#10'thirdline';
    
        3
  •  3
  •   CsZsombor    9 年前

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

    使用此:

    1. 在Delphi中创建新包
    2. 添加到“designide”以要求节(并删除任何其他内容)
    3. 创建新单元并复制代码
    4. 构建和安装

    样例代码:

    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.
    

    我知道代码不完美,但它能起作用。

        4
  •  2
  •   Toon Krijthe    15 年前

    没有引号,不能在多行上定义字符串:

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

    尽管如此,您可以将字符串设置为不受控制的字符,如:

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

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

        5
  •  2
  •   Alan Clark    15 年前

    在Delphi>=2007的版本中,如果在多行上输入带引号的字符串,它将自动添加右引号,如果不自己关闭引号,则在下一行上添加“+”。

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

        6
  •  2
  •   T.J. Crowder    15 年前

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

    然而 Andreas Hausladen 确实开发了一个可以这样做的扩展。我在谷歌上搜索,但找不到。我想是在他的DLangeXTensions包里,他在2007年底已经放弃了对他的支持。:。(

        7
  •  2
  •   Nathan Sutcliffe    15 年前

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

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

        8
  •  2
  •   Jamie Kitson    8 年前

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

    推荐文章