📄 gptextstream.pas
字号:
function TGpTextStream.Read(var buffer; count: longint): longint;
var
bufPtr : pointer;
bytesConv: integer;
bytesLeft: integer;
bytesRead: integer;
numChar : integer;
tmpBuf : pointer;
begin
DelayedSeek;
if IsUnicode then begin
if Codepage = CP_UTF8 then begin
numChar := count div SizeOf(WideChar);
tmpBuf := AllocBuffer(numChar);
try
bufPtr := @buffer;
Result := 0;
bytesLeft := 0;
repeat
// at least numChar UTF-8 bytes are needed for numChar WideChars
bytesRead := WrappedStream.Read(pointer(integer(tmpBuf)+bytesLeft)^,numChar);
bytesConv := UTF8BufToWideCharBuf(tmpBuf^,bytesRead+bytesLeft,bufPtr^,bytesLeft);
Result := Result + bytesConv;
if bytesRead <> numChar then // end of stream
break;
numChar := numChar - (bytesConv div SizeOf(WideChar));
bufPtr := pointer(integer(bufPtr) + bytesConv);
if (bytesLeft > 0) and (bytesLeft < bytesRead) then
Move(pointer(integer(tmpBuf)+bytesRead-bytesLeft)^,tmpBuf^,bytesLeft);
until numChar = 0;
finally FreeBuffer(tmpBuf); end;
end
else
Result := WrappedStream.Read(buffer,count);
end
else begin
if Odd(count) then
raise EGpTextStream.CreateFmtHelp(sCannotConvertOddNumberOfBytes,[StreamName,count],hcTFCannotConvertOdd)
else begin
numChar := count div SizeOf(WideChar);
tmpBuf := AllocBuffer(numChar);
try
bytesRead := WrappedStream.Read(tmpBuf^,numChar);
numChar := MultiByteToWideChar(tsCodePage, MB_PRECOMPOSED,
PChar(tmpBuf), bytesRead, PWideChar(@buffer), numChar);
Result := numChar * SizeOf(WideChar);
finally FreeBuffer(tmpBuf); end;
end;
end;
end; { TGpTextStream.Read }
{:Reads one text line stream. If stream is 8-bit, LF, CR, CRLF, and LFCR are
considered end-of-line terminators (if included in AcceptedDelimiters). If
stream is 16-bit, both /000D/000A/ and /2028/ are considered end-of-line
terminators (if included in AcceptedDelimiters). If stream is 8-bit, line is
converted to Unicode according to code page specified in constructor.
<b>This function is quite slow.</b>
@returns One line of text.
@raises EGpTextStream if conversion from 8-bit to Unicode failes.
}
function TGpTextStream.Readln: WideString;
var
lastCh : WideChar;
numCh : integer;
wch : WideChar;
function Reverse(w: word): word;
begin
if tscfReverseByteOrder in tsCreateFlags then begin
WordRec(Result).Hi := WordRec(w).Lo;
WordRec(Result).Lo := WordRec(w).Hi;
end
else
Result := w;
end; { Readln }
procedure ReverseResult;
var
ich: integer;
pwc: PWord;
begin
if tscfReverseByteOrder in tsCreateFlags then begin
pwc := @Result[1];
for ich := 1 to Length(Result) div SizeOf(WideChar) do begin
WordRec(pwc^).Hi := WordRec(pwc^).Lo;
WordRec(pwc^).Lo := WordRec(pwc^).Hi;
Inc(pwc);
end; //for
end;
end; { ReverseBlock }
begin { TGpTextStream.Readln }
if assigned(tsReadlnBuf) then
tsReadlnBuf.Clear
else
tsReadlnBuf := TMemoryStream.Create;
lastCh := #0;
numCh := 0;
repeat
if Read(wch,SizeOf(WideChar)) <> SizeOf(WideChar) then
break; // EOF
if (((AcceptedDelimiters = []) or ([tsldLF, tsldCRLF]*AcceptedDelimiters <> [])) or
(IsUnicode and ((AcceptedDelimiters = []) or (tsld000D000A in AcceptedDelimiters)))) and
(wch = WideChar(Reverse($000A))) then begin
if (((AcceptedDelimiters = []) or ([tsldLFCR]*AcceptedDelimiters <> [])) or
(IsUnicode and ((AcceptedDelimiters = []) or (tsld000D000A in AcceptedDelimiters)))) and
(lastCh = WideChar(Reverse($000D))) then
numCh := 1;
break;
end
else if (([tsldCR, tsldLFCR]*AcceptedDelimiters <> [])) and
(wch = WideChar(Reverse($000D))) then begin
if (([tsldLFCR]*AcceptedDelimiters <> [])) and
(lastCh = WideChar(Reverse($000A))) then
numCh := 1;
break;
end
else if IsUnicode and
((AcceptedDelimiters = []) or (tsld2028 in AcceptedDelimiters)) and
(wch = WideChar(Reverse($2028))) then
break;
tsReadlnBuf.Write(wch,SizeOf(WideChar));
lastCh := wch;
until false;
SetLength(Result,(tsReadlnBuf.Size-numCh*SizeOf(WideChar)) div SizeOf(WideChar));
if Result <> '' then
Move(tsReadlnBuf.Memory^,Result[1],tsReadlnBuf.Size-numCh*SizeOf(WideChar));
ReverseResult;
end; { TGpTextStream.Readln }
{:Internal method that sets current code page or locates default code page if
0 is passed as a parameter.
@param cp Code page number or 0 for default code page.
}
procedure TGpTextStream.SetCodepage(cp: word);
begin
if (cp = CP_UTF8) or (cp = CP_UNICODE) then begin
tsCodePage := cp;
tsCreateFlags := tsCreateFlags + [tscfUnicode];
end
else begin
if (cp = 0) and (not IsUnicode) then
tsCodePage := GetDefaultAnsiCodepage(GetKeyboardLayout(GetCurrentThreadId) and $FFFF, 1252)
else
tsCodePage := cp;
if not ((tsCodePage = 0) or (tsCodePage = CP_UNICODE)) then
tsCreateFlags := tsCreateFlags - [tscfUnicode];
end;
end; { TGpTextStream.SetCodepage }
{:Returns error message prefix.
@param param Optional parameter to be added to the message prefix.
@returns Error message prefix.
@since 2001-05-15 (3.0)
}
function TGpTextStream.StreamName(param: string): string;
begin
Result := 'TGpTextStream';
if param <> '' then
Result := Result + '.' + param;
end; { TGpTextStream.StreamName }
{:Checks condition and creates appropriately formatted EGpTextStream
exception.
@param condition If false, Win32Check will generate an exception.
@param method Name of TGpTextStream method that called Win32Check.
@raises EGpTextStream if (not condition).
}
procedure TGpTextStream.Win32Check(condition: boolean; method: string);
var
Error: EGpTextStream;
begin
if not condition then begin
tsWindowsError := GetLastError;
if tsWindowsError <> ERROR_SUCCESS then
Error := EGpTextStream.CreateFmtHelp(sStreamFailed+
{$IFNDEF D6PLUS}SWin32Error{$ELSE}SOSError{$ENDIF},
[StreamName(method),tsWindowsError,SysErrorMessage(tsWindowsError)],
hcTFWindowsError)
else
Error := EGpTextStream.CreateFmtHelp(sStreamFailed+
{$IFNDEF D6PLUS}SUnkWin32Error{$ELSE}SUnkOSError{$ENDIF},
[StreamName(method)],hcTFUnknownWindowsError);
raise Error;
end;
end; { TGpTextStream.Win32Check }
{:Writes 'count' number of bytes to stream. 'Count' must be an even number as
data is always expected in Unicode format (two bytes per character). If stream
is 8-bit, data is converted from Unicode according to code page specified in
constructor.
@param buffer Data to be written.
@param count Number of bytes to be written.
@returns Number of bytes actually written.
@raises EGpTextStream if 'count' is odd.
@raises EGpTextStream if conversion from 8-bit to Unicode failes.
}
function TGpTextStream.Write(const buffer; count: longint): longint;
var
leftUTF8 : integer;
numBytes : integer;
numChar : integer;
tmpBuf : pointer;
uniBuf : pointer;
utfWritten: integer;
begin
DelayedSeek;
if IsUnicode then begin
if Codepage = CP_UTF8 then begin
numChar := count div SizeOf(WideChar);
tmpBuf := AllocBuffer(numChar*3); // worst case - 3 bytes per character
try
numBytes := WideCharBufToUTF8Buf(buffer,count,tmpBuf^);
utfWritten := WrappedStream.Write(tmpBuf^,numBytes);
if utfWritten <> numBytes then begin
Result := 0; // to keep Delphi from complaining
// To find out how much data was actually written (in term of Unicode
// characters) we have to decode written data back to Unicode. Ouch.
GetMem(uniBuf,count); // decoded data cannot use more space than original Unicode data
try
Result := UTF8BufToWideCharBuf(tmpBuf^,Result,uniBuf^,leftUTF8);
finally FreeMem(uniBuf); end;
end
else // everything was written
Result := count;
finally FreeBuffer(tmpBuf); end;
end
else
Result := WrappedStream.Write(buffer,count);
end
else begin
if Odd(count) then
raise EGpTextStream.CreateFmtHelp(sCannotConvertOddNumberOfBytes,[StreamName,count],hcTFCannotConvertOdd)
else begin
numChar := count div SizeOf(WideChar);
tmpBuf := AllocBuffer(numChar);
try
numChar := WideCharToMultiByte(tsCodePage,
WC_COMPOSITECHECK or WC_DISCARDNS or WC_SEPCHARS or WC_DEFAULTCHAR,
@buffer, numChar, tmpBuf, numChar, nil, nil);
Win32Check(numChar <> 0,'Write');
Result := WrappedStream.Write(tmpBuf^,numChar) * SizeOf(WideChar);
finally FreeBuffer(tmpBuf); end;
end;
end;
end; { TGpTextStream.Write }
{:Writes string to stream and terminates it with line delimiter (as set in
constructor). If stream is 8-bit, data is converted from Unicode according to
code page specified in constructor.
@param ln String to be written.
@returns True if string was written successfully.
@raises EGpTextStream if conversion from 8-bit to Unicode failes.
}
function TGpTextStream.Writeln(const ln: WideString): boolean;
var
ch: AnsiChar;
wc: WideChar;
begin
if ln <> '' then begin
if not WriteString(ln) then begin
Result := false;
Exit;
end;
end;
if IsUnicode then begin
if tscfUse2028 in tsCreateFlags then begin
wc := WideChar($2028);
Result := (Write(wc,SizeOf(WideChar)) = SizeOf(WideChar));
end
else begin
wc := WideChar($000D);
Result := (Write(wc,SizeOf(WideChar)) = SizeOf(WideChar));
if Result then begin
wc := WideChar($000A);
Result := (Write(wc,SizeOf(WideChar)) = SizeOf(WideChar));
end;
end;
end
else begin
if tscfUseLF in tsCreateFlags then begin
ch := Char($0D);
Result := (WrappedStream.Write(ch,SizeOf(Char)) = SizeOf(Char));
end
else begin
ch := Char($0D);
Result := (WrappedStream.Write(ch,SizeOf(Char)) = SizeOf(Char));
if Result then begin
ch := Char($0A);
Result := (WrappedStream.Write(ch,SizeOf(Char)) = SizeOf(Char));
end;
end;
end;
end; { TGpTextStream.Writeln }
{:Writes string to stream. If stream is 8-bit, data is converted from Unicode
according to code page specified in constructor.
@param ws String to be written.
@returns True if string was written successfully.
@raises EGpTextStream if conversion from 8-bit to Unicode failes.
}
function TGpTextStream.WriteString(const ws: WideString): boolean;
begin
if ws <> '' then
Result := (Write(ws[1],Length(ws)*SizeOf(WideChar)) = Length(ws)*SizeOf(WideChar))
else
Result := true;
end; { TGpTextStream.WriteString }
end.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -