2010-07-08 2 views
1

필드 길이와 레코드 길이가 고정 된 텍스트 파일에서 데이터를 읽어야합니다. 필드는 0으로 채워지거나 공백으로 채워지며 항상 동일한 순서로 나타나며 각 레코드는 CRLF로 끝납니다. 파일은 레코드의 첫 x 째. 자에 의해 결정되는 세 가지 가능한 레코드 유형 중 하나를 가질 수 있습니다.Delphi에서 고정 길이 필드와 레코드가있는 텍스트 파일 읽기

지금까지 모든 레코드 유형에 대한 기본 클래스와 각 레코드 유형에 대한 하위 클래스를 만들었습니다.

type 
    TRecordBase = class abstract 
    public 
    // Various common fields... 
    function ToString: string; virtual; abstract; 
    procedure Read(AString: string); virtual; abstract; 
    end; 

    TRecordType1 = class(TRecordBase) 
    public 
    //RecordType1 fields... 
    function ToString: string; override; 
    procedure Read(AString: string); override; 
    end; 

    TRecordType2 = class(TRecordBase) 
    public 
    //RecordType2 fields... 
    function ToString: string; override; 
    procedure Read(AString: string); override; 
    end; 

    TRecordType3 = class(TRecordBase) 
    public 
    //RecordType3 fields... 
    function ToString: string; override; 
    procedure Read(AString: string); override; 
    end; 

그때 나는 단순히 첫 번째 문자에서 그 유형을 결정, 문자열로 파일의 각 행을 읽을 적절한 클래스 인스턴스를 생성하고 Read를 호출합니다.

아이디어는 레코드 클래스가 레코드의 문자열 표현을 읽고 쓰는 데 사용할 수 있다는 것입니다. Read 프로 시저에서는 문자열을 분리하여 공용 필드에 할당해야합니다.

나는이 두 개 (또는 3 개) 질문 :

  • 이이 유형의 파일을 처리 할 수있는 좋은 방법인가?
  • 그렇다면 Read 절차의 구현은 어떻게됩니까? (나는 구분 된 파일을 다루었지만 이것이 고정 길이 필드와의 첫 만남이다.)
  • 그렇지 않다면 어떤 접근을 취할 것인가?

업데이트 그냥 내가 누락 된 세부 사항의 일부를 입력 거라고 생각했다. 이러한 레코드 클래스는 본질적으로 DTO (데이터 전송 객체)입니다. 필드는 public으로 선언되며 유일한 메서드는 문자열로 /에서 변환하는 것입니다. 필드의 유일한 데이터 유효성 검사는 컴파일러의 유형 검사입니다. 필드는 TStringBuilder.AppendFormat을 사용하여 필수 순서로 문자열로 변환됩니다. 이렇게하면 필드가 채워지거나 적절한 길이로 절단됩니다.

Rob'sCopyStrTo*을 결합하여 사용하는 것이 좋습니다. 또한 즉

const Field1Pos = 1; 
const Field1Length = 1; 
const Field2Pos = 2; 
const Field2Length = 5; 

하는 consts가 Copy의 통화에서 "매직 넘버"보다 읽기 조금 쉽게, 클래스 상수로 필드의 위치와 길이를 정의했습니다.

기타 제안 사항을 보내 주시면 감사하겠습니다.

+0

안녕하세요 Kenneth, 고정 길이 레코드를 구문 분석하는 완전한 예를 찾고 있습니다.이 게시물을 우연히 발견했습니다. 나 (나와 StackOverflow의 다른 사람들)와 코드를 공유 할 수 있습니까? 미리 감사드립니다, fabio vitale –

+0

제가 게시 한 것은 이미 NDA를 준수하기 위해 일반화 된 것입니다. 나는 인위적인 예를 생각해 낼 것이다. –

+0

감사합니다. 케네 쓰! 나는 그것을 기다릴께 ;-) –

답변

2

나는 한 가지를 변경할 것 : 읽기 생성자와 읽기 과정을 교체,이 같은 :

TRecordBase = class 
public 
    constructor CreateFromText(Text:string);virtual;abstract; 
end; 

TRecordType1 = class(TRecordBase) 
public 
    constructor CreateFromText(Text:string);override; 
end; 

당신이 기록과 함께 무엇에 따라이 타이핑을 저장하고 읽을 코드를 쉽게 할 것이다 :

var s:string; // string from stream or string-list 
if s[1] = 'X'then DoSomethingWith(TRecordType1.Create(s)); 

가상 생성자가 있으면 레코드 유형 수가 증가 할 때 편리합니다. 당신이 뭔가를 할 수 있습니다

// Define an class type 
type TRecordBaseClass = class of TRecordBase; 

// Using Delphi 2010? Use a dictionary to register (FirstChar, TRecordBaseClass) paris 
var RecordClassDictionary = TDictionary<char, TRecordBaseClass>; 

// Init the dictionary like this: 
RecordClassDictionary.Add('1', TRecordType1); 
RecordClassDictionary.Add('2', TRecordType2); 
RecordClassDictionary.Add('3', TRecordType3); 

// And use it like this: 
var RecordBaseClass: TRecordBaseClass; 
for line in TextToParse do 
    if RecordClassDictionary.TryGetValue(line[1], RecordBaseClass) then 
    // Read the record, do something with the record 
    DoSomethingWithTheRecord(RecordBaseClass.CreateFromText(line)) 
    else 
    raise Exception.Create('Unkown record type.'); 
+0

아, 추상 팩터 리 패턴을 약간 수정했습니다. 나는 그것을 좋아한다. 나는 기록 유형의 수가 증가 할 것 같지 않다. 그들은 약 10 년 동안 계속 변하지 않았습니다. 스펙을 작성한 그룹은 변경을 결정하면 xml을 선호하여 포기할 것입니다. –

1

나에게 잘 보입니다. 필드를 추출하려면 Copy 표준 함수를 사용할 수 있습니다. 입력 문자열, 필드의 첫 번째 문자 색인 및 문자 수를 입력하면 해당 부분을 새 문자열로 반환하여 다른 문자열 변수에 할당하거나 추가 변환을 위해 다른 함수에 전달할 수 있습니다 예 : StrToInt.

0

나는 귀하의 접근 방식이 매우 우아한 해결책이라고 생각합니다.

지정하지 않은 한 가지는 입력란의 작동 방식입니다. 그들은 고정 길이이기 때문에 속성을 설정하는 방법을 고려해 볼 것이므로 속성의 길이를 확인할 수 있습니다.

1

필드 길이와 레코드 길이가 고정되어 있다면,이 가변 부분과 거의 잊혀진 기록을 사용하십시오 :

TRecord1 = packed record 
    A: array[0..10] of char; 
end; 

TRecord2 = packed record 
    B: array[0..20] of Byte; 
    C: array[0..5] of Byte; 
end; 

TRecord3 = packed record 
    D: array[0..10] of Byte; 
    E: array[0..15] of Byte; 
    F: array[0..1] of Byte; 
end; 


TMyRecord = packed record 
    case RecordType: Char of 
    '1': (Rec1: TRecord1); 
    '2': (Rec2: TRecord2); 
    '3': (Rec3: TRecord3); 
end; 

S := ReadLn; 

with TMyRecord(S[1]) do 
begin 
    ... 
end; 

당신은을 사용하는 경우 Delphi 릴리스는 필드에 액세스하는 데 사용할 수있는 레코드 메서드를 지원합니다.