2009-12-09 6 views
6

C'è un modo per assegnare un valore di stringa multilinea in Delphi senza dover citare ogni riga?Come assegnare un valore di stringa multilinea senza quotare ogni riga?

Modifica (il problema specifico): Ho alcune query SQL che voglio testare all'esterno di Delphi. Quando si copiano le query, è un po 'eccessivo aggiungere e sostituire le virgolette ogni volta.

+0

Solo per curiosità, in cui al di fuori Delphi si prova? Se capita di essere in Toad (uno strumento comune) c'è una funzione lì esattamente per questo. Copia la stringa in Delphi e in Rospo premi "Ctrl + P" (striscia sql dal codice non-sql) –

risposta

6

Ecco il codice per un'applicazione è possibile aggiungere al menu Strumenti del IDE che potrebbe aiutare E 'stato pubblicato un po' indietro a uno dei gruppi di discussione CodeGear da parte degli TeamB Peter Sotto:

 
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. 

Basta selezionare il testo nello strumento di gestione SQL dopo averlo testato e copiato negli Appunti. Passare all'editor di codice Delphi, posizionare il punto di inserimento nel punto in cui si desidera visualizzare il testo costante, selezionare "Appunti a Const" o qualsiasi altra cosa chiamata dal menu Strumenti, quindi Ctrl + V per incollarlo nell'editor

È un piccolo strumento molto utile. anche modificarlo per funzionare in modo opposto (ConstantToClipboard) per rimuovere la formattazione di origine e tornare a SQL raw, anche se non mi sono preso la briga di farlo ancora.

MODIFICA: mancava un'unità (APIClipboard). Questo deve essere un'unità separata, ovviamente. Ancora una volta, grazie a Peter Sotto: l'uso

{== 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. 

Esempio:

preparare il testo con l'editor di SQL, editor di testo, o qualsiasi altra cosa:

 
SELECT 
    lname, 
    fname, 
    dob 
FROM 
    employees 

selezionare tutto il testo e copiare negli appunti usando Ctrl + C.

Passare editor di codice del IDE, eseguire l'applicazione ClipboardToStringConst (utilizzando la voce di menu Strumenti si è aggiunto, o qualsiasi altro mezzo che si desidera).Posiziona il cursore dell'editor (punto di inserimento) nel punto in cui desideri visualizzare il testo costante e premi Ctrl + V per incollare il testo.

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

Il risultato:

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

Se qualcuno si è preso la briga di scrivere questo, presumo che non ci sia supporto per l'assegnazione di stringhe non quotate in Delphi. Grazie! – Tihauan

+0

Tihauan: vedere la modifica per la fonte dell'unità mancante. –

+0

Sarebbe ancora meglio se non fosse un programma per console. Rimuovi la direttiva Apptype in modo che non si verifichi uno sfarfallio con una finestra della console che non utilizza mai. (Esso utilizza la console per visualizzare i messaggi di eccezione, ma poi esiste prima che si possa leggere un programma non-console visualizzerà il messaggio in una finestra di dialogo..) –

4

Vuoi dire qualcosa del genere?

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

Grazie, non è esattamente quello di cui avevo bisogno. Ho modificato la domanda aggiungendo il problema specifico. – Tihauan

+0

Questa è una domanda completamente diversa ... si dovrebbe considerare regolare il titolo o l'apertura di una nuova domanda per questo – Scoregraphic

2

Non è possibile definire una stringa su più righe senza le virgolette:

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

Anche se, è possibile effettuare una stringa di caratteri di controllo come:

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

Ma questo non attribuisce al codice leggibile.

1

Nelle versioni di Delphi> = 2007, se si immette una stringa tra virgolette su più righe, si aggiungerà automaticamente una quotazione di chiusura e + 'sulla riga successiva se non si chiude la quota personalmente.

Non è una soluzione al problema, ma aiuta ad accelerare la digitazione in stringhe lunghe.

+0

sto usando Delphi 2007 e non vedo questo comportamento. Devo farlo in un certo ordine o abilitarlo nelle opzioni? –

2

La risposta breve è no, non può essere eseguita. (So ​​che non è quello che vuoi sentire.)

Tuttavia Andreas Hausladen ha sviluppato un'estensione capace proprio di questo. Ho cercato su Google ma non sono riuscito a trovarlo. Penso che fosse nel suo pacchetto DLangExtensions, di cui ha abbandonato il supporto già alla fine del 2007. . :(

2

Si potrebbe valutare l'ipotesi di SQL nei componenti TQuery sulle forme o moduli di dati.

Questo risolve il problema copia/incolla, ma introduce altri (come le diff tra due versioni di una query di essere peggio).

3

abbiamo avuto lo stesso problema, e alla fine abbiamo creato un piccolo plug-in IDE (fusa con le soluzioni esistenti). Questo crea due voci di menu extra (copia e incolla extra). Uno di questi incolla il contenuto formattato degli appunti nell'editor di codice, l'altro fa la stessa cosa al contrario (copia il contenuto della selezione negli appunti e rimuove i caratteri extra).

Per utilizzare questo:

  1. Crea nuovo pacchetto in Delphi
  2. Aggiungi a "designide" per richiede sezione (e rimuovere qualsiasi altra cosa)
  3. Creare nuova unità, e copiare il codice
  4. costruire e installare il codice

Esempio:

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. 

So che il codice non è perfetto, ma funziona :-)

2

Sono sorpreso risorse menzionate di nessuno. Anche se è un problema da implementare per la prima volta, una volta che l'hai fatto, puoi implementare il recupero di lunghe stringhe multilinea dai file senza troppi problemi. istruzioni casuali che ho trovato qui: http://www.delphibasics.info/home/delphibasicssnippets/usingresourcefileswithdelphi

+0

Questo è il modo migliore per la mia idea di includere lunghe query SQL che non possono entrare in un componente TQuery (ad esempio TFDBatchMoveSQLReader, che ha solo una stringa proprietà per il suo SQL, non per TStringList). Più uno – DaveBoltman

Problemi correlati