Замена символов в строке

Вопросы программирования на Free Pascal, использования компилятора и утилит.

Модератор: Модераторы

Замена символов в строке

Сообщение Troublemaker » 29.10.2008 14:58:46

Наступил на грабли, о которых говорил в теме "где мои тексты?"

Код: Выделить всё
s:string
...
    s:=Format('** %2.2D%2.2D %-.30S',[ComData.MyType,ComData.ID,ComData.Desc]);
    if ComData.MyType=etTheory then begin
      s[1]:=widechar('Т');
      s:=trim(s)+' ('+ExtractFileName(Attachment)+')';
      end
    else begin
      s[1]:=widechar('Э');
    end;

В таком варианте компилируется без ошибок, но во время работы вместо первой звездочки в строке я вижу вопросительный знак, а не букву "Т" или "Э", на которую должна замениться эта звездочка. Похоже, что [1] берет именно один байт строки, а не один widechar из нее. Как же мне запихать туда именно букву?

Формировать строку без первой звездочки, а потом делать вроде
Код: Выделить всё
s:='Т'+s
неразумно, потому что дальше по тексту у меня заменяется на букву и вторая звездочка.

Попробовал вместо звездочек поставить две русские буквы. Теперь при выводе не видна вся строка, очевидно сбивается последовательность кодировки. Ненавижу коды с последовательностями разной длины!
Аватара пользователя
Troublemaker
постоялец
 
Сообщения: 292
Зарегистрирован: 16.04.2008 13:00:44
Откуда: Биробиджан, Дальний Восток

Re: Замена символов в строке

Сообщение B4rr4cuda » 29.10.2008 15:31:53

http://freepascal.ru/forum/blog.php?u=2921&b=13
Имхо, для работы с юникодом предпочтительней использовать widestring.
Аватара пользователя
B4rr4cuda
энтузиаст
 
Сообщения: 693
Зарегистрирован: 28.12.2007 07:48:35

Re: Замена символов в строке

Сообщение Troublemaker » 29.10.2008 15:44:08

B4rr4cuda писал(а):для работы с юникодом предпочтительней использовать widestring

Заменил s:string на s:widestring. Ничего не изменилось: вместо первой звездочки всё равно изображается вопросительный знак (#63).
Аватара пользователя
Troublemaker
постоялец
 
Сообщения: 292
Зарегистрирован: 16.04.2008 13:00:44
Откуда: Биробиджан, Дальний Восток

Re: Замена символов в строке

Сообщение Mr.Smart » 29.10.2008 15:54:06

В первых s[1] по типу соответсвует Char, а не как не WideChar.
Во вторых в UTF-8 символы с кириллицей (а 'Э' есть символ кириллици) занимают по 2 байта.
В третьих если хочешь работать как привык переведи все строковые переменные в AnsiString (UTF8ToAnsi), а после обработки верни обратно в UTF-8 (AnsiToUTF8)
Mr.Smart
долгожитель
 
Сообщения: 1796
Зарегистрирован: 29.03.2008 01:01:11
Откуда: из леса!

Re: Замена символов в строке

Сообщение Sergei I. Gorelkin » 29.10.2008 16:09:37

Если работать с WideString, то, во-первых, Format стоит заменить на WideFormat, а во-вторых, нужно указывать компилятору кодировку, например, {$codepage utf8} в начале юнита, и редактировать исходники в той же кодировке.

А вообще я бы заменил звездочки на '%s%s' и добавил соотв. строки в аргументы ф-ции Format.
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1406
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Re: Замена символов в строке

Сообщение Troublemaker » 29.10.2008 16:34:55

Mr.Smart писал(а):В первых...

А как насчет поконкретнее?
Общая формулировка задачи: надо один символ в произвольном месте строки заменить на другой. Причем исходный символ может быть как двухбайтовым, так и однобайтовым.
Как это правильно сделать?
Sergei I. Gorelkin писал(а):я бы заменил звездочки на '%s%s' и добавил соотв. строки в аргументы ф-ции Format

Все исходники - в UTF8. Звездочки - просто плейсхолдеры, не несущие никакой смысловой нагрузки.

Короче говоря, кто-нибудь может показать образец кода, делающий то, что у меня сделать не получается? Надо произвольный символ в произвольном месте стандартного [wide]string-a заменить на другой символ, независимо от того, сколько байт в UTF8 занимают заменяемый и заменяющий символ, причем длина их UTF8-представления может быть разной.

Добавлено спустя 27 минут 4 секунды:
Пока выкрутился так:
Код: Выделить всё
s:='Т'+RightStr(s,Length(s)-1);
сработало и для widestring и для string, но почему-то такой способ мне кажется извращением, неужто нет более простого и универсального? Или придется писать свою функцию типа ReplaceChar(s:string, c:char, num:integer):string ?
Аватара пользователя
Troublemaker
постоялец
 
Сообщения: 292
Зарегистрирован: 16.04.2008 13:00:44
Откуда: Биробиджан, Дальний Восток

Re: Замена символов в строке

Сообщение Attid » 29.10.2008 17:07:09

Sergei I. Gorelkin писал(а):А вообще я бы заменил звездочки на '%s%s' и добавил соотв. строки в аргументы ф-ции Format.

+1


Troublemaker писал(а):образец кода

Код: Выделить всё
function ReplaceStr(const S, Srch, Replace: string): string;
var { где, что , чем }
  i: Integer;
  Source: string;
begin
  Source := S;
  Result := '';
  repeat
    i := Pos(UpperCase(Srch), UpperCase(Source));
    if i > 0 then
    begin
      Result := Result + Copy(Source, 1, i - 1) + Replace;
      Source := Copy(Source, i + Length(Srch), MaxInt);
    end
    else
      Result := Result + Source;
  until i <= 0;
end;
begin
  ShowMessage(ReplaceStr('123456789','5','яя'));
end;

оно ? вроде коректно работает, если нет, заменить Length на UTF8Length и т.д.
Аватара пользователя
Attid
долгожитель
 
Сообщения: 2586
Зарегистрирован: 27.10.2006 17:29:15
Откуда: 44°32′23.63″N 41°2′25.2″E

Re: Замена символов в строке

Сообщение Troublemaker » 29.10.2008 17:14:08

Attid писал(а):оно ? вроде коректно работает,

Не оно. Оно будет корректно работать при условии, что надо заменить все вхождения заранее известного символа. А мне надо заменить любой символ, находящийся в заранее известной позиции, что в ansistring-e в обычной кодировке делается просто заменой значения нужного элемента array of char, каковым эта строка является.

Пока я выкрутился (см. мое предыдущее сообщение), и, судя по всему, напишу-таки ReplaceCharByNumber(), но неужели нельзя обойтись меньшей кровью?
Аватара пользователя
Troublemaker
постоялец
 
Сообщения: 292
Зарегистрирован: 16.04.2008 13:00:44
Откуда: Биробиджан, Дальний Восток

Re: Замена символов в строке

Сообщение Attid » 29.10.2008 17:28:15

Troublemaker писал(а):неужели нельзя

незя
Аватара пользователя
Attid
долгожитель
 
Сообщения: 2586
Зарегистрирован: 27.10.2006 17:29:15
Откуда: 44°32′23.63″N 41°2′25.2″E

Re: Замена символов в строке

Сообщение Mr.Smart » 29.10.2008 18:32:24

Troublemaker писал(а):неужели нельзя

почитай http://ru.wikipedia.org/wiki/UTF-8 будет интерестно. :D
Mr.Smart
долгожитель
 
Сообщения: 1796
Зарегистрирован: 29.03.2008 01:01:11
Откуда: из леса!

Re: Замена символов в строке

Сообщение *vmr » 29.10.2008 18:45:27

А при присвоении вайдчару символа, он разве не переконверчивается автоматом из utf-8 (кодировки сорцов) ?

Т.е. по вашей логике
Код: Выделить всё
var
  ws: widestring;
begin
  ws := 'хубабуба';
  ShowMessage(ws);
end;

даст неправильный результат?
Аватара пользователя
*vmr
постоялец
 
Сообщения: 168
Зарегистрирован: 08.01.2007 01:46:07
Откуда: Киев

Re: Замена символов в строке

Сообщение Mr.Smart » 29.10.2008 18:46:50

*vmr писал(а):даст неправильный результат?

Нет не переводится и результат будет не верным!
Mr.Smart
долгожитель
 
Сообщения: 1796
Зарегистрирован: 29.03.2008 01:01:11
Откуда: из леса!

Re: Замена символов в строке

Сообщение Odyssey » 29.10.2008 18:49:20

Troublemaker писал(а):Надо произвольный символ в произвольном месте стандартного [wide]string-a заменить на другой символ, независимо от того, сколько байт в UTF8 занимают заменяемый и заменяющий символ, причем длина их UTF8-представления может быть разной.

Если нужно компактное решение - можно попробовать Delete/Insert. Если плэйсхолдеры - латинские символы (т.е. находятся в пределах ASCII, т.е. содержат один символ даже в UTF-8), то можно сделать так:
Код: Выделить всё
Delete(s, PlaceholderPos, 1);
Insert(s, Replacement, PlacehoderPos);

Если же нужно быстро работающее решение, то
Troublemaker писал(а):придется писать свою функцию типа ReplaceChar(s:string, c:char, num:integer):string ?


А вообще, если честно, лучше реструктурировать код, и генерировать строку только тогда, когда все ее составляющие известны. А потом соединить эти составляющие либо через конкатенацию, либо через Format, как предложил Sergei I. Gorelkin. Пусть даже ценой дублирования if-блока, это все равно обойдется дешевле замены в строке. Т.е. например:

Код: Выделить всё
s, prefix:string
...
    if ComData.MyType=etTheory then
       prefix := 'T'
    else
       prefix := 'Е';
    s:=Format('** %2.2D%2.2D %-.30S',[ComData.MyType,ComData.ID,ComData.Desc]);
    if ComData.MyType=etTheory then
      s:=trim(s)+' ('+ExtractFileName(Attachment)+')';
    s := prefix + s;
Odyssey
энтузиаст
 
Сообщения: 580
Зарегистрирован: 29.11.2007 17:32:24

Re: Замена символов в строке

Сообщение *vmr » 29.10.2008 18:53:09

Тогда скажите почему это работает в дельфе?
Почему дельфя самостоятельно раздупляется правильно перевести эти одинадцать байтов 1251 в двадцать два байта кодировки USC-2 ?
Почему при ShowMessage(ws) она самостоятельно делает перекодировку обратно(из USC-2 в 1251)?
Аватара пользователя
*vmr
постоялец
 
Сообщения: 168
Зарегистрирован: 08.01.2007 01:46:07
Откуда: Киев

Re: Замена символов в строке

Сообщение Mr.Smart » 29.10.2008 18:57:48

*vmr писал(а):Тогда скажите почему это работает в дельфе?
Почему дельфя самостоятельно раздупляется правильно перевести эти одинадцать байтов 1251 в двадцать два байта кодировки USC-2 ?
Почему при ShowMessage(ws) она самостоятельно делает перекодировку обратно(из USC-2 в 1251)?

Кодировка между USC-2 и 1251 работает также как и в Делфи.
А для UTF-8 используйе функции UTF8Decode\UTF8Encode
Mr.Smart
долгожитель
 
Сообщения: 1796
Зарегистрирован: 29.03.2008 01:01:11
Откуда: из леса!

След.

Вернуться в Free Pascal Compiler

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 5

Рейтинг@Mail.ru