Delphi: как динамически «разбивать» строку на подстроки в соответствии с (динамической) маской

#delphi #string #pascal

#delphi #строка #pascal

Вопрос:

Вот моя ситуация: у меня есть текстовый файл, содержащий множество строк одинаковой длины, представляющих записи, которые будут загружены в таблицу базы данных SQL, поэтому мне придется сгенерировать SQL-код из этих строк.
Затем у меня есть таблица в этой базе данных (назовем ее «таблицей форматирования»), которая сообщает мне, как форматируются строки и куда их загружать (каждая запись этой таблицы содержит имя целевой таблицы, имя поля, позицию данных и длину, на которые ссылаются строки из текстового файла).

Я уже решил эту проблему способом, который, я думаю, хорошо известен каждому программисту Delphi, используя Copy(string, pos, length) функцию и выполняя итерации по каждому полю на основе информации из «таблицы форматирования».
Это работает хорошо, но медленно, особенно когда мы говорим об исходных текстовых файлах с миллионом или более строк, каждая из которых представляет несколько десятков или даже сотен полей данных.

Что я пытаюсь сделать сейчас, так это «увидеть» исходные строки таким образом, чтобы они выглядели уже разделенными, избегая Copy() функции, которая постоянно создает новые строки, копируя содержимое из исходной строки, выделяя и освобождая память и так далее. Я бы сказал: «У меня есть целая строка, давайте рассмотрим ее таким образом, чтобы представить каждую ее «часть» (поле) за один шаг, без создания из нее подстрок».

Что могло бы решить мою проблему, так это какой-то способ определить динамическую структуру, такую как динамическая запись или динамический массив (не то, что Delphy называет динамическим массивом, скорее что-то вроде «динамического статического массива»), для «наложения» на строку, чтобы «наблюдать» за ней с этой точки зрения… Я не уверен, что мне достаточно ясно это объяснение… Однако Delphi (насколько мне известно) не реализует такого рода динамические структуры.

Это фрагмент (статического) кода, который делает то, что я хочу, за исключением отсутствия динамичности.

 procedure TForm1.FormCreate(Sender: TObject);
type
  PDecodeStr = ^TDecodeStr;
  TDecodeStr = record
    s1: Array[0..3] of AnsiChar;
    s2: Array[0..9] of AnsiChar;
    s3: Array[0..4] of AnsiChar;
    s4: Array[0..7] of AnsiChar;
    s5: Array[0..2] of AnsiChar;
  end;
var
  cWholeStr: AnsiString;
begin
  cWholeStr := '123456789012345678901234567890';
  Memo1.Lines.Add(PDecodeStr(PAnsiString(cWholeStr)).s1);
  Memo1.Lines.Add(PDecodeStr(PAnsiString(cWholeStr)).s2);
  Memo1.Lines.Add(PDecodeStr(PAnsiString(cWholeStr)).s3);
  Memo1.Lines.Add(PDecodeStr(PAnsiString(cWholeStr)).s4);
  Memo1.Lines.Add(PDecodeStr(PAnsiString(cWholeStr)).s5);
end;
  

Есть идеи о том, как решить эту проблему?

Заранее спасибо.

Ответ №1:

На самом деле вы не можете избежать создания дополнительных строк. Ваш пример в конце вашего вопроса создает строки.

 Memo1.Lines.Add(PDecodeStr(PAnsiString(cWholeStr)).s1)
  

Ваш вызов TStrings.Add() в этом коде создает динамическую строку неявно из передаваемого вами параметра, а затем эта строка передается в Add() .

Решение с Copy , вероятно, является правильным, поскольку я не вижу простого способа избежать копирования памяти, если вы хотите что-либо сделать с разделенными строками.

Комментарии:

1. вы хотите сказать мне, что PDecodeStr(PAnsiString(cWholeStr)).s1 фактически создается копия исходной строки? Не является ли это «представлением» этой строки, принудительно ограниченной Array[0..x] of AnsiChar конструкцией?

2. @Bozzy Не совсем. Если бы вы работали, скажем, с PDecodeStr переменной и записывали PDecodeStr(..)[1] := '?' , то копирования бы не было. Но я уверен, что вы захотите передать свои PDecodeStr функции для сравнения, добавления в списки и т.д. И это приведет к копированию.

3. Bozzy: используйте «debug windows» -> CPU и убедитесь сами.

Ответ №2:

Я думаю, что в Delphi нет более эффективного способа, чем использовать Copy.

Но другим решением является загрузка всех строк непосредственно во временную таблицу с одним столбцом и, после, создание разлитой с помощью SQL-запроса. Общее время зависит от множества параметров, поэтому лучший способ — протестировать !!

Комментарии:

1. Это могло бы быть хорошим решением для импорта данных общего назначения из текста, но в моей конкретной ситуации мне также приходится учитывать некоторые соображения, которые было бы слишком сложно или почти невозможно реализовать с помощью чистого SQL. Однако спасибо за альтернативное решение.

2. Хорошо, но вы можете сохранить эти соображения (фильтры ?) для кода и передать «разделение» только серверу базы данных.