我想在Delphi中实现一个非常基本的任务:将字符串保存到磁盘并加载回来。这似乎是微不足道的,但是自从我升级到IOUtils之后我做了这个TWICE就遇到了问题(之前又一次......这就是为什么我把'辉煌'的决定升级到IOUtils)。
我使用这样的东西:
procedure WriteToFile(CONST FileName: string; CONST uString: string; CONST WriteOp: WriteOperation);
begin
if WriteOp= (woOverwrite)
then IOUtils.TFile.WriteAllText (FileName, uString) //overwrite
else IOUtils.TFile.AppendAllText(FileName, uString); //append
end;
简单吧?怎么可能出错?好吧,我最近在IOUtils中遇到了另一个(另一个)错误。所以,TFile是buggy。该错误详细here。
任何人都可以分享不是基于IOUtils和已知的替代方案(或仅仅是您的想法/想法)吗?嗯......上面的代码也为我工作了一段时间......所以,我知道如果难以保证一段代码(无论多小)都能真正起作用!
此外,我真的希望让我的WriteToFile过程在可能的情况下将字符串保存到ANSI文件(uString仅包含ANSI字符),否则为Unicode。 然后ReadAFile函数应自动检测编码并正确读回字符串 我们的想法是,仍有文本编辑器会错误地打开/解释Unicode / UTF文件。因此,只要有可能,请向用户提供一个好的旧ANSI文本文件。
所以:
- 覆盖/附加
- 尽可能保存为ANSI
- 内存效率(当加载文件为2GB时,不要吃4GB的ram)
- 应该使用任何文本文件(显然最多2GB)
- 没有IOUtils(太麻烦无法使用)
答案 0 :(得分:5)
然后ReadAFile函数应该自动检测编码并正确读回字符串。
这是不可能的。如果解释为任何文本编码,则存在格式良好的文件。例如,请参阅The Notepad file encoding problem, redux。
这意味着您的目标无法实现,您需要更改它们。
我的建议是做以下事情:
不了解UTF-8的文本编辑器不值得支持。如果您有意愿,请在创建文件时包含UTF-8 BOM。使用TEncoding.UTF8.GetBytes
和TEncoding.UTF8.GetString
进行编码和解码。
答案 1 :(得分:4)
只需使用TStringList,直到文件大小< ~50-100Mb(取决于CPU速度):
procedure ReadTextFromFile(const AFileName: string; SL: TStringList);
begin
SL.Clear;
SL.DefaultEncoding:=TEncoding.ANSI; // we know, that old files has this encoding
SL.LoadFromFile(AFileName, nil); // let TStringList detect real encoding.
// if not - it just use DefaultEncoding.
end;
procedure WriteTextToFile(const AFileName: string; const TextToWrite: string);
var
SL: TStringList;
begin
SL:=TStringList.Create;
try
ReadTextFromFile(AFileName, SL); // read all file with encoding detection
SL.Add(TextToWrite);
SL.SaveToFile(AFileName, TEncoding.UTF8); // write file with new encoding.
// DO NOT SET SL.WriteBOM to False!!!
finally
SL.Free;
end;
end;
答案 2 :(得分:1)
Inifiles单元应该支持unicode。至少根据这个答案:How do I read a UTF8 encoded INI file?
Inifiles常用于存储字符串,整数,布尔值甚至字符串列表。
procedure TConfig.ReadValues();
var
appINI: TIniFile;
begin
appINI := TIniFile.Create(ChangeFileExt(Application.ExeName,'.ini'));
try
FMainScreen_Top := appINI.ReadInteger('Options', 'MainScreen_Top', -1);
FMainScreen_Left := appINI.ReadInteger('Options', 'MainScreen_Left', -1);
FUserName := appINI.ReadString('Login', 'UserName', '');
FDevMode := appINI.ReadBool('Globals', 'DevMode', False);
finally
appINI.Free;
end;
end;
procedure TConfig.WriteValues(OnlyWriteAnalyzer: Boolean);
var
appINI: TIniFile;
begin
appINI := TIniFile.Create(ChangeFileExt(Application.ExeName,'.ini'));
try
appINI.WriteInteger('Options', 'MainScreen_Top', FMainScreen_Top);
appINI.WriteInteger('Options', 'MainScreen_Left', FMainScreen_Left);
appINI.WriteString('Login', 'UserName', FUserName);
appINI.WriteBool('Globals', 'DevMode', FDevMode);
finally
appINI.Free;
end;
end;
另请参阅关于inifiles的embarcadero文档:http://docwiki.embarcadero.com/Libraries/Seattle/en/System.IniFiles.TIniFile
答案 3 :(得分:1)
基于大卫建议的代码:
{--------------------------------------------------------------------------------------------------
READ/WRITE UNICODE
--------------------------------------------------------------------------------------------------}
procedure WriteToFile(CONST FileName: string; CONST aString: String; CONST WriteOp: WriteOperation= woOverwrite; WritePreamble: Boolean= FALSE); { Write Unicode strings to a UTF8 file. It can also write a preamble }
VAR
Stream: TFileStream;
Preamble: TBytes;
sUTF8: RawByteString;
aMode: Integer;
begin
ForceDirectories(ExtractFilePath(FileName));
if (WriteOp= woAppend) AND FileExists(FileName)
then aMode := fmOpenReadWrite
else aMode := fmCreate;
Stream := TFileStream.Create(filename, aMode, fmShareDenyWrite); { Allow read during our writes }
TRY
sUTF8 := Utf8Encode(aString); { UTF16 to UTF8 encoding conversion. It will convert UnicodeString to WideString }
if (aMode = fmCreate) AND WritePreamble then
begin
preamble := TEncoding.UTF8.GetPreamble;
Stream.WriteBuffer( PAnsiChar(preamble)^, Length(preamble));
end;
if aMode = fmOpenReadWrite
then Stream.Position:= Stream.Size; { Go to the end }
Stream.WriteBuffer( PAnsiChar(sUTF8)^, Length(sUTF8) );
FINALLY
FreeAndNil(Stream);
END;
end;
procedure WriteToFile (CONST FileName: string; CONST aString: AnsiString; CONST WriteOp: WriteOperation);
begin
WriteToFile(FileName, String(aString), WriteOp, FALSE);
end;
function ReadFile(CONST FileName: string): String; {Tries to autodetermine the file type (ANSI, UTF8, UTF16, etc). Works with UNC paths }
begin
Result:= System.IOUtils.TFile.ReadAllText(FileName);
end;