我正在处理一个特定的场景,我必须从文本文件中读取,解析它,从中提取有意义的信息,使用信息执行SQL查询,然后生成响应,输出文件。
我有大约3000行代码。一切都按预期工作。但是我一直在考虑一个可能会破坏我的项目的可能性。
正在读取的文本文件(我们称之为Text.txt)可能包含一行或多行。
在我的情况下,'line'由其段名称标识 - 例如ISA,BHT,HB,NM1等......每个段结尾由特殊字符'〜'标识。
现在,如果文件由多行组成(这样每行对应一个段);说: -
ISA .......〜
NM1 .......〜
DMG .......〜
SE ........〜
依此类推....然后我的代码基本上每次读取一行'(即每个段),一次一个,并使用以下命令将其存储到临时缓冲区中: -
ReadLn(myFile,buffer);
然后根据每一行执行评估。产生所需的输出。没问题。
然而问题是......如果文件只包含一行(由多个段组成),表示为: -
ISA .......〜NM1 .......〜DMG .......〜SE ........〜
然后用我的ReadLine命令我读取整行而不是每个段,一次一个。这对我的代码不起作用。
我正在考虑创建一个if,else语句对...这是基于我的Txt.txt文件包含的行数。例如: -
如果line = 1: - 然后一次提取每个片段......由特殊字符'〜'分隔 执行必要的任务(3000行代码) 否则如果行> 1: - 然后一次提取每一行(对应每个段) 执行必要的任务(3000行代码)。
现在3000行代码重复两次,我发现复制并粘贴所有代码两次并不优雅。
如果我能得到一些关于如何解决这个问题的反馈,我将不胜感激,无论是单行文件还是多行文件...当我继续评估时,我只使用一个段一段时间。
答案 0 :(得分:1)
有很多可行的方法可以做到这一点。哪种方法最适合您可能取决于这些文件的持续时间以及性能的重要性。
一个简单的解决方案是一次只读取一个字符,直到你达到你的代字号分隔符。 下面的例程ReadOneItem显示了如何完成此操作。
procedure TForm1.Button1Click(Sender: TObject);
const
FileName = 'c:\kuiper\test2.txt';
var
MyFile : textfile;
Buffer : string;
// Read one item from text file MyFile.
// Load characters one at a time.
// Ignore CR and LF characters
// Stop reading at end-of-file, or when a '~' is read
function ReadOneItem : string;
var
C : char;
begin
Result := '';
// loop continues until break
while true do
begin
// are we at the end-of-file? If so we're done
if eof(MyFile) then
break;
// read in the next character
read ( MyFile, C );
// ignore CR and LF
if ( C = #13 ) or ( C = #10 ) then
{do nothing}
else
begin
// add the character to the end
Result := Result + C;
// if this is the delimiter then stop reading
if C = '~' then
break;
end;
end;
end;
begin
assignfile ( MyFile, FileName );
reset ( MyFile );
try
while not EOF(MyFile) do
begin
Buffer := ReadOneItem;
Memo1 . Lines . Add ( Buffer );
end;
finally
closefile ( MyFile );
end;
end;
答案 1 :(得分:0)
我将通过Win32 API CreateFileMapping()
和MapViewOfFile()
函数使用文件映射,然后按原样解析原始数据,扫描~
个字符并忽略任何换行符您可能会在每个细分之间遇到。例如:
var
hFile: THandle;
hMapping: THandle;
pView: Pointer;
FileSize, I: DWORD;
pSegmentStart, pSegmentEnd: PAnsiChar;
sSegment: AnsiString;
begin
hFile := CreateFile('Path\To\Text.txt', GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0);
if hFile = INVALID_HANDLE_VALUE then RaiseLastOSError;
try
FileSize := GetFileSize(hFile, nil);
if FileSize = INVALID_FILE_SIZE then RaiseLastOSError;
if FileSize > 0 then
begin
hMapping := CreateFileMapping(hFile, nil, PAGE_READONLY, 0, FileSize, nil);
if hMapping = 0 then RaiseLastOSError;
try
pView := MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, FileSize);
if pView = nil then RaiseLastOSError;
try
pSegmentStart := PAnsiChar(pView);
pSegmentEnd := pSegmentStart;
I := 0;
while I < FileSize do
begin
if pSegmentEnd^ = '~' then
begin
SetString(sSegment, pSegmentStart, Integer(pSegmentEnd-pSegmentStart));
// use sSegment as needed...
pSegmentStart := pSegmentEnd + 1;
Inc(I);
while (I < FileSize) and (pSegmentStart^ in [#13, #10]) do
begin
Inc(pSegmentStart);
Inc(I);
end;
pSegmentEnd := pSegmentStart;
end else
begin
Inc(pSegmentEnd);
Inc(I);
end;
end;
if pSegmentEnd > pSegmentStart then
begin
SetString(sSegment, pSegmentStart, Integer(pSegmentEnd-pSegmentStart));
// use sSegment as needed...
end;
finally
UnmapViewOfFile(pView);
end;
finally
CloseHandle(hMapping);
end;
end;
finally
CloseHandle(hFile);
end;