Delphi Tstringlist, получить строки как строку в кавычках с разделителями-запятыми?

У меня есть Tstringlist, содержащий список ключей, используемых в таблице базы данных. Мне нужен простой способ создать одну строку, содержащую все ключи, каждый из которых разделен запятой и заключен в одинарные кавычки. Это сделано для того, чтобы его можно было использовать в операторе SQL «IN», например, WHERE FieldX IN («Один», «Два», «Три»).

Я пытался использовать кавычки, но они игнорируются при чтении запятой. например следующий код

procedure junk;
var
  SL : Tstringlist;
  s : string;
begin
 SL := Tstringlist.Create;
 SL.Delimiter :=','; //comma delimiter
 SL.QuoteChar := ''''; //single quote around strings
 SL.Add('One');
 SL.Add('Two');
 SL.Add('Three');
 try
   s :=  SL.commatext;
   showmessage(s);
 finally
   SL.Free;
 end; //finally
end; //junk

показывает сообщение One,Two,Three - без кавычек.

Я знаю, что могу сделать это длинным путем, как в

procedure junk;
var
  SL : Tstringlist;
  s : string;
  i : integer;
begin
 SL := Tstringlist.Create;
 SL.Delimiter :=','; //comma delimiter
 SL.Add('One');
 SL.Add('Two');
 SL.Add('Three');
 try
 s := '';
 for I := 0 to SL.Count - 1 do
    begin
    s := s +  ',' + '''' + SL[i] + '''';
    end;
 delete(s,1,1);
 showmessage(s);
 finally
   SL.Free;
 end;//finally
end;

но есть ли более простой способ использования свойств самого Tstringlist?


person user3209752    schedule 18.11.2015    source источник
comment
Ваши попытки сделать это недостаточны, потому что вы не экранируете символы кавычек   -  person David Heffernan    schedule 19.11.2015


Ответы (5)


Если вы используете D2006 или более позднюю версию, вы можете использовать ПОМОЩНИК КЛАССА:

USES Classes,StrUtils;

TYPE
  TStringListHelper = CLASS HELPER FOR TStrings
                        FUNCTION ToSQL : STRING;
                      END;

FUNCTION TStringListHelper.ToSQL : STRING;
  VAR
    S : STRING;

  FUNCTION QuotedStr(CONST S : STRING) : STRING;
    BEGIN
      Result:=''''+ReplaceStr(S,'''','''''')+''''
    END;

  BEGIN
    Result:='';
    FOR S IN Self DO BEGIN
      IF Result='' THEN Result:='(' ELSE Result:=Result+',';
      Result:=Result+QuotedStr(S)
    END;
    IF Result<>'' THEN Result:=Result+')'
  END;

Этот код:

SL:=TStringList.Create;
SL.Add('One');
SL.Add('Two');
SL.Add('Number Three');
SL.Add('It''s number 4');
WRITELN('SELECT * FROM TABLE WHERE FIELD IN '+SL.ToSQL);

затем выведет:

SELECT * FROM TABLE WHERE FIELD IN ('One','Two','Number Three','It''s number 4')
person HeartWare    schedule 18.11.2015
comment
Полезный ответ. Я уже написал целую единицу вспомогательных функций, точно так же, как вы предлагаете, которые делают такие вещи, как ToSQL.text, ToSQL.date и т. д., а также наоборот, например, FromDB.date, FromSQL.time и т. д., чтобы упростить создание sql с использованием переменные делфи. По сути, вы предлагаете, чтобы он просто поместил мой код «длинного пути» выше в упакованной форме внутри этого модуля. Хорошая идея для аккуратности, но на самом деле не отвечает на мой актуальный вопрос о том, может ли Tstringlist сделать это сам по себе. - person user3209752; 18.11.2015
comment
Это невозможно, но с помощью CLASS HELPER вы можете заставить его выглядеть так, как будто он может :-) - person HeartWare; 18.11.2015
comment
Я думаю, что приму ваш ответ, отчасти потому, что вы на самом деле ответили на вопрос, хотя и сказали, что это невозможно сделать ‹ухмылка›, а отчасти потому, что вы делаете так, как вы предлагаете, и помещаете мой «длинный код» в другую вспомогательную функцию. действительно сделать так, чтобы это выглядело так, будто это Tstringlist делает всю работу. - person user3209752; 18.11.2015

Используйте sl.DelimitedText вместо sl.CommaText, чтобы он соответствовал вашим настройкам. CommaText временно изменит Delimiter и QuoteChar на некоторые жестко заданные значения.

person Uwe Raabe    schedule 18.11.2015
comment
Я уже пробовал это, это не имеет значения, оно все равно получается как One, Two, Three без одинарных кавычек вокруг значений. - person user3209752; 18.11.2015
comment
@user3209752: user3209752: Это может быть потому, что строки не нужно заключать в кавычки, так как в них нет пробелов или запятых. Попробуйте использовать значения, содержащие запятые и/или пробелы... - person HeartWare; 18.11.2015
comment
Но мне нужно, чтобы они были указаны в последней строке с разделителями-запятыми, иначе SQL, использующий эту строку, будет интерпретировать их как имена столбцов, а не строковые литералы. то есть FieldX IN (один, два, три) сильно отличается от FieldX IN («Один», «Два», «Три») - person user3209752; 18.11.2015
comment
Документы на самом деле поддерживают вас: When retrieving DelimitedText, the resulting value delimits individual strings in two ways: each string is surrounded (before and after) by the quotation marks character specified by the QuoteChar property. In addition, individual strings are separated by the character specified by the Delimiter property.. Это просто не делает того, что указано в первой части (по крайней мере, не для строк из одного слова). - person Tom Brunberg; 18.11.2015

На CommaText (и, по-видимому, также на DelimitedText) нельзя полагаться при добавлении кавычек, потому что они по-разному обрабатывают строки из одного и нескольких слов.

При извлечении CommaText любая строка в списке, содержащая пробелы, запятые или кавычки, будет заключена в двойные кавычки, а любые двойные кавычки в строке будут повторяться.

Кажется, что не существует какой-либо комбинации только с TStringList, поэтому я предлагаю вам добавить строки, используя QuotedStr. Следующие настройки работают по вашему желанию, независимо от того, являются ли строки отдельными словами или составными словами:

 SL := Tstringlist.Create;
 SL.Delimiter :=','; //comma delimiter
 SL.StrictDelimiter := True;
// SL.QuoteChar := ''''; //single quote around strings
// SL.Add('One');
// SL.Add('Two words');
// SL.Add('Three');
 SL.Add(QuotedStr('One'));
 SL.Add(QuotedStr('Two words'));
 SL.Add(QuotedStr('Three'));
 s := SL.CommaText; // or SL.DelimitedText;
 showmessage(s);

Выход:

«Раз», «Два слова», «Три»

person Tom Brunberg    schedule 18.11.2015
comment
StrictDelimiter не делает того, что может вас удивить - person fantaghirocco came to Rome; 18.11.2015
comment
@fantaghirocco Ох? Что это может быть? - person Tom Brunberg; 18.11.2015
comment
был одним из моих тестов: вы пробовали свой код? он не печатает кавычки, как ожидалось. Кажется, это не простой путь... - person fantaghirocco came to Rome; 18.11.2015
comment
@fantaghirocco Как вы видите в последней строке. Но перечитав, мое первое утверждение о CommaText не дошло до того, как предполагалось. Спасибо - person Tom Brunberg; 18.11.2015
comment
в моем последнем комментарии я не заметил использования QuotedStr: очевидно, он печатает то, что ожидалось. Прошу прощения О:-) - person fantaghirocco came to Rome; 18.11.2015
comment
Но размещение кавычек в списке строк в первую очередь не сильно отличается от моего «долгого пути», когда я потом добавляю кавычки. Я до сих пор не могу получить список строк, чтобы вставить их для меня. Я попробовал StrictDelimiter = true, но, как вы узнали, это не имеет значения для OUTPUT, только при загрузке списка строк в первую очередь. В моем случае фактический список строк загружается с помощью другого программного кода, а не упрощенного способа, который я показал в своем примере, поэтому я не могу помещать кавычки в источник. - person user3209752; 18.11.2015
comment
@user3209752 user3209752 Конечный результат по сравнению с вашим «долгим путем» по существу такой же, но его лучше кодировать. Кажется, есть ошибка с TStringList, я исследую. - person Tom Brunberg; 18.11.2015

Методы TStrings.DelimitedText заключают в кавычки только строки, когда это необходимо: когда есть что-то, связанное с O'ne, это будет напечатано как 'O''ne', т.е. удвоит кавычки.

Вы можете добиться того, чего хотите, установив удобный разделитель и свойство QuoteChar в значение #0, а затем заменив разделители кавычками.

procedure junk;
var
  sl : Tstringlist;
  s: string;
begin
  sl := Tstringlist.Create;
  try
    sl.Delimiter := '|';
    sl.QuoteChar := #0;//no quote
    sl.Add('On''e');
    sl.Add('Two');
    sl.Add('Three');

    //escape the single quotes
    s := StringReplace(sl.DelimitedText, '''', '''''', [rfReplaceAll]);

    //replace the delimiters and quote the text
    s := '''' +  StringReplace(s, sl.Delimiter, ''',''', [rfReplaceAll]) + '''';

    WriteLn(s);

  finally
    sl.Free;
  end;
end;
person fantaghirocco came to Rome    schedule 18.11.2015
comment
Это еще сложнее, чем мой «долгий путь»! Вы сообщаете списку строк, чего ожидать, с помощью разделителей в его входной строке, чего я не могу сделать, поскольку ввод генерируется в другом месте без разделителей, плюс вы явно заключаете кавычки вокруг каждой строки, что по той же причине я не могу сделать. Я не могу изменить то, что содержит список строк. Похоже, вариант моего «длинного пути» будет единственным путем. - person user3209752; 18.11.2015
comment
строки объекта sl никогда не меняются. Вы можете назначить Delimiter и QuoteChar даже после того, как список будет заполнен значениями. Это влияет только на вывод DelimitedText - person fantaghirocco came to Rome; 18.11.2015

Я решил аналогичную проблему с чем-то вроде этого:

  s := ''' + StringReplace(sl.CommaText, ',', ''',''', [rfReplaceAll]) + '''
person Moore    schedule 03.12.2019
comment
Произойдет ошибка, если одна из записей в списке строк содержит запятую... Или если одна из записей содержит одинарную кавычку... - person HeartWare; 03.12.2019