I have been hounded this problem for a few days, I have two cliendatasets with data in them and I want to convert the olevariant data to string using two functions I found here in Stack Overflow.
The purpose of conversion to string is to be able to transfer the string to another location and convert it back again to olevariant and assign it to another clientdataset.
To simulate it, I created a sample app with the following partial code(see block below).
The code executes properly but my problem is when I convert the windows locale to japanese(which is the requirement), I encounter a datapacket mismatch in the data assignment on the second dataset. but if I do this in the japanese locale:
clientdataset2.data := clientdataset1.data
it works fine. English locale, the code works just fine.
Is there a problem in the string conversion? or is there anything I can do? I really would appreciate help with this.
//to simulate the conversion
TempData := ClientDataSet1.Data;
TempString := OleVariantToString(ClientDataset1.Data);
TempData2 := StringToOleVariant(TempString);
ClientDataSet2.Data := TempData2; //mismatch in data packet happens here in japanese locale
//conversion functions
function TForm1.OleVariantToString(const Value: OleVariant): string;
var
ss: TStringStream;
Size: integer;
Data: PByteArray;
begin
Result := '';
if Length(Value) = 0 then
Exit;
ss := TStringStream.Create;
try
Size := VarArrayHighBound(Value, 1) - VarArrayLowBound(Value, 1) + 1;
Data := VarArrayLock(Value);
try
ss.Position := 0;
ss.WriteBuffer(Data^, Size);
ss.Position := 0;
Result := ss.DataString;
finally
VarArrayUnlock(Value);
end;
finally
ss.Free;
end;
end;
function TForm1.StringToOleVariant(const Value: string): OleVariant;
var
ss: TStringStream;
MyBuffer: Pointer;
begin
Result := null;
if Value = '' then
Exit;
ss := TStringStream.Create(Value);
try
Result := VarArrayCreate([0, ss.Size - 1], varByte);
MyBuffer := VarArrayLock(Result);
try
ss.Position := 0;
ss.ReadBuffer(MyBuffer^, ss.Size);
finally
VarArrayUnlock(Result);
end;
finally
ss.Free;
end;
end;
Streaming to string is already implemented, you can use
Writing: TClientDataSet.SaveToFile or TClientDataSet.SaveToStream
Reading: TClientDataSet.LoadFromFile or TClientDataSet.LoadFromStream
procedure SaveToStream(Stream: TStream; Format: TDataPacketFormat = dfBinary);
procedure SaveToFile(const FileName: string = ''; Format: TDataPacketFormat = fBinary);
procedure LoadFromStream(Stream: TStream);
procedure LoadFromFile(const FileName: string = '');
the TDataPacketFormat options are:
dfBinary: Information is encoded in binary format.
dfXML:Information is encoded in XML, with extended characters encoded using an escape sequence.
dfXMLUTF8:Information is encoded in XML, with extended characters represented using UTF8.
Using dfXMLUTF8 you should have no problems with non/ansi characters sets.
Related
I have an issue with TStringStream encoding on different OS region languages.
I am trying to transfer image data. I was having a problem with it before, and it was solved in another question.
But now I have an encoding issue on TStringStream itself. If the Operating System region language is set to English, I get corrupted data from TStringStream.
When I change the language to Arabic, the data comes out correctly.
I currently use Indy to encode the binary data, but before starting the encoding, the data from StringStream.DataString is already corrupted if the region encoding is not set to Arabic. I tried to add TEncoding.UTF8 to TStringStream.Create(), but the data still comes out incorrectly from StringStream.DataString.
function Encode64(const S: string; const ByteEncoding: IIdTextEncoding = nil): string;
begin
Result := TIdEncoderMIME.EncodeString(S, ByteEncoding);
end;
function Decode64(const S: string; const ByteEncoding: IIdTextEncoding = nil): string;
begin
Result := TIdDecoderMIME.DecodeString(S, ByteEncoding);
end;
StringStream := TStringStream.Create('');
try
Jpg.Performance := jpBestSpeed;
Jpg.ProgressiveEncoding := True;
Jpg.ProgressiveDisplay := True;
jpg.Assign(Image2.Picture.Bitmap);
jpg.CompressionQuality := 25;
jpg.Compress;
jpg.SaveToStream(StringStream);
StringImageData := StringStream.DataString; // the data here is corrupted when Os region is not set to arabic
strcams := '<[S:' + IntToStr(Length(StringImageData)) + 'B]>' + StringImageData;
if length(strcams) < byt then begin
Sendimgdata('IMGDATA123', Encode64(strcams, IndyTextEncoding_UTF8) + sep);
end;
...
You cannot save a JPG to a TStringStream to begin with. You need to encode the binary data without converting it to a string first, which will corrupt the data. Use a TMemoryStream instead for the binary data:
MemoryStream := TMemoryStream.Create;
...
jpg.SaveToStream(MemoryStream);
MemoryStream.Position := 0;
StringImageData := TIdEncoderMIME.EncodeStream(MemoryStream);
...
If you want to encode after you insert the text with length, you have to work with TMemoryStream too:
procedure StringToStream(aStream: TStream; const aString: AnsiString);
begin
aStream.Write(PAnsiChar(AString)^, Length(AString));
end;
...
JpegStream := TMemoryStream.Create;
jpg.SaveToStream(JpegStream);
CompleteStream := TMemoryStream.Create;
StringToStream(CompleteStream, '<[S:' + IntToStr(JpegStream.Size)+'B]>');
CompleteStream.CopyFrom(JpegStream, 0);
StringImageData := TIdEncoderMIME.EncodeStream(CompleteStream);
I wanna over delphi change hex adress 15 character,
I follow like this a way but I didnt get success,
BlockRead(F,arrChar,1); //read all to the buf
CloseFile(F); //close file
IMEI:=Form1.Edit1.Text; //get the number
Form1.Memo1.Lines.Add('new IMEI is'+IMEI); //output
for i:=524288 to 524288+15 do /
arrChar[i]:=IMEI[i-524287];
Do this with a file stream.
var
Stream: TFileStream;
....
Stream := TFileStream.Create(FileName, fmOpenWrite);
try
Stream.Position := $080000;
Stream.WriteBuffer(IMEI, SizeOf(IMEI));
finally
Stream.Free;
end;
I'm assuming that IMEI is an fixed length array of bytes of length 15 but your code attempts to write 16 bytes so it would appear that you are suffering from a degree of confusion.
In your code, your variable IMEI is a string. Which is not an array of bytes. Please don't make that classic mistake of regarding a string as an array of bytes.
You might declare an IMEI type like this:
type
TIMEI = array [0..14] of Byte;
Then you might write a function to populate such a variable from text:
function TextToIMEI(const Text: string): TIMEI;
var
ResultIndex, TextIndex: Integer;
C: Char;
begin
if Length(Text) <> Length(Result) then
raise SomeExceptionClass.Create(...);
TextIndex := low(Text);
for ResultIndex := low(Result) to high(Result) do
begin
C := Result[TextIndex];
if (C < '0') or (C > '9') then
raise SomeExceptionClass.Create(...);
Result[ResultIndex] := ord(C);
inc(TextIndex);
end;
end;
You might then combine this code with that above:
procedure WriteIMEItoFile(const FileName: string; FileOffset: Int64; const IMEI: TIMEI);
var
Stream: TFileStream;
begin
Stream := TFileStream.Create(FileName, fmOpenWrite);
try
Stream.Position := FileOffset;
Stream.WriteBuffer(IMEI, SizeOf(IMEI));
finally
Stream.Free;
end;
end;
Call it like this:
WriteIMEItoFile(FileName, $080000, TextToIMEI(Form1.Edit1.Text));
Although it looks a bit odd that you are explicitly using the Form1 global variable. If that code executes in a method of TForm1 then you should use the implicit Self variable.
I know 🌠is %F0 %9F %8C %A0
but how can I convert this to be usable in Delphi ?
I tried several html encoders , but none give this result
my test
for i := 1 to length(s) do
result:= result+IntToHex(ord(s[i]),2);
but my result is D83CDF20
That is a simple UTF-8 encoding of this character. You can get the Delphi string using TEncoding like this:
var
S: string;
begin
S := TEncoding.UTF8.GetString(TBytes.Create($F0, $9F, $8C, $A0));
end;
or simply
S := '🌠';
In case you want it the other way round:
var
bytes: TBytes;
begin
bytes := TEncoding.UTF8.GetBytes('🌠');
end;
Or:
var
S: UTF8String;
begin
S := UTF8String('🌠');
end;
Valid for Delphi 2009 and later.
This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
Convert Array of ShortInt to String, Delphi
I would like to turn a file into a string, this string should contain the numbers corresponding to the file in bytes.
I did in a way but it was very slow, using an array of FOR and ShortInt ...
Example:
have any file on my computer, my goal would be to transform it into Bytes, Bytes that these should be between -127 .. 128, it would have with something like this:
A[0] = 120
A[1] = -35
A[2] = 40
Ate here OK, but I need it in a concatenated string and a ',' between them, thus:
'120,-35,40'
I did it with a 'FOR', but it was very slow, if you have another alternative.
This question is rather similar to your previous question, but with the added complexity of reading the array from a file.
I'd probably write it like this:
function ConvertFileToCommaDelimitedArray(const FileName: string): string;
var
i, BytesLeft, BytesRead: Integer;
Buffer: array [0..4096-1] of Shortint;
Stream: TFileStream;
sb: TStringBuilder;
begin
sb := TStringBuilder.Create;
try
Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
BytesLeft := Stream.Size;
while BytesLeft>0 do
begin
BytesRead := Min(SizeOf(Buffer), BytesLeft)
Stream.ReadBuffer(Buffer, BytesRead);
dec(BytesLeft, BytesRead);
for i := 0 to BytesRead-1 do
begin
sb.Append(IntToStr(Buffer[i]));
sb.Append(',');
end;
end;
finally
Stream.Free;
end;
if sb.Length>0 then
sb.Length := sb.Length-1;//remove trailing comma
Result := sb.ToString;
finally
sb.Free;
end;
end;
You can load a file into an array of one-byte signed integral types (also known as ShortInt) like this:
type
TShortIntArray = array of TShortInt;
function LoadFileAsShortInt(const name: TFileName): TShortIntArray;
var
f: TFileStream;
begin
f := TFileStream.Create(name, fmOpenRead or fmShareDenyWrite);
try
SetLength(Result, f.Size);
f.ReadBuffer(Result[0], f.Size);
finally
f.Free;
end;
end;
If you want the file's contents as a string, then you should skip the array and load the file directly into a string:
function FileAsString(const name: TFileName): AnsiString;
var
s: TStringStream;
begin
s := TStringStream.Create;
try
s.LoadFromFile(name);
Result := s.DataString;
finally
s.Free;
end;
end;
I am trying to remotely read a binary (REG_BINARY) registry value, but I get nothing but junk back. Any ideas what is wrong with this code? I'm using Delphi 2010:
function GetBinaryRegistryData(ARootKey: HKEY; AKey, AValue, sMachine: string; var sResult: string): boolean;
var
MyReg: TRegistry;
RegDataType: TRegDataType;
DataSize, Len: integer;
sBinData: string;
bResult: Boolean;
begin
bResult := False;
MyReg := TRegistry.Create(KEY_QUERY_VALUE);
try
MyReg.RootKey := ARootKey;
if MyReg.RegistryConnect('\\' + sMachine) then
begin
if MyReg.KeyExists(AKey) then
begin
if MyReg.OpenKeyReadOnly(AKey) then
begin
try
RegDataType := MyReg.GetDataType(AValue);
if RegDataType = rdBinary then
begin
DataSize := MyReg.GetDataSize(AValue);
if DataSize > 0 then
begin
SetLength(sBinData, DataSize);
Len := MyReg.ReadBinaryData(AValue, PChar(sBinData)^, DataSize);
if Len <> DataSize then
raise Exception.Create(SysErrorMessage(ERROR_CANTREAD))
else
begin
sResult := sBinData;
bResult := True;
end;
end;
end;
except
MyReg.CloseKey;
end;
MyReg.CloseKey;
end;
end;
end;
finally
MyReg.Free;
end;
Result := bResult;
end;
And I call it like this:
GetBinaryRegistryData(
HKEY_LOCAL_MACHINE,
'\SOFTWARE\Microsoft\Windows NT\CurrentVersion',
'DigitalProductId', '192.168.100.105',
sProductId
);
WriteLn(sProductId);
The result I receive from the WriteLn on the console is:
ñ ♥ ???????????6Z ????1 ???????☺ ???♦ ??3 ? ??? ?
??
Assuming that you are already connected remotely, try using the GetDataAsString function
to read binary data from the registry.
sResult := MyReg.GetDataAsString(AValue);
You're using Delphi 2010, so all your characters are two bytes wide. When you set the length of your result string, you're allocating twice the amount of space you need. Then you call ReadBinaryData, and it fills half your buffer. There are two bytes of data in each character. Look at each byte separately, and you'll probably find that your data looks less garbage-like.
Don't use strings for storing arbitrary data. Use strings for storing text. To store arbitrary blobs of data, use TBytes, which is an array of bytes.