How to assign a multi-line string value without quoting each line?

Is there a way to assign a multi-line string value in Delphi without specifying each line?

Edit (specific problem): I have SQL queries that I want to test outside of Delphi. When copying queries, there is a bit of overhead to add and replace quotation marks each time.

+6
delphi pascal
source share
8 answers

Here is the code for the application, which you can add to the IDE tool menu, which can help. He was posted some time ago to one of the CodeGear newsgroups as a member of TeamB Peter Below:

  program ClipToStringConst;

 // Remove the dot from the line below for a console app, 
 // per Rob Kennedy 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.

Just select the text in the SQL management tool after you have tested it and copied it to the clipboard. Switch to the Delphi code editor, place the insertion point where you want the text to appear, select "Clipboard To Const" or whatever you called in the "Tools" menu, and then Ctrl + V to paste it into the editor.

This is a pretty handy tool. You can also change it to work the other way around (ConstantToClipboard) to remove the original formatting and return to raw SQL, although I have not bothered to do this yet.

EDIT: skipped block (APIClipboard). Obviously, this should be a separate unit. Again, thanks to 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. 

Using an example:

Prepare the text in an SQL editor, text editor, or something else:

  SELECT 
   lname
   fname
   dob
 FROM
   employees

Select all the text and copy it to the clipboard using Ctrl + C.

Switch to the IDE code editor, start the ClipboardToStringConst application (using the "Tools" menu item you added or any other tools you want). Place the editor cursor (insertion point) where you want the constant text to be displayed, and press Ctrl + V to paste the text.

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

Result:

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

Do you mean something like this?

 myStr := 'first line'#13#10'secondline'#13#10'thirdline'; 
+4
source share

We had the same problem, and finally we created a small IDE plugin (combined with existing solutions). This creates two additional menu items (Copy and Paste extra). One of them inserts formatted content into the clipboard into the code editor, the other does the same in the reverse order (copies the contents of the selection to the clipboard and removes additional attributes).

To use this:

  • Create a new package in Delphi
  • Add to designide to require a section (and delete anything else)
  • Create a new block and copy the code
  • Assembly and installation

Code example:

 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. 

I know the code is not perfect, but it works :-)

+3
source share

You cannot define a string on multiple lines without quotes:

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

Although you can make a string of control characters, for example:

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

But this does not apply to the code being read.

+2
source share

The short answer is no, it is impossible to do. (I know this is not what you want to hear.)

However, Andreas Hausladen has developed an extension that can do just that. I was looking for her, but could not find her. I think that was in his DLangExtensions package, which he refused support at the end of 2007. :(

+2
source share

You might consider placing SQL in TQuery components in forms or data modules.

This solves the copy / paste problem, but it introduces others (for example, the differences between the two versions of the request are worse).

+2
source share

I am surprised that no one has mentioned resources. Although it’s a pain to implement for the first time, as soon as you have done it, as soon as you can realize extracting long multi-line lines from files without any problems. Random instructions I found here: http://www.delphibasics.info/home/delphibasicssnippets/usingresourcefileswithdelphi

+2
source share

In versions of Delphi> = 2007, if you enter a line with quotation marks along several lines, it will automatically add a final quote and + 'on the next line if you yourself do not close the quote.

This is not a solution to the problem, but it helps speed up long line input.

+1
source share

All Articles