I try to convert text in utf8 to windows1251.
Since you are using Delphi 2009+, the easiest solution is to use the UT8String
and AnsiString(N)
types and let the RTL handle the conversion for you (unless you are compiling for mobile platforms, in which case UTF8String
and AnsiString(N)
are not available without installing a third party compiler patch):
type
Win1251String = type AnsiString(1251);
var
Utf8: UTF8String;
Win1251: Win1251String;
begin
// populate UTF-8 as needed...
Utf8 := ...;
// converts from UTF-8 to UTF-16 to Windows-1251...
Win1251 := Win1251String(Utf8);
// use Win1251 as needed...
end;
Alternatively, you can use the RTL’s UnicodeFromLocaleChars()
and LocaleCharsFromUnicode()
functions:
var
Utf8: UTF8String;
Utf16: UnicodeString;
Win1251: RawByteString;
begin
// populate UTF-8 as needed...
Utf8 := ...;
// convert from UTF-8 to UTF-16...
SetLength(Utf16, UnicodeFromLocaleChars(65001, 0, PAnsiChar(UTf8), Length(Utf8), nil, 0));
UnicodeFromLocaleChars(65001, 0, PAnsiChar(Utf8), Length(Utf8), PWideChar(Utf16), Length(Utf16));
// convert from UTF-16 to Windows-1251...
SetLength(Win1251, LocaleCharsFromUnicode(1251, 0, PWideChar(Utf16), Length(Utf16), nil, 0, nil, nil));
LocaleCharsFromUnicode(1251, 0, PWideChar(Utf16), Length(Utf16), PAnsiChar(Win1251), Length(Win1251), nil, nil);
SetCodePage(Win1251, 1251);
// use Win1251 as needed...
end;
Or you can use the Win32 MultiByteToWideChar()
and WideCharToMultiByte()
functions directly (or a third-party Unicode library, such as iconv or ICU):
var
Utf8: UTF8String;
Utf16: UnicodeString; // or WideString in pre-2009 versions
Win1251: AnsiString;
begin
// populate UTF-8 as needed...
Utf8 := ...;
// convert from UTF-8 to UTF-16...
SetLength(Utf16, MultiByteToWideChar(65001, 0, PAnsiChar(UTf8), Length(Utf8), nil, 0));
MultiByteToWideChar(65001, 0, PAnsiChar(Utf8), Length(Utf8), PWideChar(Utf16), Length(Utf16));
// convert from UTF-16 to Windows-1251...
SetLength(Win1251, WideCharToMultiByte(1251, 0, PWideChar(Utf16), Length(Utf16), nil, 0, nil, nil));
WideCharToMultiByte(1251, 0, PWideChar(Utf16), Length(Utf16), PAnsiChar(Win1251), Length(Win1251), nil, nil);
// use Win1251 as needed...
end;
This is source text Ñàíêò-Ïåòåðáóðã This is targer Санкт-Петербург
Ñàíêò-Ïåòåðáóðã
is not the UTF-8 encoded form of Санкт-Петербург
, the correct UTF-8 encoded form would be Санкт-Петербург
instead. So, as other people have pointed out, your data is not actually encoded in UTF-8 to begin with.
I tested a lot of functions: Utf8ToAnsi, UTF8ToString, Utf8Encode and other but didn’t get true result.
Utf8ToAnsi()
does not allow you to specify the target charset. In Delphi 2009+, it decodes a UTF-8 string to a UTF-16 UnicodeString
. In pre-2009 versions, it decodes to an AnsiString
that is encoded using the OS default Ansi codepage, whatever that happens to be.
UTF8ToString()
decodes a UTF-8 string to a UTF-16 UnicodeString
.
Utf8Encode()
encodes a UTF-16 WideString
/UnicodeString
to UTF-8.
уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
Сколько постов написано в блогах, сколько вопросов задано на форумах о кодировках и ещё большее количество подобных вопросов осталось не отвеченными или ответом было что-то наподобие «Поиском пользовались?» или «Сто раз обсуждалось!!!». Честно говоря, никогда не понимал таких «ответчиков», ИМХО не хочешь отвечать — лучше жуй и молчи, ответят те, кто захочет.
Понятное дело, что обучать иногда приходится не только с помощью пряника, но и с помощью кнута, но, раз уж такие вопросы всё время всплывают, следовательно они остаются актуальными.
Сегодня я попробую рассказать Вам всё, что мне известно о работе с кодировками в тексте. Вполне возможно, что эта статья поможет Вам при разработке своих проектов в Delphi да и у меня уже пару раз возникало желание немного систематизировать ту информацию. которая накопилась за время существования блога.
1. Delphi и Unicode
Если говорить о работе с Unicode в Delphi, то начать стоит с того, что полноценная поддержка unicode в Delphi стала возможна лишь после выхода Delphi 2009. Такое событие не могло пройти незамеченным, так как благодаря поддержке Unicode и, соответственно, для облегчения работы с кодировками текста в Delphi были реализованы новые возможности, методы, типы данных, о которых написано большое количество статей. На мой взгляд одной из лучших публикаций на эту темя является цикл из трех статей «Delphi и кодировка Unicode» где достаточно чётко и доступно рассказано о нововведениях Delphi 2009 для работы с unicod’ом. Думаю, что нет смысла подробно останавливаться на всех новшествах при наличии ссылки на целых три статьи на эту тему. Остается только упомянуть о том, с чем мы сегодня будем работать для представления веб-страницы в нормальном читаемом виде.
Для первого способа работы с кодировкой мы воспользуемся:
- Класс TEncoding, который и дает нам возможность без лишних хлопот работать с кодировками
- Тип данных TBytes — массива байтов строк
- RawByteString — тип для передачи строковых данных для любой кодовой страницы без каких-либо преобразований последней
В одной из статей блога рассматривалась работа с MLang и сегодня, в качестве второго способа, я продемонстрирую Вам пример её применения при работе с кодировками.
Ну и в качестве третьего способа работы с кодировками, воспользуемся «штатными» методами модуля system. Все три варианта работы с кодировками приведут к одному и тому же результату — текст веб-страницы будет читаемым, без «кракозябров» и вопросительных знаков. Какой способ лучше — решать только Вам.
2. Подготовка исходных данных для работы
Прежде, чем начать что-то кодировать и перекодировать, необходимо это «что-то» получить. В нашем случае — текст веб-страницы. Чтобы не залезать слишком глубоко в вопросы, касающиеся новых типов данных будем сохранять все данные из Сети не в строки или TStringList’ы, как мы к этому привыкли, а воспользуемся типом TBytes.
Используя Synapse исходник любой страницы можно получить, например так:
with THTTPSend.Create do begin if HTTPMethod('GET',URL)then begin //в свойстве Document объекта THTTPSend содержится исходник страницы //обрабатываем Document end; end;
Теперь, получив данные (свойство Document:TMemoryStream) скопируем эти данные в массив байтов строки TBytes, например так (способ достаточно медленный, приведен исключительно для примера):
var B: TBytes; i:integer; begin //загружаем контент страницы with THTTPSend.Create do begin if HTTPMethod('GET',URL) then begin Document.Position:=0; SetLength(B,Document.Size); for i := 0 to Document.Size - 1 do Document.Read(B[i],1); end; end;
Первый этап подготовки можно считать законченным — у нас есть массив байтов строки, которые необходимо представить в виде текста в правильной кодировке. А как узнать, что наш массив TBytes надо перевести в новую «правильную» кодировку? Естественно узнать в какой кодировке была исходная страница. Здесь можно пойти двумя путями:
1. Узнать кодировку из мета-тегов. Обычно кодировка страницы описывается мета-тегом следующего содержания:
META http-equiv="content-type" content="text/html; charset=windows-1251
. Пример того как узнать кодировку из мета-тегов страницы Вы можете посмотреть в модуле HtmlCPConvert, который я выкладывал в блоге, когда рассматривал работу с MLang.
2. Узнать кодировку текста из заголовков сервера. Если рассматривать этот пример, используя для работу с HTTP-протоколом библиотеку Synapse, то заголовки сервера будут содержаться в свойстве Headers:TStringList. Найти элемент, содержащий сведения о кодировке и получить имя кодовой страницы можно, например так:
function GetCharset(Headers: TStringList): string; var i:integer; begin if Headers.Count=0 then Exit; for I := 0 to Headers.Count - 1 do begin //Content-Type: text/html; charset=UTF-8 if Pos('content-type',LowerCase(Headers[i]))>0 then if pos('=',Headers[i])>0 then Result:=LowerCase(Copy(Headers[i],pos('=',Headers[i])+1,Length(Headers[i])-pos('=',Headers[i]))) else Result:=DefCharset; end; end;
Так как в заголовке может отсутствовать сведения о кодировке, то переменная DefCharset должна содержать имя какой-либо дефолтной кодовой страницы, например windows-1251.
Теперь у нас есть все исходные данные:
1. Массив байтов строки B:TBytes;
2. Сведения о кодировке.
Приступим к преобразованию байтов в читабельный текст.
3. Массив байтов — в нормальный текст
3.1. Работа с TEncoding
Разработчики Delphi предусмотрели использование двух взаимопротивоположных метода:
- function BytesOf(const Val: RawByteString): TBytes; — получение байтов из строки
- function StringOf(const Bytes: TBytes): UnicodeString; — получение строки из массива байтов
Пусть Вас не пугает то, что StringOf возвращает UnicodeString, т.к., начиная с Delphi 2009
type UnicodeString = string
Теперь, что касается непосредственно работы с TEncoding. Во-первых, от нас не требуется создавать отдельный экземпляр класса, наподобие такого:
var Enc: TEncoding; begin Enc:=TEncoding.Create; end;
Во-вторых, с помощью TEncoding мы можем менять кодовую страницу не преобразовывая массив байтов в строку. Для этого класс содержит следующие классовые методы:
class function Convert(Source, Destination: TEncoding; Bytes: TBytes): TBytes; overload; class function Convert(Source, Destination: TEncoding; Bytes: TBytes; StartIndex, Count: Integer): TBytes; overload;
Одним из этих методов мы и воспользуемся. Для того, чтобы продемонстрировать работу с TEncoding попробуем получить текст прямо из этого блога. Кодировка UTF-8, именно она наиболее часто является «проблемной» и возвращает в TMemo или ещё куда-либо «кракозябры».
Создадим простое приложение как показано на рисунке:
В ComboBox сразу занесем все варианты работы с текстом:
- Без преобразования
- TEncoding (модуль SysUtils)
- MLang
- Utf8ToAnsi (модуль System)
По нажатию кнопки TButton будем грузить страницу в массив байтов и, зависимости от выбранного варианта работы с текстом, преобразовывать массив в строку и выводить в Memo. Для первого варианта (без преобразования) код может быть следующий:
with THTTPSend.Create do begin if HTTPMethod('GET',Edit1.Text)then begin Document.Position:=0; SetLength(B,Document.Size); for i := 0 to Document.Size - 1 do Document.Read(B[i],1); label4.Caption:=GetCharset(Headers); end; end; Label6.Caption:=IntToStr(Length(B))+' байт текста'; case ComboBox1.ItemIndex of 0:Memo1.Text:=StringOf(B); [...] end;
Запускаем приложение и видим:
Как и ожидалось — вместо русских букв кракозябры. Преобразуем их с помощью классового метода TEncoding.Convert:
var astr: String; B: TBytes; [...] case ComboBox1.ItemIndex of 0:Memo1.Text:=StringOf(B); 1:begin B:=TEncoding.Convert(TEncoding.UTF8,TEncoding.GetEncoding(1251),B); astr:=StringOf(B); Memo1.Lines.Add(astr); end; [...] end;
Здесь следует отметить, что TEncoding.GetEncoding(1251) возвращает кодовую страницу кириллического текста. Если необходимо получить другую кодовую страницу, то можете либо воспользоваться классовыми свойствами TEncoding либо определить кодовую страницу как я, используя данные с MSDN о доступных в Windows кодовых страницах.
Проверим, что получилось в итоге. Грузим ту же самую страницу:
Как видите — кракозябры преобразовались в нормальный русский текст.
Какие плюсы дает нам использование TEncoding? Думаю, что ответ вполне очевиден — у нас появляется под рукой инструмент, позволяющий перекодировать строки из любых кодировок в любые, т.е. фактически возможности работы с текстом ограничиваются количеством доступных в Windows кодовых страниц, а их по данным MSDN 152 штуки. Прикиньте сколько вариантов получится, если в Convert используется пара кодовых страниц. Много :).
Но, наряду с таким большим и жирным плюсом существует и минус — TEncoding есть только в версиях Delphi 2009 и выше, что исключает его использование в более ранних версиях. Как быть?
Если хотите получить не менее впечатляющие возможности работы с кодировками — используйте возможности MLang. Вот как раз и пример его использования.
3.2. Использование возможностей MLang для работы с кодовыми страницами
Позволю себе ещё раз напомнить, что скачать всё необходимое для реализации возможностей MLang Вы можете с этой страницы или, перейдя по этой ссылке. В архиве содержатся всего два модуля: MLang.pas и HtmlCPConvert.pas. Подключаем оба модуля к проекту и для получения читабельного текста пишем:
Memo1.Text:=RawHTMLToHTML(StringOf(B));
RawHTMLToHTML из модуля HtmlCPConvert переводит текст в кодировку windows-1251. Также есть и обратный метод для перевода текста в RawByteString и называется этот метод HTMLToRawHTML.
Результат преобразования текста с помощью MLang абсолютно идентичен предыдущему варианту:
Если Вам необходимо работать со множеством кодировок, как при использовании TEncoding, то придётся самостоятельно немного доработать модуль HtmlCPConvert. В целом возможности TEncoding и MLang вполне сопоставимы.
И, наконец, третий вариант — использование методов модуля System.pas.
3.3. Использование методов System.pas для перекодирования текста
В модуле System.pas можно найти следующие полезные функции для работы с кодировками:
Utf8Encode(const US: UnicodeString): RawByteString; function UTF8EncodeToShortString(const WS: WideString): ShortString; function Utf8Decode(const S: RawByteString): WideString; function UTF8ToUnicodeString(const S: RawByteString): UnicodeString; function UTF8ToString(const S: RawByteString): string; function AnsiToUtf8(const S: string): RawByteString; function Utf8ToAnsi(const S: RawByteString): string;
Как видите, методы завязаны на работе с конкретными кодовыми страницами UTF-8, ANSI, а также на работе с новым типом данных RawByteString. Поэтому возможности третьего способы по сравнению с первыми двумя резко ограничены. Хотя в реалиях нынешнего Рунета их вполне достаточно, т.к. веб-страницы в кодировке koi8-r уже больше раритет, чем обыденность. Использовать методы достаточно просто. Например так:
var RS: RawByteString; [...] begin RS:=StringOf(B); Memo1.Text:=Utf8ToAnsi(RS); end;
я перевел массив байтов в стоку RawByteString и затем представил её в кодировке Ansi, получив в Memo читабельный русский текст.
Вот три способа работы с различными кодировками текста, которые могут применяться где угодно. И это только часть всех возможностей, которые предоставляет нам Delphi. Какой способ использовать — решать Вам.
Кстати, при работе с кодировками следует обратить внимание на то, что «кракозябры» в тексте — это следствие представления строки с использованием неправильной кодовый страницы. Если же вместо русских букв появляются знаки вопроса, то это следствие того, что в текущей кодовой странице текста нет тех символов, которые вы пробуете показать. Например, представляя текст ANSI в кодировке UTF-8 мы получаем кракозябры, а если попробовать представить текст ANSI Cyrillic (windows-1251) с кодовой страницей ANSI Latin 1 (windows-1252), то вместо русских букв получим знаки вопроса, т.к. в windows-1252 отсутствуют русские буквы и т.д.
уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
← →
Zhachuk ©
(2005-06-08 11:14)
[0]
Имеется проблема: необходимо перекодировать строку в кодировке UTF-8 в Windows-1251 средствами Delphi-4. В Delphi-6 есть функция Utf8ToAnsi, которая и делает все дело, однако в Delphi-4 ее нет. В результате долгих поисков в Интернете нарыл бесплатную библиотеку DIConverters.pas (http://www.zeitungsjunge.de/delphi/converters/index.htm), которая вроде как позволяет конвертировать из UTF-8 в Unicode. Однако умения воспользоваться этой библиотекой не хватает
Если у кого есть мысли, как выполнить перекодировку UTF-8 —> Unicode —> Windows-1251, please, чиркните примерчик.
← →
dmitry501 ©
(2005-06-08 11:32)
[1]
Zhachuk © (08.06.05 11:14)
Вот, взял из Audio Tools Library (Freeware) http://jfaul.de/atl
UTF-ANSIfunction ConvertFromUTF8(const Source: string): string;
var
Iterator, SourceLength, FChar, NChar: Integer;
begin
{ Convert UTF-8 string to ANSI string }
Result := "";
Iterator := 0;
SourceLength := Length(Source);
while Iterator < SourceLength do
begin
Inc(Iterator);
FChar := Ord(Source[Iterator]);
if FChar >= $80 then
begin
Inc(Iterator);
if Iterator > SourceLength then break;
FChar := FChar and $3F;
if (FChar and $20) <> 0 then
begin
FChar := FChar and $1F;
NChar := Ord(Source[Iterator]);
if (NChar and $C0) <> $80 then break;
FChar := (FChar shl 6) or (NChar and $3F);
Inc(Iterator);
if Iterator > SourceLength then break;
end;
NChar := Ord(Source[Iterator]);
if (NChar and $C0) <> $80 then break;
Result := Result + WideChar((FChar shl 6) or (NChar and $3F));
end
else
Result := Result + WideChar(FChar);
end;
end;
UTF-UNICODE
function DecodeUTF8(const Source: string): WideString;
var
Index, SourceLength, FChar, NChar: Cardinal;
begin
{ Convert UTF-8 to unicode }
Result := "";
Index := 0;
SourceLength := Length(Source);
while Index < SourceLength do
begin
Inc(Index);
FChar := Ord(Source[Index]);
if FChar >= $80 then
begin
Inc(Index);
if Index > SourceLength then exit;
FChar := FChar and $3F;
if (FChar and $20) <> 0 then
begin
FChar := FChar and $1F;
NChar := Ord(Source[Index]);
if (NChar and $C0) <> $80 then exit;
FChar := (FChar shl 6) or (NChar and $3F);
Inc(Index);
if Index > SourceLength then exit;
end;
NChar := Ord(Source[Index]);
if (NChar and $C0) <> $80 then exit;
Result := Result + WideChar((FChar shl 6) or (NChar and $3F));
end
else
Result := Result + WideChar(FChar);
end;
end;
function EncodeUTF8(const Source: WideString): string;
var
Index, SourceLength, CChar: Cardinal;
begin
{ Convert unicode to UTF-8 }
Result := "";
Index := 0;
SourceLength := Length(Source);
while Index < SourceLength do
begin
Inc(Index);
CChar := Cardinal(Source[Index]);
if CChar <= $7F then
Result := Result + Source[Index]
else if CChar > $7FF then
begin
Result := Result + Char($E0 or (CChar shr 12));
Result := Result + Char($80 or ((CChar shr 6) and $3F));
Result := Result + Char($80 or (CChar and $3F));
end
else
begin
Result := Result + Char($C0 or (CChar shr 6));
Result := Result + Char($80 or (CChar and $3F));
end;
end;
end;
Пост
Hello
Столкнулся с проблемой в Delphi, перекодировкой текста с UTF-8 в Windows-1251, в Delphi нет ICONV, как в PHP, для конвертации текста без проблем, вот нашел Юнит, который решает эту проблему с помощью библиотеки libiconv2.dll
unit iconv;
interface
uses
Windows,Classes,SysUtils;
procedure ConvertFiles(const InFileName,OutFileName:string;const FromCP,ToCP:string);
function iconv2(s, FromCP, ToCP: string): string;
implementation
const
iconv_lib='libiconv2.dll';
type
size_t=Cardinal;
iconv_t=Pointer;
argptr=iconv_t;
function errno:PInteger; cdecl;external'msvcrt.dll' Name'_errno';
function iconv_open(tocode:PChar;fromcode:PChar):iconv_t; cdecl;external iconv_lib Name'libiconv_open';
function iconv_convert(cd:iconv_t;var inbuf:Pointer;var inbytesleft:size_t;var outbuf:Pointer;var outbytesleft:size_t):size_t; cdecl;external iconv_lib Name'libiconv';
function iconv_close(cd:iconv_t):Integer; cdecl;external iconv_lib Name'libiconv_close';
function iconvctl(cd:iconv_t;request:Integer;argument:argptr):Integer; cdecl;external iconv_lib Name'libiconvctl';
const
E2BIG=7;
EINVAL=22;
EILSEQ=84;
ICONV_TRIVIALP=0;//int *argument
ICONV_GET_TRANSLITERATE=1;//int *argument
ICONV_SET_TRANSLITERATE=2;//const int *argument
ICONV_GET_DISCARD_ILSEQ=3;//Int*argument
ICONV_SET_DISCARD_ILSEQ=4;//const Int*argument
procedure ConvertStreams(cd:iconv_t;InStream,OutStream:TStream);
const
BufferSize=4096;
var
inbuf:array[0..BufferSize*2-1] of Char;
insize,inbufsize,inbufrest:DWORD;
inptr:Pointer;
function Convert:Boolean;
var
outbuf:array[0..BufferSize-1] of Char;
outptr:Pointer;
outsize:DWORD;
res:Integer;
begin
Result:=True;
outptr:[email protected];
outsize:=SizeOf(outbuf);
res:=iconv_convert(cd,inptr,insize,outptr,outsize);
if outptr<>@outbuf then
OutStream.WriteBuffer(outbuf,PChar(outptr)[email protected]);
if res=-1 then begin
case errno^ of
EILSEQ:raise Exception.Create('cannot convert');
EINVAL:begin
if (inbufsize=0)or(insize>BufferSize) then
raise Exception.Create('incomplete character or shift sequence')
else begin
inbufrest:=insize;
Move(inptr^,inbuf[BufferSize-insize],insize);
Result:=False;
end;
end;
E2BIG:;
else
raise Exception.Create('unknown error')
end;
end;
end;
begin
inbufrest:=0;
while True do begin
inbufsize:=InStream.Read(inbuf[BufferSize],BufferSize);
if inbufsize=0 then
if inbufrest=0 then begin
inptr:=nil;
Convert;
Exit;
end
else
raise Exception.Create('incomplete character or shift sequence')
else begin
inptr:[email protected][BufferSize-inbufrest];
insize:=inbufrest+inbufsize;
inbufrest:=0;
while (insize>0)and Convert do
;
end;
end;
end;
function iconv2(s, FromCP, ToCP: string): string;
var
cd:iconv_t;
Buf:Integer;
InStream,OutStream:TMemoryStream;
begin
Result := s;
cd:=iconv_open(PChar(ToCP),PChar(FromCP));
if cd=iconv_t(-1) then
raise Exception.Create('internal error');
try
Buf:=1;
iconvctl(cd,ICONV_SET_DISCARD_ILSEQ,@Buf);
iconv_convert(cd,Pointer(nil^),size_t(nil^),Pointer(nil^),size_t(nil^));
InStream:=TMemoryStream.Create;
try
OutStream:=TMemoryStream.Create;
try
InStream.Write(s[1], length(s));
InStream.Seek(0, soFromBeginning);
ConvertStreams(cd,InStream,OutStream);
SetLength(s, OutStream.Size);
OutStream.Seek(0, soFromBeginning);
OutStream.Read(s[1], OutStream.Size);
Result := s;
finally
OutStream.Free;
end;
finally
InStream.Free;
end;
finally
iconv_close(cd);
end;
end;
procedure ConvertFiles(const InFileName,OutFileName:string;const FromCP,ToCP:string);
var
cd:iconv_t;
Buf:Integer;
InStream,OutStream:TFileStream;
begin
cd:=iconv_open(PChar(ToCP),PChar(FromCP));
if cd=iconv_t(-1) then
raise Exception.Create('internal error');
try
Buf:=1;
iconvctl(cd,ICONV_SET_DISCARD_ILSEQ,@Buf);
iconv_convert(cd,Pointer(nil^),size_t(nil^),Pointer(nil^),size_t(nil^));
InStream:=TFileStream.Create(InFileName,fmOpenRead);
try
OutStream:=TFileStream.Create(OutFileName,fmCreate);
try
ConvertStreams(cd,InStream,OutStream);
finally
OutStream.Free;
end;
finally
InStream.Free;
end;
finally
iconv_close(cd);
end;
end;
end.
Здравствуйте!
Как преобразовать строку из UTF-8 в WIN-1251?
И может кто знает ссылочки про эти форматы,
желательно на русском языке.
3 ответа
10
02 июня 2005 года
Freeman
3.2K / / 06.03.2004
Цитата:
Originally posted by senser
Как преобразовать строку из UTF-8 в WIN-1251?
И может кто знает ссылочки про эти форматы,
желательно на русском языке.
Тебе через систему или как? Например, в Дельфи, начиная с шестой версии, есть функции UTF8Encode/Decode. Если через систему, смотри полное описание функции WideCharToMultiByte и другие из этой группы. Ну, а если по каким-то причинам вручную, ищи алгоритмы сам и геморрой тоже самостоятельно имей.
7.0K
02 июня 2005 года
senser
24 / / 22.10.2004
Цитата:
Originally posted by Freeman
Тебе через систему или как? Например, в Дельфи, начиная с шестой версии, есть функции UTF8Encode/Decode. Если через систему, смотри полное описание функции WideCharToMultiByte и другие из этой группы. Ну, а если по каким-то причинам вручную, ищи алгоритмы сам и геморрой тоже самостоятельно имей.
Благодарствую!
P.S. А геморроя мне сейчас и так хватает.
12K
08 июня 2005 года
zhachuk
1 / / 08.06.2005
Существует бесплатная библиотека DIConverters.pas (http://www.zeitungsjunge.de/delphi/converters/index.htm).
Работает под все версии Дельфи, начиная со 2ой. Позволяет конвертировать многие кодировки, в том числе и UTF-8 в Unicode. Однако как ей пользоваться, никак не пойму.
Если кто знает и умеет, напишите, плз, пример, как конвертировать UTF-8—>Unicode—>Windows-1251 (ANSI) используя Delphi 4.
Премного благодарен
Руслан
2009 г.
Delphi и кодировка Unicode
Часть II: Новые функции библиотеки RTL и классы для поддержки кодировки Unicode
Nick Hodges, Embarcadero
Перевод: Виктор Роднев, www.interface.ru
Оригинал: Delphi in a Unicode World Part II: New RTL Features and Classes to Support Unicode
В данной статье рассматриваются новые функции библиотеки Tiburon Runtime Library, которые помогают обрабатывать строки Unicode.
Наверх
Введение
В Части I было показано, какие выгоды дает разработчикам на Delphi поддержка кодировки Unicode, позволяя работать со всеми наборами символов в кодировке Unicode. Были рассмотрены основные особенности типа строки UnicodeString и было показано, как его можно использовать в Delphi.
В части II будет рассказано о некоторых новых функциях библиотеки Delphi Runtime Library, предназначенных для поддержки Unicode, и общих методах обработки строк.
Класс TCharacter
Библиотека Tiburon RTL включает новый класс TCharacter, который описывается в модуле Character. Это закрытый класс, который полностью состоит из статичных функций класса. Разработчикам не следует создавать экземпляры класса TCharacter, предпочтительнее просто вызывать его статические методы класса напрямую. Функции этого класса позволяют, в числе прочего, выполнять:
- преобразование символов в верхний или нижний регистр;
- определение типа данного символа, то есть является ли символ буквой, цифрой, знаком препинания и так далее.
Классом TCharacter используются стандарты, установленные организацией Unicode consortium.
Разработчики могут использовать класс TCharacter для выполнения многих действий, выполнявшихся до этого с наборами символов. Например, следующий программный код:
uses Character; begin if MyChar in ["a"..."z", "A"..."Z"] then begin ... end; end; можно легко заменить кодом: uses Character; begin if TCharacter.IsLetter(MyChar) then begin ... end; end;
Модуль Character содержит также ряд автономных функций, которые выполняют функции каждой из функций класса TCharacter, так что если предпочтительнее простой вызов функции, приведенный выше программный код можно переписать как:
uses Character; begin if IsLetter(MyChar) then begin ... end; end;
Таким образом, класс TCharacter можно использовать для выполнения многих действий или проверки символов, которая может понадобиться.
Кроме того, класс TCharacter содержит методы класса, позволяющие определить, является ли символ верхним или нижним суррогатом суррогатной пары.
Класс TEncoding
Библиотека Tiburon RTL также включает новый класс TEncoding. Его назначение — определить конкретный тип кодировки символов, чтобы можно было сообщить библиотеке VCL, какой тип кодировки необходимо использовать в конкретных ситуациях.
Например, пусть есть экземпляр TStringList, содержащий текст, который необходимо записать в файл. Ранее необходимо было бы написать
begin ... MyStringList.SaveToFile("SomeFilename.txt"); ... end;
и файл записался бы, по умолчанию, в кодировке ANSI. Данный код по-прежнему хорошо работает — файл запишется им в кодировке ANSI, как и раньше, но теперь, когда в Delphi поддерживаются строковые данные в кодировке Unicode, разработчикам может потребоваться записать строковые данные в какой-либо конкретной кодировке. Поэтому оператор SaveToFile (а также LoadFromFile) теперь имеет дополнительный второй параметр, которым определяется используемая кодировка:
begin ... MyStringList.SaveToFile("SomeFilename.txt", TEncoding.Unicode); ... end;
При выполнении приведенного выше кода файл будет записан как текстовый файл в кодировке Unicode (UTF-16).
Классом TEncoding будет также преобразовываться заданный набор байтов из одной кодировки в другую, извлекаться информация о байтах и/или символах в заданной строке или массиве символов, преобразовываться любая строка в массив array of byte (TBytes) и выполняться другие функции, которые могут потребоваться для конкретной кодировки заданной строки или заданного массива символов.
Класс TEncoding включает следующие свойства класса, дающие доступ к экземпляру TEncoding заданной кодировки:
class property ASCII: TEncoding read GetASCII; class property BigEndianUnicode: TEncoding read GetBigEndianUnicode; class property Default: TEncoding read GetDefault; class property Unicode: TEncoding read GetUnicode; class property UTF7: TEncoding read GetUTF7; class property UTF8: TEncoding read GetUTF8;
Свойство Default ссылается на активную кодовую страницу ANSI. Свойство Unicode ссылается на UTF-16.
Класс TEncoding также включает функцию
class function TEncoding.GetEncoding(CodePage: Integer): TEncoding;
которая будет возвращать экземпляр TEncoding, соответствующий кодовой странице, переданной в параметре.
Кроме того, он включает следующую функцию:
function GetPreamble: TBytes;
которая будет возвращать правильный маркер порядка байтов для заданной кодировки.
Класс TEncoding также представляет собой интерфейс, совместимый с классом .Net Encoding.
TStringBuilder
Библиотека RTL теперь включает класс TStringBuilder. Его назначение ясно из его названия — это класс, предназначенный для создания строк. Класс TStringBuilder содержит большое количество перегружаемых функций для добавления, замены и вставки содержимого в заданную строку. Этот класс упрощает создание единых строк из множества различных типов данных. Каждая из функций Append, Insert и Replace возвращает экземпляр класса TStringBuilder, поэтому их можно легко объединять для создания единой строки.
Например, можно использовать класс TStringBuilder вместо усложненного оператора Format. Например, можно написать следующий программный код:
procedure TForm86.Button2Click(Sender: TObject); var MyStringBuilder: TStringBuilder; Price: double; begin MyStringBuilder := TStringBuilder.Create(''); try Price := 1.49; Label1.Caption := MyStringBuilder.Append('The apples are $').Append(Price). ÄAppend(' a pound.').ToString; finally MyStringBuilder.Free; end; end;
Класс TStringBuilder также представляет собой интерфейс, совместимый с классом .Net StringBuilder.
Объявление новых типов строк
Компилятор Tiburon позволяет объявить собственный тип строки, связанный с заданной кодовой страницей. Доступно любое число кодовых страниц. Например, если необходим тип строки, соответствующий кодировке ANSI-кириллице, можно объявить:
type // Кодовая страница для ANSI-кириллицы - 1251 CyrillicString = type Ansistring(1251);
И новый тип строки будет соответствовать кодовой странице кириллицы.
Дополнительная поддержка Unicodeбиблиотекой RTL
В библиотеку RTL добавлен ряд подпрограмм, которыми поддерживается использование строк Unicode.
StringElementSize
Подпрограммой StringElementSize возвращается типичный размер элемента (элемента кода) в заданной строке. Рассмотрим следующий код:
procedure TForm88.Button3Click(Sender: TObject); var A: AnsiString; U: UnicodeString; begin A := 'This is an AnsiString'; Memo1.Lines.Add('The ElementSize for an AnsiString is: ' + IntToStr(StringElementSize(A))); U := 'This is a UnicodeString'; Memo1.Lines.Add('The ElementSize for an UnicodeString is: ' + IntToStr(StringElementSize(U))); end;
Результатом выполнения приведенного выше кода будет:
The ElementSize for an AnsiString is: 1 The ElementSize for an UnicodeString is: 2
StringCodePage
Подпрограммой StringCodePage будет возвращаться значение Word, которое соответствует кодовой странице для заданной строки.
Рассмотрим следующий код:
procedure TForm88.Button2Click(Sender: TObject); type // Кодовая страница для ANSI-кириллицы - 1251 CyrillicString = type AnsiString(1251); var A: AnsiString; U: UnicodeString; U8: UTF8String; C: CyrillicString; begin A := 'This is an AnsiString'; Memo1.Lines.Add('AnsiString Codepage: ' + IntToStr(StringCodePage(A))); U := 'This is a UnicodeString'; Memo1.Lines.Add('UnicodeString Codepage: ' + IntToStr(StringCodePage(U))); U8 := 'This is a UTF8string'; Memo1.Lines.Add('UTF8string Codepage: ' + IntToStr(StringCodePage(U8))); C := 'This is a CyrillicString'; Memo1.Lines.Add('CyrillicString Codepage: ' + IntToStr(StringCodePage(C))); end;
Результатом выполнения приведенного выше кода будет:
The Codepage for an AnsiString is: 1252 The Codepage for an UnicodeString is: 1200 The Codepage for an UTF8string is: 65001 The Codepage for an CyrillicString is: 1251
Другие функции библиотеки RTL, связанные с кодировкой Unicode
Есть ряд других подпрограмм для преобразования строк из одной кодовой страницы в другую. В их числе:
- UnicodeStringToUCS4String
- UCS4StringToUnicodeString
- UnicodeToUtf8
- Utf8ToUnicode
Кроме того, в библиотеке RTL также появился тип RawByteString, который представляет собой тип строки, не связанный ни с какой кодировкой:
RawByteString = type AnsiString($FFFF);
Тип RawByteString позволяет передавать строковые данные для любой кодовой страницы без каких-либо преобразований последней. Он особенно полезен для подпрограмм, для которых кодировка не имеет значения, например для побайтового поиска в строке. Обычно это означает, что параметры подпрограмм, которыми обрабатываются строки без связи с их кодовыми страницами, должны иметь тип RawByteString. Тип RawByteString следует присваивать переменным как можно реже, если вообще когда-либо, так как это может привести к непредсказуемому поведению и возможной потере данных.
Как правило, присвоения типов строк совместимы друг с другом.
Например, присвоение
MyUnicodeString := MyAnsiString;
будет выполнено, как и ожидалось — содержимое строки типа AnsiString будет помещено в строку типа UnicodeString. Как правило, можно присваивать один тип строки строке другого типа, и компилятор выполнит необходимые преобразования, если возможно.
Однако некоторые преобразования могут привести к потере данных, и необходимо остерегаться при перемещении из строки одного типа, которая включает данные в кодировке Unicode, в другую, тип которой не поддерживает Unicode. Например, можно присваивать тип UnicodeString строке типа AnsiString, но если строка типа UnicodeString содержит символы, которые не отображаются в активной в данный момент кодовой странице ANSI, то эти символы будут потеряны при преобразовании. Рассмотрим следующий код:
procedure TForm88.Button4Click(Sender: TObject); var U: UnicodeString; A: AnsiString; begin U := 'This is a UnicodeString'; A := U; Memo1.Lines.Add(A); U := 'Добро пожаловать в мир Юникода с использованием Дельфи 2009!!'; A := U; Memo1.Lines.Add(A); end;
Результат выполнения этого кода, если текущая кодовая страница ОС — 1252, будет следующим:
This is a UnicodeString ????? ?????????? ? ??? ??????? ? ?????????????? ?????? 2009!!
Как можно видеть, при назначении типа UnicodeString строке типа AnsiString информация теряется, так как символы кириллицы не отображаются кодовой страницей Windows-1252. Причина такого результата — строка UnicodeString содержала символы, не представимые в кодовой странице AnsiString, эти символы были потеряны и заменены вопросительными знаками при назначении типа UnicodeString строке типа AnsiString.
SetCodePage
Функция SetCodePage, объявленная в модуле System.pas как
procedure SetCodePage(var S: AnsiString; CodePage: Word; Convert: Boolean);
представляет собой новую функцию RTL, которой задается новая кодовая страница для строки типа AnsiString. Необязательным параметром Convert определяется, следует ли преобразовать сами данные строки в заданную кодовую страницу. Если значение параметра Convert — False, то для строки просто будет изменена кодовая страница. Если значение параметра Convert — True, то данные передаваемой строки будут преобразованы в заданную кодовую страницу.
Функция SetCodePage должна использоваться редко и с большой осторожностью. Обратите внимание, что, если кодовая страница не соответствует существующим данным строки (то есть значение параметра Convert — False), то результаты могут оказаться непредсказуемыми. Кроме того, если существующие данные строки преобразованы, а в новой кодовой странице не представлен заданный исходный символ, данные могут потеряться.
Получение массива байтов TBytesиз строк
В составе библиотеки RTL есть также набор перегружаемых подпрограмм для извлечения из строки массива байтов. Как будет показано в части III, рекомендуется использовать в качестве буфера данных массив TBytes, а не строку. Библиотека RTL упрощает это за счет перегружаемых версий функции BytesOf(), принимающей в качестве параметра разные типы строк.
Заключение
Библиотека Tiburon’s Runtime Library теперь полностью поддерживает новый тип строки UnicodeString. В нее входят новые классы и подпрограммы для обработки и преобразования строк в кодировке Unicode, для управления кодовыми страницами и для упрощения перехода с более ранних версий.
В Части III будут рассмотрены специальные кодовые конструкции, которые необходимы, чтобы выяснить: готов ли тот или иной программный код к переходу на кодировку Unicode.
Наверх
|
|
|
Пожалуйста, выделяйте текст программы тегом [сode=pas] … [/сode]. Для этого используйте кнопку [code=pas] в форме ответа или комбобокс, если нужно вставить код на языке, отличном от Дельфи/Паскаля.
Следующие вопросы задаются очень часто, подробно разобраны в FAQ и, поэтому, будут безжалостно удаляться:
1. Преобразовать переменную типа String в тип PChar (PAnsiChar)
2. Как «свернуть» программу в трей.
3. Как «скрыться» от Ctrl + Alt + Del (заблокировать их и т.п.)
4. Как прочитать список файлов, поддиректорий в директории?
5. Как запустить программу/файл?
… (продолжение следует) …
Вопросы, подробно описанные во встроенной справочной системе Delphi, не несут полезной тематической нагрузки, поэтому будут удаляться.
Запрещается создавать темы с просьбой выполнить какую-то работу за автора темы. Форум является средством общения и общего поиска решения. Вашу работу за Вас никто выполнять не будет.
Внимание
Попытки открытия обсуждений реализации вредоносного ПО, включая различные интерпретации спам-ботов, наказывается предупреждением на 30 дней.
Повторная попытка — 60 дней. Последующие попытки бан.
Мат в разделе — бан на три месяца…
Кодировка строки: UTF-8,ANSI,Windows-1251
, мистика полная 2 дня борюсь
- Подписаться на тему
- Сообщить другу
- Скачать/распечатать тему
|
|
Member Рейтинг (т): 1 |
Добрый день! Кто-то может мистику развеять? Буду благодарен очень. У меня Delphi 2010. Значит строки в unicode. загружаю google.ru в string (idHttp.Get), вывожу строку в showmessage — русские символы отображаются. В заголовке Гугла вообще пишется charset=UTF-8, а когда сохранил страницу — уже Windows-1251. В string русские символы отображаются нормально, если на сайтах кодировки Windows 1251,UTF-8. Сохраняю текстовые файлы в Total Commander с кодировками Windows-1251 и UTF-8, загружаю на сайт, получаю (idHttp.Get) обратно в string — русские символы НЕ отображаются. Прочитал статью СТАТЬЯ, по словам автора нужно с UTF-8 на Windows-1251 менять? Тогда почему сайты с UTF-8 и Windows-1251 нормально уже отображаются. Yandex — UTF-8 и отображается нормально в string. Сохраняю дерево TreeView и загружаю обратно, все ок. Беру этот же файл, загружаю на сервер — русские символы НЕ отображаются. Мне только одно помогает: сохранить в блокноте в ANSI, загрузить на сервер и сделать это:
function W1252To1251(const AData: String): String; var S: RawByteString; X: Integer; begin SetLength(S, Length(AData)); for X := 1 to Length(AData) do S[X] := AnsiChar(AData[X]); SetCodePage(S, 1251, False); Result := S; end; В браузере: если сохранил в 1251 и в браузере то же стоит 1251 — все отображается, UTF-8 так же. То есть сервер ничего не подменяет. Экспериментировал с иным хостингом — так же ерунда. Я уже запутался, ничего понять не могу, прошу Вашей помоши. |
ViktorXP |
|
1) Цитата maxefficiently @ 30.05.11, 04:26 — русские символы НЕ отображаются. Что в место их? знаки вопросов или корокозяблы? 2) какая кодировка в заголовке от обеих серверов? (имеется ввиду не заголовок в html теле) Добавлено 30.05.11, 05:23 |
CodeMonkey |
|
Посмотрите: Парсинг — проблема с кодировками
uses HtmlCPConvert; procedure TForm1.Button1Click(Sender: TObject); var Str: TMemoryStream; RawContent: RawByteString; Content: String; begin Str := TMemoryStream.Create; try IdHTTP1.Get(Edit1.Text, Str); SetLength(RawContent, Str.Size); Move(Str.Memory^, Pointer(RawContent)^, Str.Size); finally FreeAndNil(Str); end; Content := RawHTMLToHTML(RawContent); Memo1.Lines.Text := Content; end; Если на сервере кодировка указана верно, то это будет работать с любой кодировкой. Модули можно взять тут. Цитата maxefficiently @ 30.05.11, 04:26 В заголовке Гугла вообще пишется charset=UTF-8, а когда сохранил страницу — уже Windows-1251 Как сохраняешь-то? Если через SaveToFile у TStrings, то там по-умолчанию ANSI — что и есть Windows-1251. Цитата maxefficiently @ 30.05.11, 04:26 Прочитал статью СТАТЬЯ, по словам автора нужно с UTF-8 на Windows-1251 менять? Не нужно. Зачем? |
Anatoly Podgoretsky |
|
Сохраняй UTF-8, а то все равно получишь крякозябры. Добавлено 30.05.11, 05:39 |
maxefficiently |
|
Member Рейтинг (т): 1 |
Indy 10.5.5 Сохранил в разных кодировках: Windows 1251 Использовал: HTTP Analyzer ничего особенного не показал. Добавлено 30.05.11, 06:04 CodeMonkey, сохраняю дерево TreeView и загружаю обратно, все ок. Беру этот же файл, загружаю на сервер, качаю с сервера в стринг, — русские символы НЕ отображаются. За статью: зачем автор тогда из UTF-8 в 1251 конвертирует, типа надо так, проблемы возникают. Сообщение отредактировано: maxefficiently — 30.05.11, 14:14 |
Anatoly Podgoretsky |
|
Все три страницы показываются нормально. С BOM лучше всего. |
maxefficiently |
|
Member Рейтинг (т): 1 |
Anatoly Podgoretsky, это вы говорите, что в браузере показываются? Там да! Если в Delphi, какой код используете? Я просто: s:=form1.idhttp1.Get(‘http://xxx.com/UT8-BOM.v’); Добавлено 30.05.11, 06:12 |
Fr0sT |
|
У TStringList есть параметр Encoding в методе SaveToFile, если, конечно, сохранение идет через него. А по дефолту метод сохраняет в UTF-16. |
maxefficiently |
|
Member Рейтинг (т): 1 |
Мне так надо было потом: Добавлено 30.05.11, 06:26 Добавлено 30.05.11, 06:32 Добавлено 30.05.11, 06:39 Сообщение отредактировано: maxefficiently — 30.05.11, 06:39 |
CodeMonkey |
|
Я для кого ссылку давал? Там показано, как получить страницу в виде «как есть». Если делать:
s:=idhttp1.Get(‘http://www.google.com.ua/’); То здесь Indy нахимичит с кодировками, как указано здесь: http://www.delphikingdom.ru/asp/answer.asp?IDAnswer=68134 |
maxefficiently |
|
Member Рейтинг (т): 1 |
CodeMonkey, так вывод, что Indy так хитро работает? Почему она то нормально работает, то хитрит… |
CodeMonkey |
|
Я вам говорю, что вы с Indy неправильно работаете. |
maxefficiently |
|
Member Рейтинг (т): 1 |
CodeMonkey, понял-понял, спасибо большое, я там все читал, только не применял, думал проще есть способ и я туплю. Вам +1 Добавлено 30.05.11, 07:38 Сообщение отредактировано: maxefficiently — 30.05.11, 07:39 |
ViktorXP |
|
подобный прикол с String + Utf8 + Indy тоже наблюдал. но лечится банально загрузкой данный напрямую в TStringList минуя переменную String. и без доп. перекодировки
/// Strings: TStringList; idhttp1.Get(‘http://xxx.com/UT8-BOM.v’, Strings); |
maxefficiently |
|
Member Рейтинг (т): 1 |
так что сохраняйте полученное idHTTP в TMemoryStream и все будет ОК. Добавлено 30.05.11, 08:15 |
0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
0 пользователей:
- Предыдущая тема
- Delphi: Общие вопросы
- Следующая тема
[ Script execution time: 0,0385 ] [ 16 queries used ] [ Generated: 4.02.23, 12:07 GMT ]