I have a line with the following description: '|0200|4|SALGADOS|||KG|00|19051000||||17|'
I want to separate where the pipe to save data in the database.
I'm using the pos function incorrectly. But I am getting the data.
Inside the if then i will insert data into db.
ReadLn(txt, line);
if True then
if (Pos('|0200|', line)) = 1 then
begin
fArq.Add(line);
end;
if (pos('|0000|', line)) = 1 then
begin
fArq.Add(line);
end;
if (pos('|0005|', line)) = 1 then
begin
fArq.Add(line);
end;
if (pos('|C460|', line)) = 1 then
begin
fArq.Add(line);
flagCF := True;
end
else
begin
if flagCF = True then
if (pos('|C490|', line)) = 0 then
fArq.Add(line)
else
flagCF := False;
end
You can also use TStringList:
lStringList := TStringList.Create;
lStringList.delimiter := '|';
lStringList.DelimitedText := '|0200|4|SALGADOS|||KG|00|19051000||||17|';
Now you can access each field using lStringList.Items[ index ]
Note (from comments): if space characters is included in the string, set StrictDelimiter to true to avoid treating them as delimiters.
With ExtractStrings, you can add all values between the |'s to a TStrings-descendant.
Assuming fArq is a TStrings descendent:
ExtractStrings(['|'], [], PChar(line), fArq);
If you use Delphi XE3 and up you can use the Split Class Helper Method.
parts:=line.Split(['|'],TStringSplitOptions.ExcludeEmpty);
I have the following functions that I've been using for quite some time.
There are two variants here, the first one is a one time use type of function and the other is when you want to efficiently process the entire input string from the first element up to the last in order.
I have also included the related functions to count the number of sections.
P.S. These functions are actually 1 based, as written.
P.P.S. I ripped the functions from another unit and haven't checked this unit for complete correctness. YMMV.
The non POS methods are considered one-off functions. ie. You are only looking for a single value in the given input string.
The POS methods take two addition integer variables to store internal index positions for later use. The initial value of the variables should be set to -1 for the first call. After that you should just provide the values to the next iteration of the call that the previous call returned.
For example.
Non-POS use:
const
Str1 = '|0200|4|SALGADOS|||KG|00|19051000||||17|';
.
.
.
begin
showmessage( ParseSection(Str1, 1, '|') ); //returns 0200
showmessage( ParseSection(Str1, 4, '|') ); //returns '' (empty string)
//this will show every element in the string once
Idx1 := -1;
Idx2 := -1;
for loop := 1 to CountSections(Str1, '|') do
showmessage( ParseSectionPos(Str1, loop, '|', Idx1, Idx2) );
//Idx1 and Idx2 are self referenced variables and don't need outside intervention
//These are necessary to obtain the best possible speed
end;
The other variations of the methods allow for quoted values and the quoting character to be user defined.
unit rmControls.Strings.Sections;
interface
uses System.Classes, System.SysUtils;
function CountSections(const ParseLine: string; const ParseSep: char): integer; overload;
function CountSections(const ParseLine: string; const ParseSep: char; const QuotedStrChar: char): integer; overload;
function ParseSection(const ParseLine: string; ParseNum: integer; const ParseSep: char): string; overload;
function ParseSection(const ParseLine: string; ParseNum: integer; const ParseSep: char; const QuotedStrChar: char): string; overload;
function ParseSectionPos(const ParseLine: string; ParseNum: integer; const ParseSep: char; var FromIDX, FromPOS: integer): string; overload;
function ParseSectionPos(const ParseLine: string; ParseNum: integer; const ParseSep: char; const QuotedStrChar: char; var FromIDX, FromPOS: integer): string; overload;
function UpdateSection(const ParseLine, UpdateText: string; ParseNum: integer; const ParseSep: char):string; overload;
function UpdateSection(const ParseLine, UpdateText: string; ParseNum: integer; const ParseSep: char; const QuotedStrChar: char):string; overload;
function UpdateSectionPos(const ParseLine, UpdateText: string; ParseNum: integer; const ParseSep: char; var FromIDX, FromPOS: integer):string; overload;
function UpdateSectionPos(const ParseLine, UpdateText: string; ParseNum: integer; const ParseSep: char; const QuotedStrChar: char; var FromIDX, FromPOS: integer):string; overload;
implementation
uses System.Math, System.Masks, System.Character, System.Variants;
function CountSections(const ParseLine: string; const ParseSep: char; const QuotedStrChar: char): integer; overload;
var
wEnd: PChar;
Loop: integer;
wInQuote: boolean;
begin
wInQuote := false;
wEnd := PChar(ParseLine);
result := 0;
for Loop := 1 to Length(ParseLine) do
begin
if (wEnd^ = QuotedStrChar) then
wInQuote := not wInQuote;
if not wInQuote and (wEnd^ = ParseSep) then
inc(result);
inc(wEnd);
end;
if Length(ParseLine) <> 0 then
inc(result);
end; { CountSections }
function CountSections(const ParseLine: string; const ParseSep: char): integer; overload;
var
wEnd: PChar;
Loop: integer;
begin
wEnd := PChar(ParseLine);
result := 0;
for Loop := 1 to Length(ParseLine) do
begin
if (wEnd^ = ParseSep) then
inc(result);
inc(wEnd);
end;
if Length(ParseLine) <> 0 then
inc(result);
end; { CountSections }
function ParseSection(const ParseLine: string; ParseNum: integer; const ParseSep: char): string; overload;
var
w1, w2: integer;
begin
w1 := -1;
w2 := -1;
result := ParseSectionPos(ParseLine, ParseNum, ParseSep, w1, w2);
end; { ParseSection }
function ParseSection(const ParseLine: string; ParseNum: integer; const ParseSep: char; const QuotedStrChar: char): string; overload;
var
w1, w2: integer;
begin
w1 := -1;
w2 := -1;
result := ParseSectionPos(ParseLine, ParseNum, ParseSep, QuotedStrChar, w1, w2);
end; { ParseSection }
function ParseSectionPos(const ParseLine: string; ParseNum: integer; const ParseSep: char; var FromIDX, FromPOS: integer): string;
var
wStart, wEnd: PChar;
wIndex, Loop: integer;
wLoopIDX: integer;
begin
wIndex := 1;
wLoopIDX := 1;
wEnd := PChar(ParseLine);
if (FromIDX > -1) and (FromIDX < Length(ParseLine)) then
begin
inc(wEnd, FromIDX);
wIndex := FromPOS;
wLoopIDX := FromIDX;
end;
wStart := wEnd;
for Loop := wLoopIDX to Length(ParseLine) do
begin
if (wEnd^ = ParseSep) then
begin
if wIndex = ParseNum then
break
else
begin
inc(wIndex);
inc(wEnd);
wStart := wEnd;
end;
end
else
inc(wEnd);
end;
if wIndex = ParseNum then
begin
SetString(result, wStart, wEnd - wStart);
if result = #0 then
result := '';
FromIDX := wEnd - PChar(ParseLine);
FromPOS := ParseNum;
end
else
result := '';
end;
function ParseSectionPos(const ParseLine: string; ParseNum: integer; const ParseSep: char; const QuotedStrChar: char; var FromIDX, FromPOS: integer): string;
var
wStart, wEnd: PChar;
wIndex, Loop: integer;
wInQuote: boolean;
wLoopIDX: integer;
begin
wInQuote := false;
wIndex := 1;
wLoopIDX := 1;
wEnd := PChar(ParseLine);
if (FromIDX > -1) and (FromIDX < Length(ParseLine)) then
begin
inc(wEnd, FromIDX);
wIndex := FromPOS;
wLoopIDX := FromIDX;
end;
wStart := wEnd;
for Loop := wLoopIDX to Length(ParseLine) do
begin
if (wEnd^ = QuotedStrChar) then
wInQuote := not wInQuote;
if not wInQuote and (wEnd^ = ParseSep) then
begin
if wIndex = ParseNum then
break
else
begin
inc(wIndex);
inc(wEnd);
wStart := wEnd;
end;
end
else
inc(wEnd);
end;
if wIndex = ParseNum then
begin
SetString(result, wStart, wEnd - wStart);
if (Length(result) > 0) and (result[1] = QuotedStrChar) then
result := AnsiDequotedStr(result, QuotedStrChar);
if result = #0 then
result := '';
FromIDX := wEnd - PChar(ParseLine);
FromPOS := ParseNum;
end
else
result := '';
end;
function UpdateSection(const ParseLine, UpdateText: string; ParseNum: integer; const ParseSep: char): string; overload;
var
w1, w2: integer;
begin
w1 := -1;
w2 := -1;
result := UpdateSectionPos(ParseLine, UpdateText, ParseNum, ParseSep, w1, w2);
end;
function UpdateSection(const ParseLine, UpdateText: string; ParseNum: integer; const ParseSep: char; const QuotedStrChar: char): string; overload;
var
w1, w2: integer;
begin
w1 := -1;
w2 := -1;
result := UpdateSectionPos(ParseLine, UpdateText, ParseNum, ParseSep, QuotedStrChar, w1, w2);
end;
function UpdateSectionPos(const ParseLine, UpdateText: string; ParseNum: integer; const ParseSep: char; var FromIDX, FromPOS: integer):string; overload;
var
wStart, wEnd: PChar;
wIndex, Loop: integer;
wLoopIDX: integer;
begin
wIndex := 1;
wLoopIDX := 1;
wEnd := PChar(ParseLine);
if (FromIDX > -1) and (FromIDX < Length(ParseLine)) then
begin
inc(wEnd, FromIDX);
wIndex := FromPOS;
wLoopIDX := FromIDX;
end;
wStart := wEnd;
for Loop := wLoopIDX to Length(ParseLine) do
begin
if (wEnd^ = ParseSep) then
begin
if wIndex = ParseNum then
break
else
begin
inc(wIndex);
inc(wEnd);
wStart := wEnd;
end;
end
else
inc(wEnd);
end;
if wIndex = ParseNum then
begin
SetString(result, PChar(ParseLine), wStart - pChar(ParseLine));
if result = #0 then
result := '';
result := result + updateText + pchar(wEnd);
FromIDX := wEnd - PChar(ParseLine);
FromPOS := ParseNum;
end
else
raise Exception.Create('Index not found');
end;
function UpdateSectionPos(const ParseLine, UpdateText: string; ParseNum: integer; const ParseSep: char; const QuotedStrChar: char; var FromIDX, FromPOS: integer):string; overload;
var
wStart, wEnd: PChar;
wIndex, Loop: integer;
wInQuote: boolean;
wLoopIDX: integer;
begin
wInQuote := false;
wIndex := 1;
wLoopIDX := 1;
wEnd := PChar(ParseLine);
if (FromIDX > -1) and (FromIDX < Length(ParseLine)) then
begin
inc(wEnd, FromIDX);
wIndex := FromPOS;
wLoopIDX := FromIDX;
end;
wStart := wEnd;
for Loop := wLoopIDX to Length(ParseLine) do
begin
if (wEnd^ = QuotedStrChar) then
wInQuote := not wInQuote;
if not wInQuote and (wEnd^ = ParseSep) then
begin
if wIndex = ParseNum then
break
else
begin
inc(wIndex);
inc(wEnd);
wStart := wEnd;
end;
end
else
inc(wEnd);
end;
if wIndex = ParseNum then
begin
SetString(result, PChar(ParseLine), wStart - pChar(ParseLine));
if result = #0 then
result := '';
result := result + AnsiQuotedStr(updateText, QuotedStrChar) + pchar(wEnd);
FromIDX := wEnd - PChar(ParseLine);
FromPOS := ParseNum;
end
else
raise Exception.Create('Index not found');
end;
end.
Here's the function I use. It supports any-length delimiter (for splitting CRLF-separated string, f.i.) and AllowEmpty parameter which determines whether empty elements would be omitted or returned.
function Split(const Str: string; Delim: string; AllowEmpty: Boolean): TStringDynArray;
var CurrDelim, NextDelim, CurrIdx: Integer;
begin
if Str = '' then begin SetLength(Result, 0); Exit; end;
CurrDelim := 1; CurrIdx := 0; SetLength(Result, 16);
repeat
if CurrIdx = Length(Result) then
SetLength(Result, CurrIdx + 16);
NextDelim := PosEx(Delim, Str, CurrDelim);
if NextDelim = 0 then NextDelim := Length(Str)+1; // the end of the string
Result[CurrIdx] := Copy(Str, CurrDelim, NextDelim - CurrDelim);
CurrDelim := NextDelim + Length(Delim);
if (Result[CurrIdx] <> '') or AllowEmpty
then Inc(CurrIdx)
else Continue;
until CurrDelim > Length(Str);
SetLength(Result, CurrIdx); // cut the array to actual length
end;
Related
in two different project i need to use crc16 checksum.one in windows and other in android.i used a code for windows and it worked prefect.
showmessage( bin2crc16(HexToBin('1234')) ); //---> 0EC9
here is used function for winsows
function Pow(i, k: Integer): Integer;
var
j, Count: Integer;
begin
if k>0 then j:=2
else j:=1;
for Count:=1 to k-1 do
j:=j*2;
Result:=j;
end;
function BinToDec(Str: string): Integer;
var
Len, Res, i: Integer;
Error: Boolean;
begin
Error:=False;
Len:=Length(Str);
Res:=0;
for i:=1 to Len do
if (Str[i]='0')or(Str[i]='1') then
Res:=Res+Pow(2, Len-i)*StrToInt(Str[i])
else
begin
//MessageDlg('It is not a binary number', mtInformation, [mbOK], 0);
Error:=True;
Break;
end;
if Error=True then Result:=0
else Result:=Res;
end;
//------------------------------------------------------------------------------
function CRC16CCITT(bytes: array of Byte): Word;
const
polynomial = $1021;
var
crc: Word;
I, J: Integer;
b: Byte;
bit, c15: Boolean;
begin
crc := $FFFF;
for I := 0 to High(bytes) do
begin
b := bytes[I];
for J := 0 to 7 do
begin
bit := (((b shr (7-J)) and 1) = 1);
c15 := (((crc shr 15) and 1) = 1);
crc := crc shl 1;
if ((c15 xor bit) <> false) then crc := crc xor polynomial;
end;
end;
Result := crc and $ffff;
end;
//------------------------------------------------------------------------------
function HexToDec(const Str: string): Integer;
begin
if (Str <> '') and ((Str[1] = '-') or (Str[1] = '+')) then
Result := StrToInt(Str[1] + '$' + Copy(Str, 2, MaxInt))
else
Result := StrToInt('$' + Str);
end;
//------------------------------------------------------------------------------
function bin2crc16(str: string): string;
var
I:integer;
lengthCount : integer;
crcByteArr : array of Byte;
crcOut : Word;
begin
lengthCount := Trunc(length(str)/8);
setlength(crcByteArr , lengthCount );
for I := 0 to lengthCount-1 do
begin
crcByteArr[I] := BinToDec(copy(str, I*8+1, 8));
end;
crcOut := CRC16CCITT(crcByteArr);
result := crcOut.ToHexString;
end;
//------------------------------------------------------------------------------
function HexToBin(Hexadecimal: string): string;
const
BCD: array [0..15] of string =
('0000', '0001', '0010', '0011', '0100', '0101', '0110', '0111',
'1000', '1001', '1010', '1011', '1100', '1101', '1110', '1111');
var
i: integer;
begin
Result := '';
for i := Length(Hexadecimal) downto 1 do
Result := BCD[StrToInt('$' + Hexadecimal[i])] + Result;
end;
but for android i changed the code to handle zero index string.
the result is different
memo2.Lines.Add( bin2crc16(HexToBin('1234')) ); //-----> 1AFa
here is used functions in android
function BinToDec(Str: string): Integer;
var
Len, Res, i: Integer;
Error: Boolean;
begin
Error:=False;
Len:=Length(Str);
Res:=0;
for i:=0 to Len-1 do
if (Str[i]='0')or(Str[i]='1') then
Res:=Res+Pow(2, Len-i)*StrToInt(Str[i])
else
begin
Error:=True;
Break;
end;
if Error=True then Result:=0
else Result:=Res;
end;
//------------------------------------------------------------------------------
function CRC16CCITT(bytes: array of Byte): Word;
const
polynomial = $1021;
var
crc: Word;
I, J: Integer;
b: Byte;
bit, c15: Boolean;
begin
crc := $FFFF;
for I := 0 to High(bytes) do
begin
b := bytes[I];
for J := 0 to 7 do
begin
bit := (((b shr (7-J)) and 1) = 1);
c15 := (((crc shr 15) and 1) = 1);
crc := crc shl 1;
if ((c15 xor bit) <> false) then crc := crc xor polynomial;
end;
end;
Result := crc and $ffff;
end;
//------------------------------------------------------------------------------
function bin2crc16(str: string): string;
var
I:integer;
lengthCount : integer;
crcByteArr : array of Byte;
crcOut : Word;
begin
lengthCount := Trunc(length(str)/8);
setlength(crcByteArr , lengthCount );
for I := 0 to lengthCount-1 do
begin
crcByteArr[I] := BinToDec(copy(str, I*8, 8));
end;
crcOut := CRC16CCITT(crcByteArr);
result := crcOut.ToHexString;
end;
//-----------------------------------------------------------------------------------
function HexToBin(Hexadecimal: string): string;
const
BCD: array [0..15] of string =
('0000', '0001', '0010', '0011', '0100', '0101', '0110', '0111',
'1000', '1001', '1010', '1011', '1100', '1101', '1110', '1111');
var
i: integer;
begin
Result := '';
for i := Length(Hexadecimal)-1 downto 0 do
Result := BCD[StrToInt('$' + Hexadecimal[i])] + Result;
end;
//---------------------------------------------------------------------------------
function Pow(i, k: Integer): Integer;
var
j, Count: Integer;
begin
if k>0 then j:=2
else j:=1;
for Count:=1 to k-1 do
j:=j*2;
Result:=j;
end;
how can i fix my problem !?
You have not adjusted your HexToBin function for zero length strings.
There is also an issue in your BinToDec function. Your power calculation is wrong because the index into the string has changed. Possibly the simplest way to deal with it is as follows, although you could also adjust the index in the POW function
function BinToDec(Str: string): Integer;
var
Len, Res, i: Integer;
Error: Boolean;
begin
Error:=False;
Len:=Length(Str);
Res:=0;
for i:=1 to Len do
if (Str[I - 1]='0')or(Str[I - 1]='1') then
Res:=Res+Pow(2, Len-i)*StrToInt(Str[I - 1])
else
begin
Error:=True;
Break;
end;
if Error=True then Result:=0
else Result:=Res;
end;
The last thing to note is that 'Copy' uses One based indexing even on zero based strings, but you have assumed that it is zero indexed. I agree it is confusing, but there it is.
I want to fetch the entire line string (UTF8) and want to do operation on the line string. I have tried following code. but if we are having multibyte characters am not able to do this.
J:=1;
CurrentRowStr :='';
while True do
begin
//detect end of line
Buffer.EditPosition.Move(Changes[I].FLine,J);
CurrentRowStr := CurrentRowStr + Buffer.EditPosition.Character ;
J := J+1;
end;
CurrentRowStr := Buffer.EditPosition.Read(J-1);
if anyone can help me to get particular line string using OpenToolsAPI, it would be great help.
You can use a IOTAEditReader to get entire lines. The following code is from my Conversion Helper Package. Most of this revolves around the GetCurrentLineParams function:
function GetEditor: IOTASourceEditor;
var
ModuleServices: IOTAModuleServices;
Module: IOTAModule;
I: Integer;
begin
ModuleServices := BorlandIDEServices as IOTAModuleServices;
Module := ModuleServices.CurrentModule;
for I := 0 to Module.GetModuleFileCount - 1 do
if Supports(Module.GetModuleFileEditor(I), IOTASourceEditor, Result) then
Break;
end;
function GetLineAtCharPos(const Editor: IOTASourceEditor;
const EditView: IOTAEditView; CharPos: TOTACharPos): string;
var
EditReader: IOTAEditReader;
Start, Len: Integer;
Res: AnsiString;
begin
CharPos.CharIndex := 0;
Start := EditView.CharPosToPos(CharPos);
Inc(CharPos.Line);
Len := EditView.CharPosToPos(CharPos) - Start;
if Len > 0 then
begin
SetLength(Res, Len);
EditReader := Editor.CreateReader;
EditReader.GetText(Start, PAnsiChar(Res), Len);
Result := string(PAnsiChar(Res));
end;
end;
function GetCurrentLine(const Editor: IOTASourceEditor;
var BufferStart, Index: LongInt): string;
var
BufferLength: LongInt;
EditReader: IOTAEditReader;
Res: AnsiString;
begin
GetCurrentLineParams(Editor, BufferStart, BufferLength, Index);
SetLength(Res, BufferLength);
EditReader := Editor.CreateReader;
EditReader.GetText(BufferStart, PAnsiChar(Res), BufferLength);
Result := string(PAnsiChar(Res)); // just to be sure.
end;
function GetCurrentCharPos(const Editor: IOTASourceEditor; out EditView:
IOTAEditView): TOTACharPos;
var
CursorPos: TOTAEditPos;
begin
EditView := Editor.GetEditView(0);
CursorPos := EditView.CursorPos;
EditView.ConvertPos(True, CursorPos, Result);
end;
procedure GetCurrentLineParams(const Editor: IOTASourceEditor;
var Start, Length, Index: Integer);
var
EditView: IOTAEditView;
CharPos: TOTACharPos;
begin
CharPos := GetCurrentCharPos(Editor, EditView);
Index := CharPos.CharIndex + 1;
CharPos.CharIndex := 0;
Start := EditView.CharPosToPos(CharPos);
Inc(CharPos.Line);
Length := EditView.CharPosToPos(CharPos) - Start;
end;
function GetCurrentLineStart(const Editor: IOTASourceEditor): Integer;
var
L, I: Integer;
begin
GetCurrentLineParams(Editor, Result, L, I);
end;
function GetCurrentLineLength(const Editor: IOTASourceEditor): Integer;
var
S, I: Integer;
begin
GetCurrentLineParams(Editor, S, Result, I);
end;
In Notepad you can Open any File and it will display the raw data inside.
I would like to do this in a TMemo but have struggled to find out how to do this.
I managed to find this code here.
I modified it to a function and changed it slightly for my purposes:
function OpenBinaryFile(var Data; Count: Cardinal): string;
var
Line: string[80];
i: Cardinal;
P: PAnsiChar;
nStr: string[4];
SL: TStringList;
const
posStart = 1;
binStart = 7;
ascStart = 57;
begin
P := #Data;
Line := '';
SL := TStringList.Create;
try
for i := 0 to Count - 1 do
begin
if (i mod 16) = 0 then
begin
if Length(Line) > 0 then
SL.Add(Trim(Line));
FillChar(Line, SizeOf(Line), ' ');
Line[0] := Chr(72);
end;
if P[i] >= ' ' then
Line[i mod 16 + ascStart] := P[i]
else
Line[i mod 16 + ascStart] := '.';
end;
SL.Add(Trim(Line));
Result := SL.Text;
finally
SL.Free;
end;
end;
It works, but it only displays in a fixed amount of characters per line, like this:
What do I need to change so it fills all the memo in the same way Notepad would?
Well, it's the if (i mod 16) = 0 test that is truncating the lines at 16 characters.
I believe that Notepad does the same as this code:
var
i: Integer;
s: AnsiString;
Stream: TFileStream;
begin
Stream := TFileStream.Create(FileName, fmOpenRead);
try
SetLength(s, Stream.Size);
if Stream.Size>0 then
Stream.ReadBuffer(s[1], Stream.Size);
finally
Stream.Free;
end;
for i := 1 to Length(s) do
if s[i]=#0 then
s[i] := ' ';
Memo1.Text := s;
end;
If you want to replace non-printable characters with '.' then you can easily do so by modifying the code above like this:
if s[i]<#32 then
s[i] := '.';
TStrings became TEncoding-aware in D2009. By default, TStrings.LoadFrom...() will use TEncoding.Default unless you tell it otherwise. I would suggest implementing a custom TEncoding derived class that reads/writes raw 8-bit data, eg:
type
TRawEncoding = class(TEncoding)
protected
function GetByteCount(Chars: PChar; CharCount: Integer): Integer; override;
function GetBytes(Chars: PChar; CharCount: Integer; Bytes: PByte; ByteCount: Integer): Integer; override;
function GetCharCount(Bytes: PByte; ByteCount: Integer): Integer; override;
function GetChars(Bytes: PByte; ByteCount: Integer; Chars: PChar; CharCount: Integer): Integer; override;
public
constructor Create;
function GetMaxByteCount(CharCount: Integer): Integer; override;
function GetMaxCharCount(ByteCount: Integer): Integer; override;
function GetPreamble: TBytes; override;
end;
.
constructor TRawEncoding.Create;
begin
FIsSingleByte := True;
FMaxCharSize := 1;
end;
function TRawEncoding.GetByteCount(Chars: PChar; CharCount: Integer): Integer;
begin
Result := CharCount;
end;
function TRawEncoding.GetBytes(Chars: PChar; CharCount: Integer; Bytes: PByte; ByteCount: Integer): Integer;
var
i : Integer;
begin
Result := Math.Min(CharCount, ByteCount);
for i := 1 to Result do begin
// replace illegal characters > $FF
if Word(Chars^) > $00FF then begin
Bytes^ := Byte(Ord('?'));
end else begin
Bytes^ := Byte(Chars^);
end;
//advance to next char
Inc(Chars);
Inc(Bytes);
end;
end;
function TRawEncoding.GetCharCount(Bytes: PByte; ByteCount: Integer): Integer;
begin
Result := ByteCount;
end;
function TRawEncoding.GetChars(Bytes: PByte; ByteCount: Integer; Chars: PChar; CharCount: Integer): Integer;
var
i : Integer;
begin
Result := Math.Min(CharCount, ByteCount);
for i := 1 to Result do begin
Word(Chars^) := Bytes^;
//advance to next char
Inc(Chars);
Inc(Bytes);
end;
end;
function TRawEncoding.GetMaxByteCount(CharCount: Integer): Integer;
begin
Result := CharCount;
end;
function TRawEncoding.GetMaxCharCount(ByteCount: Integer): Integer;
begin
Result := ByteCount;
end;
function TRawEncoding.GetPreamble: TBytes;
begin
SetLength(Result, 0);
end;
Then you can use it like this:
var
Enc: TEncoding;
begin
Enc := TRawEncoding.Create;
try
Memo1.Lines.LoadFromFile('filename', Enc);
finally
Enc.Free;
end;
end;
I have a problem with sending a mail with Indy. The message is a cyrillic and there is a also a file attached to the mail but the when I send the file in the received email there is no file attached. Only some strange symbols. I googled all the information for Indy but nothing wasn't useful.
My question is how to send a message with file attached to it in cyrillic?
Thanks in advance!
Here's my emailer code unit. Hope it helps:
// ****************************************************
// Mass Emailer v1.0
//
// by: Steve Faleiro email: steve_goa#yahoo.com
// date: 14 Apr 2009
//
// Special thanks / dedications to:
// Remy Lebau of Team Indy,
// Nick Hodges of Codegear,
// Andy (Andreas Hausladen),
// & the JEDI JVCL project.
// ****************************************************
unit u_functions;
interface
uses SysUtils, Classes, IDMessageBuilder, Forms, StrUtils,
IDMessage, IDSmtp, IdSSLOpenSSL, IdExplicitTLSClientServerBase,
Windows, StdCtrls, DB, dialogs, ShellAPI;
type
smtpserverdetails = record
Host: string; // 'smtp.gmail.com';
Port: integer; // 465;
needAuthentication: string; // Y or N
secureMode: string; // Y or N
Username: string; // 'xx#gmail.com';
Password: string; // 'pp';
end;
type
TEmailMessageType = (HTMLMessage, PlainTextMessage);
type
emailmessage = record
EmailMessageType: TEmailMessageType;
FromAddress: string;
FromName: string;
ReplyToAddress: string;
ReplyToName: string;
ReceiptRecipientAddress: string;
ReceiptRecipientName: string;
RecipientAddress: string;
MsgSubject: string;
MsgBody: TMemoryStream;
Footer: TMemoryStream;
HTMLImages: TStringList;
Attachmnts: TStringList;
procedure copyTo(var dst: emailmessage);
constructor Create(Sender: TObject);
procedure Destroy;
end;
type
substList = record
findList: TStringList;
replaceList: TStringList;
end;
type
emailSender = class
constructor Create(srvr: smtpserverdetails);
procedure setEmail(emlmessg: emailmessage);
procedure customizeEmail(emlmessg: emailmessage; replaceables: substList);
procedure sendEmail;
destructor Destroy; override;
private
IDSMTP1: TIDSmtp;
IdSSLIOHandlerSocketOpenSSL1: TIdSSLIOHandlerSocketOpenSSL;
IDMessage1: TIDMessage;
FEmlMsg: emailmessage;
public
end;
procedure DeleteFileToRecycleBin(f: String);
procedure delAllFiles(d: string);
procedure getAllFileNames(d: string; out lstfn: TStringList);
function IsNumeric(const s: string): boolean;
function quotedString(s: string; c: Char): string;
procedure populateComboBox(c: TComboBox; ds: TDataSet;
KeyColumnIndex, DisplayColumnIndex: integer); overload;
procedure populateComboBox(c: TComboBox; sl: TStrings); overload;
procedure disposeComboBoxObjects(c: TComboBox);
procedure disposeStringListObjects(c: TStringList);
procedure disposeListBoxObjects(l: TListBox);
procedure populateListBox(l: TListBox; ds: TDataSet; KeyColumnIndex, DisplayColumnIndex: integer);
implementation
procedure DeleteFileToRecycleBin(f: String);
var
FileOpStruc: TSHFileOpStruct;
begin
FillChar(FileOpStruc, SizeOf(FileOpStruc), 0);
with FileOpStruc do begin
Wnd := 0;
wFunc := FO_DELETE;
pFrom := PChar(f + #0);
fFlags := FOF_ALLOWUNDO or FOF_NOCONFIRMATION or FOF_SILENT;
end;
try
SHFileOperation(FileOpStruc);
except
on E: Exception do
showmessage('Error!' + e.Message);
end;
end;
procedure emailmessage.copyTo(var dst: emailmessage);
begin
dst.EmailMessageType := EmailMessageType;
dst.FromName := FromName;
dst.FromAddress := FromAddress;
dst.ReplyToAddress := ReplyToAddress;
dst.ReplyToName := ReplyToName;
dst.ReceiptRecipientAddress := ReceiptRecipientAddress;
dst.ReceiptRecipientName := ReceiptRecipientName;
dst.RecipientAddress := RecipientAddress;
dst.MsgSubject := MsgSubject;
dst.HTMLImages.Assign(HTMLImages);
dst.Attachmnts.Assign(Attachmnts);
MsgBody.Position := 0;
dst.MsgBody.LoadFromStream(MsgBody);
if Assigned(Footer) then begin
Footer.Position := 0;
dst.Footer.LoadFromStream(Footer);
end;
end;
constructor emailmessage.Create(Sender: TObject);
begin
MsgBody := TMemoryStream.Create;
Footer := TMemoryStream.Create;
HTMLImages := TStringList.Create;
Attachmnts := TStringList.Create;
end;
procedure emailmessage.Destroy;
begin
MsgBody.Free;
Footer.Free;
HTMLImages.Free;
Attachmnts.Free;
end;
constructor emailSender.Create(srvr: smtpserverdetails);
begin
IDSMTP1 := TIDSMTP.Create;
IdSSLIOHandlerSocketOpenSSL1 := TIdSSLIOHandlerSocketOpenSSL.Create;
with IDSMTP1 do begin
Host := srvr.Host;
Port := srvr.Port;
if (srvr.needAuthentication = 'Y') then
AuthType := satDefault
else
AuthType := satNone;
IOHandler := IdSSLIOHandlerSocketOpenSSL1;
if (srvr.secureMode = 'Y') then
UseTLS := utUseRequireTLS
else
UseTLS := utNoTLSSupport;
Username := srvr.Username;
Password := srvr.Password;
end;
FEmlMsg.Create(nil);
end;
destructor emailSender.Destroy;
begin
FEmlMsg.Destroy;
IdSSLIOHandlerSocketOpenSSL1.Free;
IDSMTP1.Free;
inherited Destroy;
end;
//Author: Steve Faleiro. June 21, 2008.
procedure emailSender.customizeEmail(emlmessg: emailmessage; replaceables: substList);
var
buffer: pointer;
begin
emlmessg.copyTo(FEmlMsg);
//move to last position and insert Email signature
if Assigned(FEmlMsg.Footer) then //only if footer is populated
begin
getmem(buffer, FEmlMsg.Footer.size);
FEmlMsg.footer.Write(buffer, FEmlMsg.footer.size);
FEmlMsg.MsgBody.seek(0, soFromEnd);
FEmlMsg.MsgBody.Read(buffer, FEmlMsg.footer.size);
end;
// ReplaceData(emlmsg.MsgBody, replaceables);
FEmlMsg.MsgBody.Position := 0;
end;
procedure ReplaceData(Data: TStringList; replaceables: substList);
var
i, d: integer;
s: string;
begin
for i := 0 to Data.Count - 1 do begin
s := Data[i];
for d := 0 to replaceables.FindList.Count - 1 do
s := StringReplace(s, replaceables.FindList[d], replaceables.replaceList[d], [rfReplaceAll]);
Data[i] := s;
end;
end;
//Author: Steve Faleiro. June 21, 2008.
procedure emailSender.sendEmail;
var
idMBHTML: TIdMessageBuilderHTML;
c: integer;
pic, tempPath: string;
enc: TEncoding;
begin
idMBHTML := TIdMessageBuilderHTML.Create;
tempPath := extractfilepath(application.exename) + 'temp';
if not DirectoryExists(tempPath) then begin
if not CreateDir(tempPath) then
exit//showmessage('error');
;
end
else //directory exists
delAllFiles(tempPath);
FEmlMsg.MsgBody.Position := 0;
Idmessage1 := TIDMessage.Create;
with idMBHTML do begin
if (FEmlMsg.EmailMessageType = HTMLMessage) then begin
// enc := nil;
// TEncoding.GetBufferEncoding(FEmlMsg.MsgBody.Memory, enc) ;
enc := TEncoding.Unicode;
HTML.LoadFromStream(FEmlMsg.MsgBody, enc);
// showmessage(Html.Text);
// for c := 0 to FEmlMsg.HTMLImages.Count - 1 do
// HTMLFiles.Add(FEmlMsg.HTMLImages.Strings[c])//
// pic := FEmlMsg.HTMLImages.Strings[c];
// HTML.Text := ReplaceStr(HTML.Text, pic, 'cid:' + pic);
//// showmessage(Html.Text);
end
else
if (FEmlMsg.EmailMessageType = PlainTextMessage) then
PlainText.LoadFromStream(FEmlMsg.MsgBody);
for c := 0 to FEmlMsg.Attachmnts.Count - 1 do
Attachments.Add(FEmlMsg.Attachmnts[c]);
FillMessage(IDMessage1);
end;
with Idmessage1 do begin
Subject := FEmlMsg.MsgSubject;
From.Address := FEmlMsg.FromAddress;
From.Name := FEmlMsg.FromName;
Recipients.EMailAddresses := FEmlMsg.RecipientAddress;
if FEmlMsg.ReceiptRecipientAddress <> '' then
ReceiptRecipient.Address := FEmlMsg.ReceiptRecipientAddress;
if FEmlMsg.ReceiptRecipientName <> '' then
ReceiptRecipient.Name := FEmlMsg.ReceiptRecipientName;
end;
with IDSMTP1 do begin
if not Connected then
Connect;
Send(IdMessage1);
end;
Idmessage1.Free;
idMBHTML.Free;
end;
procedure emailSender.setEmail(emlmessg: emailmessage);
begin
emlmessg.copyTo(FEmlMsg);
end;
function quotedString(s: string; c: Char): string;
begin
Result := c + s + c;
end;
procedure delAllFiles(d: string);
var
fr: TSearchRec;
searchResult: integer;
begin
searchResult := FindFirst(Pansichar(d + '\*.*'), 0, fr);
if (searchResult = 0) then
repeat
DeleteFile(Pchar(d + '\' + fr.Name))
until (FindNext(fr) <> 0);
SysUtils.FindClose(fr);
end;
procedure getAllFileNames(d: string; out lstfn: TStringList);
var
fr: TSearchRec;
searchResult: integer;
begin
searchResult := FindFirst(d + '\*.*', 0, fr);
if (searchResult = 0) then
repeat
lstfn.Add(d + '\' + fr.Name)
until (FindNext(fr) <> 0);
SysUtils.FindClose(fr);
end;
function IsNumeric(const s: string): boolean;
var
v: single;
code: integer;
begin
Val(s, v, code);
Result := code = 0;
end;
function countWords(s: string): integer;
var
l, p, o: integer;
begin
l := Length(s);
if l = 0 then begin
Result := 0;
exit;
end;
o := 1;
for p := 0 to l - 1 do
if s[p] = ' ' then
Inc(o);
Result := o;
end;
function getWord(s: string; n: integer): string;
var
c, p, o: integer;
begin
p := 0;
for c := 0 to n do begin
o := p + 1;
p := PosEx(' ', s, p + 1);
if p = 0 then
p := Length(s) + 1;
end;
s := MidStr(s, o, p - o);
Result := s;
end;
// ---- Populate a Combobox ---------------------------------------------
// ---- for accessing the value, use:
// var
// s : PVariant;
// begin
// s := PVariant( lstServers.Items.Objects[lstServers.ItemIndex] );
// my_key := integer( s ^ ); // <--- cast to your type
// ---------------------------------------------------------------------
procedure populateComboBox(c: TComboBox; ds: TDataSet;
KeyColumnIndex, DisplayColumnIndex: integer);
var
v: variant;
pt: PVariant;
begin
disposeComboBoxObjects(c);
c.Items.Clear;
with ds do begin
First;
while not EOF do begin
v := Fields[KeyColumnIndex].Value;
New(pt);
pt ^ := v;
c.Items.AddObject(Fields[DisplayColumnIndex].AsString, TObject(pt));
Next;
end;
end;
if c.Items.Count > 0 then
c.ItemIndex := 0;
end;
// ---- Populate a Combobox ---------------------------------------------
// ---- for accessing the value, use:
// var
// s : PVariant;
// begin
// s := PVariant( lstServers.Items.Objects[lstServers.ItemIndex] );
// my_key := integer( s ^ ); // <--- cast to your type
// ---------------------------------------------------------------------
procedure populateComboBox(c: TComboBox; sl: TStrings);
var
v: variant;
pt: PVariant;
l: integer;
begin
disposeComboBoxObjects(c);
c.Items.Clear;
for l := 0 to sl.Count - 1 do begin
v := sl.ValueFromIndex[l];
New(pt);
pt ^ := v;
c.Items.AddObject(sl.Names[l], TObject(pt));
end;
if c.Items.Count > 0 then
c.ItemIndex := 0;
end;
// ---- Populate a Listbox ---------------------------------------------
// ---- for accessing the value, use:
// var
// s : PVariant;
// begin
// s := PVariant( lstServers.Items.Objects[lstServers.ItemIndex] );
// my_key := integer( s ^ ); // <--- cast to your type
// ---------------------------------------------------------------------
procedure populateListBox(l: TListBox; ds: TDataSet; KeyColumnIndex, DisplayColumnIndex: integer);
var
v: variant;
pt: PVariant;
begin
disposeListBoxObjects(l);
l.Items.Clear;
with ds do begin
First;
while not EOF do begin
v := Fields[KeyColumnIndex].Value;
New(pt);
pt ^ := v;
l.Items.AddObject(Fields[DisplayColumnIndex].AsString, TObject(pt));
Next;
end;
end;
if l.Items.Count > 0 then
l.ItemIndex := 0;
end;
procedure disposeComboBoxObjects(c: TComboBox);
var
i: integer;
begin
if c.Items.Count > 0 then
for i := 0 to c.Items.Count - 1 do
Dispose(PVariant(c.Items.Objects[i]));
end;
procedure disposeStringListObjects(c: TStringList);
var
i: integer;
begin
if c.Count > 0 then
for i := 0 to c.Count - 1 do
Dispose(PVariant(c.Objects[i]));
end;
procedure disposeListBoxObjects(l: TListBox);
var
i: integer;
begin
if l.Items.Count > 0 then
for i := 0 to l.Items.Count - 1 do
Dispose(PVariant(l.Items.Objects[i]));
end;
end.
var
FileBuff: TBytes;
Pattern: TBytes;
begin
FileBuff := filetobytes(filename);
Result := CompareMem(#Pattern[0], #FileBuff[0], Length(Pattern));
end;
Is there any function such as
Result := Pos(#Pattern[0], #FileBuff[0]);
I think this does it:
function BytePos(const Pattern: TBytes; const Buffer: PByte; const BufLen: cardinal): PByte;
var
PatternLength: cardinal;
i: cardinal;
j: cardinal;
OK: boolean;
begin
result := nil;
PatternLength := length(Pattern);
if PatternLength > BufLen then Exit;
if PatternLength = 0 then Exit(Buffer);
for i := 0 to BufLen - PatternLength do
if PByte(Buffer + i)^ = Pattern[0] then
begin
OK := true;
for j := 1 to PatternLength - 1 do
if PByte(Buffer + i + j)^ <> Pattern[j] then
begin
OK := false;
break
end;
if OK then
Exit(Buffer + i);
end;
end;
Write your own. No optimization can be done when looking for just one byte, so any implementation you'll find would basically do the same thing.
Written in browser:
function BytePos(Pattern:Byte; Buffer:PByte; BufferSize:Integer): Integer;
var i:Integer;
begin
for i:=0 to BufferSize-1 do
if Buffer[i] = Pattern then
begin
Result := i;
Exit;
end;
Result := -1;
end;