{ ----------------------------------- 文字列処理関数 2002/08/18 VCLのSysUtilsに依存しない AnsiPosが欲しかったのでRangeAnsiPosを利用した StrPartsCompare関数の使っているSysUtils.ByteTypeの 実装についてVCLのソースから切り出す事が困難だったので 独自に実装してみた 2002/08/21 独自に実装しても結局SysUtils.ByteTypeTestと 同じになってしまった。 2002/08/22 SysUtilsに依存しない部分を StringUnitLightに移動 2003/03/13 Unit名をStringUnitからStringUnitHeavyへ変更 ChangeLineBreakesをStringUnitLightへ移動 2003/03/30 BackAnsiPosを廃止、StringUnitLightの同名関数と同機能 testBackAnsiPosで動作は検証済み 2003/08/16 Unicode入出力関数をリファクタリング 2004/03/05 SaveStringToFile内のForceDirectoryのエラー判定が 間違っていたので修正 //----------------------------------- } unit StringUnitHeavy; interface uses StringUnitLight, SysUtils, Classes; //TFileStreamを使っているからusesが必要 //function BackAnsiPos(const SubStr,S: String): Integer; //procedure testBackAnsiPos; function LoadStringFromFile(const FileName: String): String; procedure SaveStringToFile(const FileName, Str: String); type TUtf16UnicodeEndian = (ueLittle, ueBig); function LoadWideStringFromFile(const FileName: String; Endian: TUtf16UnicodeEndian = ueLittle; UseBOM: Boolean = True): WideString; procedure SaveWideStringToFile(const FileName: String; const Str: WideString; Endian: TUtf16UnicodeEndian = ueLittle; BOMAdd: Boolean = True); procedure WriteAddStringToFile(const FileName, Str: String); implementation //XPテスト procedure Check(A, B: Variant); begin if not(A = B) then raise Exception.Create('エラーです ' + A + ':' + B); end; {------------------------------- //文字列をファイルに読み込み/書き込みする関数 LoadStringFromFile SaveStringToFile 機能: StringをCRLFまでちゃんとファイルに入出力します (StringListではCRLFが変になってしまう時もある) 戻り値: LoadStringFromFile: 読み込んだファイルの文字列 処理: AssignFileやReset/Rewrite,BlockRead/BlockWriteで 処理を行ないます 備考: ・FindFileはソース中で使っているように FindFile(FileName).Size; のようにするとファイルサイズを取得できます。 ・DirectryExistを使っているので uses FileCtrlを追加してください 履歴: 2001/04/20 2004/11/20 ・Load/Saveでファイルが開けない時の例外処理を追加 ResetやRewriteでI/Oエラーが発生する時がある [Delphi-ML:3531] RE: [Q]Exception handling on File Accesses ・ReadOnly属性ファイルが読み込めないので FileMode := fmOpenRead;を指定 [Delphi-ML:25781] RE: ReadOnlyファイルを開く方法 2005/5/13 ・SaveStringToFileで例外処理に不具合があったので修正 2006/12/08 ・例外処理のコメントをしっかり記述した //------------------------------} function FindFile(const FileName: String): TSearchRec; begin if FindFirst(FileName, faAnyFile, Result) = 0 then FindClose(Result) else raise Exception.Create(FileName+'の情報の取得に失敗しました。'); end; function LoadStringFromFile(const FileName: String): String; var size: Integer; F: File; begin size := FindFile(FileName).Size; if size = 0 then begin Result := ''; Exit; end; SetLength(Result, size); AssignFile(F, FileName); try try FileMode := fmOpenRead; Reset(F, size); BlockRead(F, PChar(Result)^, 1); finally CloseFile(F); end; except on EInOutError do begin raise EInOutError.Create(ExtractFileName(FileName) + CRLF + 'ファイルの読込みが出来ません'); end; end; end; procedure SaveStringToFile(const FileName, Str: String); var path: String; F: File; begin path := ExtractFileDir(FileName); if (path <> '') and not DirectoryExists(path) then if not ForceDirectories(path) then raise Exception.Create('ディレクトリ'+path+'が作れません。'); AssignFile(F, FileName); try Rewrite(F, Length(Str)); try if Length(Str) > 0 then BlockWrite(F, PChar(Str)^, 1); finally CloseFile(F); end; except on EInOutError do begin raise EInOutError.Create(ExtractFileName(FileName) + CRLF + 'ファイルの書込みが出来ません'); end; end; end; { ----------------------------------- 以下に示すコード 『try try Rewrite』の書き方では 読み取り専用ファイルにアクセスする際には Rewrite で例外が発生した後に finally 内の CloseFile で例外が発生する。 D2006では改善されているようだが、 以前のVersionでは処理されないCloseFileの例外が メモリリークを引き起こしていた。(MemChk等で確認) なので、『try try Rewrite』ではなく 上記のように『try Rewrite try』と記述して Rewrite で例外が発生した時は CloseFileを呼び出さずに except に飛ぶように記述するのがよい //----------------------------------- } //procedure SaveStringToFile(const FileName, Str: String); //var // path: String; // F: File; //begin // path := ExtractFileDir(FileName); // if (path <> '') and not DirectoryExists(path) then // if not ForceDirectories(path) then // raise Exception.Create('ディレクトリ'+path+'が作れません。'); // // AssignFile(F, FileName); // try // try // Rewrite(F, Length(Str)); // if Length(Str) > 0 then // BlockWrite(F, PChar(Str)^, 1); // finally // CloseFile(F); // end; // except // on EInOutError do begin // raise EInOutError.Create(ExtractFileName(FileName) + CRLF + // 'ファイルの書込みが出来ません'); // end; // end; //end; //------------------------------ {------------------------------- // 文字列をファイルに追記する関数 機能: 備考: 履歴: 2007/02/23(金) 19:27 //------------------------------} procedure WriteAddStringToFile(const FileName, Str: String); var path: String; F: TextFile; // PBuf: PChar; // BufStr: String; begin path := ExtractFileDir(FileName); if (path <> '') and not DirectoryExists(path) then if not ForceDirectories(path) then raise Exception.Create('ディレクトリ'+path+'が作れません。'); AssignFile(F, FileName); try {$IOChecks Off} Append(F); {$IOChecks On} if IOResult <> 0 then Rewrite(F); try if Length(Str) > 0 then begin System.Write(F, Str); end; finally System.CloseFile(F); end; except on EInOutError do begin raise EInOutError.Create(ExtractFileName(FileName) + CRLF + 'ファイルの書込みが出来ません'); end; end; end; //------------------------------ {------------------------------- //文字列をファイルに読み込み/書き込みする関数 LoadWideStringFromFile SaveWideStringToFile 機能: 文字列をUnicode(UTF16)でファイルに読み書きする 引数: Load/Save FileName: ファイルの名前 Endian: エンディアン指定 Load UserBOM: BOMフラグがあればそれを読みEndianを判断 Save BOMADD: 保存するときにBOMフラグをつける 備考: Unicode・UTF16にはEndianというものがあり LittleとBigと両方のタイプがある。 通常WindowsではLittleが使われるようです。 参考: http://member.nifty.ne.jp/m-and-i/tips/unicode.htm http://homepage1.nifty.com/nomenclator/unicode/ucs_utf.htm [Delphi:45937] Re: Unicode でのファイル保存 履歴: 2002/10/08 作成 2003/08/16 ソース短縮・内部の簡略化 //------------------------------} const UNICODE_Little_Endian = $FEFF; const UNICODE_Big_Endian = $FFFE; const UTF16BOM: array[ueLittle..ueBig] of word = (UNICODE_Little_Endian, UNICODE_Big_Endian); { ----------------------------------- Win自体がLittle_Endianなので変数に格納される時、バイト列で見ると $FEFFが[FF FE .. ..] $FFFEが[FE FF .. ..]となる //----------------------------------- } function LoadWideStringFromFileBigEndian(FileStream: TFileStream): WideString; var StreamSize: Integer; i: Integer; DownByte, UpByte: Byte; begin Result :=''; StreamSize := FileStream.Size - FileStream.Position; if StreamSize <= 0 then Exit; SetLength(Result, StreamSize div 2 ); for i := 1 to StreamSize div 2 do begin FileStream.ReadBuffer(DownByte, SizeOf(Byte)); FileStream.ReadBuffer(UpByte, SizeOf(Byte)); Result[i] := WideChar( (UpByte) + (DownByte) * $100 ); end; {↑上位バイトと下位バイトを合わせてWideCharを作成している} end; function LoadWideStringFromFileLittleEndian(FileStream: TFileStream): WideString; var StreamSize: Integer; begin Result :=''; StreamSize := FileStream.Size - FileStream.Position; if StreamSize <= 0 then Exit; SetLength(Result, StreamSize div 2 ); FileStream.ReadBuffer(Result[1], StreamSize); end; function LoadWideStringFromFile(const FileName: String; Endian: TUtf16UnicodeEndian = ueLittle; UseBOM: Boolean = True): WideString; var FileStream: TFileStream; BOMWord: Word; begin Result := ''; if not FileExists(FileName) then Exit; FileStream := TFileStream.Create(FileName, fmOpenRead); try if FileStream.Size <= 0 then Exit; FileStream.Position := 0; BOMWord := 0; if SizeOf(Word) <= FileStream.Size then begin FileStream.ReadBuffer(BOMWord, SizeOf(Word)); if not ( (BOMWord = UTF16BOM[ueLittle]) or (BOMWord = UTF16BOM[ueBig]) ) then begin FileStream.Position := 0; end; {↑BOMを読み込めなかった場合、読み込み位置を先頭にする} end; {↓UseBOMはBOMを優先的に使う指定} if UseBOM and (BOMWord = UTF16BOM[ueLittle]) then begin Result := LoadWideStringFromFileLittleEndian(FileStream); end else if UseBOM and (BOMWord = UTF16BOM[ueBig]) then begin Result := LoadWideStringFromFileBigEndian(FileStream); end else begin {↓BOMがない場合・またはBOMを使わない指定の場合は Endian引数の指定を使って判断する} case Endian of {←利用者の選択を反映} ueLittle: Result := LoadWideStringFromFileLittleEndian(FileStream); ueBig: Result := LoadWideStringFromFileBigEndian(FileStream); end; end; finally FileStream.Free; end; end; procedure SaveWideStringToFile(const FileName: String; const Str: WideString; Endian: TUtf16UnicodeEndian = ueLittle; BOMAdd: Boolean = True); var FileStream: TFileStream; EndianWord: Word; i: Integer; begin FileStream := TFileStream.Create(FileName, fmCreate); try EndianWord := UTF16BOM[Endian]; FileStream.Write(EndianWord, SizeOf(Word)); case Endian of ueLittle: begin FileStream.Write(Str[1], Length(Str) * 2); end; ueBig: begin for i := 0 to Length(Str)-1 do begin FileStream.Write((PChar(Str)+(i*2)+1)^, SizeOf(Byte)); FileStream.Write((PChar(Str)+(i*2) )^, SizeOf(Byte)); end; end; end; finally FileStream.Free; end; end; //------------------------------ end.