2013-02-22 2 views
1

Я использую следующий подход для перемещения и управления AnsiString. Он работает большую часть времени, но иногда указатель на строку перестает функционировать. Учитывая следующий код:Delphi AnsiString Manipulation - PAnsiChar становится коррумпированным?

var 
    s: AnsiString; 
    p: PAnsiChar; 
    offset, idx, cnt: Integer; 
begin 
    s := 'some>very>long>string>with>field>delimiters>'; 
    p := @s[1]; 
    offset := 1; 

    // find the 5th field 
    cnt := 5; 
    repeat 
    idx := AnsiString.AnsiPos('>', p); 
    Inc(p, idx); 
    Inc(offset, idx); 
    Dec(cnt); 
    until cnt = 0; 

    // insert a new field after the 5th field 
    Insert(AnsiString('something new>'), s, offset); 

    // skip other fields 
    // insert other values 
    // repeat 
end; 

При отладке, только после того, как цикл repeat..until заканчивает вы можете посмотреть на инспектора и посмотреть, что p = 'field>delimiters>'. После заявления Insert(), s = 'some>very>long>string>with>something new>field>delimiters>' и p = 'something new>field>delimiters>' в инспекторе. Это так, как ожидалось.

Моя настоящая строка длиной несколько тысяч символов. Этот метод перемещения по строке и добавление новых полей работает десятки раз, а затем внезапно перестает работать. p больше не показывает вставленное значение в начале строки после вызова Insert(). p, кажется, не знают, что s изменилось ...

Почему p правильно ссылаться на символ на s после самой Insert() заявления, и вдруг перестают работать после нескольких звонков в Insert()?

(Я обнаружил ответ на свой вопрос, набрав его. Ответ кажется очевидным сейчас, но не так, пока я боролся с проблемой. Возможно, вопрос и ответ помогут кому-то другому ...)

+2

Just FYI: В вашем вопросе и вашем ответе вы никогда не инициализируете 'offset', прежде чем использовать его в цикле. Поэтому вы начинаете со случайного содержимого памяти и каждый раз увеличиваете его с помощью значения idx каждый через цикл. –

+0

То, что вы действительно можете использовать здесь, если оно существует, это «AnsiPosEx», функция, которая объединяет MBCS-осведомленность AnsiPos с настраиваемым индексом начала PosEx. Тогда вам вообще не понадобится 'p', и это спасет вас от создания и уничтожения строк длиной в несколько тысяч символов каждый раз, когда вы передаете' p' в качестве параметра, который ожидает, что AnsiString и компилятор выполнит автоматическое преобразование для вы. Если ваш поисковый запрос действительно один символ, тогда рассмотрите вместо этого использование AnsiStrScan. –

+0

Хорошие комментарии. Спасибо, парни. Мой фактический код инициализирует 'offset' - я просто упустил его здесь. Я также посмотрю AnsiPosEx и AnsiStrScan. Благодаря! –

ответ

7

при вызове Insert(), диспетчер памяти перемещает AnsiString на новое место в памяти, если не хватает дополнительной непрерывной памяти, чтобы расширить буфер в его текущем местоположении памяти. Это оставляет p, указывая на старую ячейку памяти, которая не содержит модифицированную строку и, вероятно, приведет к нарушениям доступа.

Добавление одной строки кода для повторной инициализации p после каждого заявления Insert() устраняет проблему.

var 
    s: AnsiString; 
    p: PAnsiChar; 
    offset, idx, cnt: Integer; 
begin 
    s := 'some>very>long>string>with>field>delimiters>'; 
    p := @s[1]; 
    offset := 1; 

    // find the 5th field 
    cnt := 5; 
    repeat 
    idx := AnsiString.AnsiPos('>', p); 
    Inc(p, idx); 
    Inc(offset, idx); 
    Dec(cnt); 
    until cnt = 0; 

    // insert a new field after the 5th field 
    Insert(AnsiString('something new>'), s, offset); 
    p := @s[offset];         // <- this fixes the issue 

    // skip other fields 
    // insert other values 
    // repeat 
end; 
+0

Упс, это тонко! –

Смежные вопросы