Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Общие вопросы > Парсинг!!!


Автор: [EViL] 20.3.2005, 21:02
Мне надо прочитать настройки из XML - подобного (именно подобного!!!) файла.
Например,
Код

<graphics>
  <width>1024</width>
  <height>768</height>
  <antialiasing>true</antialiasing>
</graphics>
<sound>
  <volume>100</volume>
</sound>


Для этих целей я создал модуль, который работает наподобие TIniFile:
Код

unit ZD_ZMLParser;

interface

uses
  SysUtils, Classes;

type
  TZMLParser = class
    private
      Data: TStringList;
      _ZMLFileName: string;
      function _Read(const ASection, AKey: string): string;
      procedure _Write(ASection, AKey, AValue: string);
    public
      constructor Create(const AZMLFileName: string);
      function ReadString(const ASection, AKey: string): string;
      function ReadInteger(const ASection, AKey: string): integer;
      function ReadBoolean(const ASection, AKey: string): boolean;
      procedure WriteString(const ASection, AKey: string; AValue: string);
      procedure WriteInteger(const ASection, AKey: string; AValue: integer);
      procedure WriteBoolean(const ASection, AKey: string; AValue: boolean);
      procedure UpdateZMLFile;
      destructor Destroy; override;
  end;

implementation

function TwoSideTrim(AString: string): string;
begin
  Result := TrimLeft(TrimRight(AString));
end;

function GetCharCount(AString: string; AChar: char): integer;
var
  _Index: integer;
begin
  _Index := 0;
  repeat
    Inc(_Index);
  until AString[_Index] <> AChar;
  Result := _Index;
end;

function FillStr(AChar: char; ACount: integer): string;
var
  _Index: integer;
begin
  for _Index := 0 to ACount do Result := Result + AChar;
end;

constructor TZMLParser.Create(const AZMLFileName: string);
begin
  Data := TStringList.Create;
  if FileExists(AZMLFileName) then Data.LoadFromFile(AZMLFileName);
  _ZMLFileName := AZMLFileName;
end;

function TZMLParser._Read(const ASection, AKey: string): string;
var
  _Data: string;
  _Index, _SectionBegin, _SectionEnd: integer;
begin
  _SectionBegin := -1;
  _SectionEnd := -1;
  for _Index := 0 to Data.Count - 1 do begin
    _Data := TwoSideTrim((Data[_Index]));
    if Pos('<' + LowerCase(ASection) + '>', LowerCase(_Data)) = 1 then
      _SectionBegin := _Index;
    if Pos('</' + LowerCase(ASection) + '>', LowerCase(_Data)) = 1 then begin
      _SectionEnd := _Index;
      Break;
    end;
  end;
  for _Index := _SectionBegin + 1 to _SectionEnd - 1 do begin
    _Data := TwoSideTrim((Data[_Index]));
    if Pos('<' + LowerCase(AKey) + '>', LowerCase(_Data)) = 1 then begin
      Delete(_Data, 1, Length('<' + AKey + '>'));
      Delete(_Data, Length(_Data) - Length('</' + AKey + '>') + 1, Length('</' + AKey + '>'));
      Result := _Data;
      Break;
    end;
  end;
end;

procedure TZMLParser._Write(ASection, AKey, AValue: string);
var
  _Data: string;
  _Index, _SectionBegin, _SectionEnd: integer;
begin
  _SectionBegin := -1;
  _SectionEnd := -1;
  ASection := LowerCase(ASection);
  AKey := LowerCase(AKey);
  if _Read(ASection, AKey) = '' then begin
    if Data.Count <> 0 then begin
      for _Index := 0 to Data.Count - 1 do begin
        _Data := TwoSideTrim(Data[_Index]);
        if Pos('<' + LowerCase(ASection) + '>', LowerCase(_Data)) = 1 then
          _SectionBegin := _Index;
        if Pos('</' + LowerCase(ASection) + '>', LowerCase(_Data)) = 1 then begin
          _SectionEnd := _Index;
          Break;
        end;
      end;
      if _SectionBegin = -1 then begin
        Data.Add('<' + ASection + '>');
        Data.Add(Format('  <%s>%s</%s>', [AKey, AValue, AKey]));
        if _SectionEnd = -1 then
          Data.Add('</' + ASection + '>');
      end;
    end else begin
      Data.Add('<' + ASection + '>');
      Data.Add(Format('  <%s>%s</%s>', [AKey, AValue, AKey]));
      Data.Add('</' + ASection + '>');
    end;
  end else begin
    for _Index := 0 to Data.Count - 1 do begin
      _Data := TwoSideTrim(Data[_Index]);
      if Pos('<' + LowerCase(ASection) + '>', LowerCase(_Data)) = 1 then
        _SectionBegin := _Index;
      if Pos('</' + LowerCase(ASection) + '>', LowerCase(_Data)) = 1 then begin
        _SectionEnd := _Index;
        Break;
      end;
    end;
    for _Index := _SectionBegin + 1 to _SectionEnd - 1 do begin
    _Data := TwoSideTrim((Data[_Index]));
    if Pos('<' + LowerCase(AKey) + '>', LowerCase(_Data)) = 1 then
      Data[_Index] := Format('%s<%s>%s</%s>', [FillStr(' ', GetCharCount(_Data, ' ')), AKey, AValue, AKey]);
    end;
  end;
end;

function TZMLParser.ReadString(const ASection, AKey: string): string;
begin
  Result := _Read(ASection, AKey);
end;

function TZMLParser.ReadInteger(const ASection, AKey: string): integer;
var
  _Result: integer;
begin
  if TryStrToInt(_Read(ASection, AKey), _Result)
    then Result := _Result
    else Result := 0;
end;

function TZMLParser.ReadBoolean(const ASection, AKey: string): boolean;
var
  _Result: string;
begin
  _Result := Trim(LowerCase(_Read(ASection, AKey)));
  if (_Result = 'true') or (_Result = 'yes') or (_Result = '1')
    then Result := true
    else Result := false;
end;

procedure TZMLParser.WriteString(const ASection, AKey: string; AValue: string);
begin
  _Write(ASection, AKey, AValue);
end;

procedure TZMLParser.WriteInteger(const ASection, AKey: string; AValue: integer);
begin
  _Write(ASection, AKey, IntToStr(AValue));
end;

procedure TZMLParser.WriteBoolean(const ASection, AKey: string; AValue: boolean);
begin
  case AValue of
    true: _Write(ASection, AKey, 'true');
    false: _Write(ASection, AKey, 'false');
  end;
end;

procedure TZMLParser.UpdateZMLFile;
begin
  Data.SaveToFile(_ZMLFileName);
end;

destructor TZMLParser.Destroy;
begin
  inherited Destroy;
  Data.Free;
end;

end.


Что-то мне не нравится, как он работает, а на отладку просто нет времени!
Помогите, ибо на вас последняя надежда!

Автор: Vit 20.3.2005, 23:55
Почему XML подобный? Это самый настоящий XML - будет читаться стандартными парсерами

Автор: [EViL] 21.3.2005, 06:12
Нет, не будет. Смотри тогда как он должен выглядить, чтобы его читали НОРМАЛЬНЫЕ парсеры (типа IE):
Код

<xml>
  <graphics>    
    <width>1024</width>    
    <height>768</height>    
    <antialiasing>true</antialiasing>    
  </graphics>    
  <sound>    
    <volume>100</volume>    
  </sound>
</xml>


Помогите пофиксить в парсере (выше) процедуру записи!

Автор: Vit 21.3.2005, 06:47
Цитата
, 20.3.2005,  21:12]Нет, не будет. Смотри тогда как он должен выглядить, чтобы его читали НОРМАЛЬНЫЕ парсеры (типа IE):


Ну в общем то да, я заметил что не хватает общего тэга... Но проще считать файл, доставить <xml> в начало и в конец и загнать в стандартный парсер...

Автор: [EViL] 21.3.2005, 13:47
Ок, хорошо, допустим доствили <xml> в начало и конец.
Я не понимаю как пользоваться парсером (покажи пример хотя бы TXMLDocument, а то там всё очень сложно).

Автор: Vit 21.3.2005, 16:36
http://forum.sources.ru/index.php?showtopic=81572

Автор: <Spawn> 22.3.2005, 17:32
[EViL] Примерно так:

Код

uses XMLDoc, XMLDom ...;

procedure TForm1.Button1Click(Sender: TObject);
begin
  with TXMLDocument.Create(Self) do
  try
    LoadFromFile('FileName.xml');
    Active := True;
    ShowMessage(Format('Root node: name - %s, value - %s', [(DOMDocument as IDOMNode).nodeName, (DOMDocument as IDOMNode).nodeValue]));
    //Ну и далее все подветки являются типичным деревом - доступ к ним можешь получить так (DOMDocument as IDOMNode).childNodes[n-ое значение]
    //Приводить к IDOMNode не обязательно - я сделал лишь для того, чтобы показать DOMDocument представляет из себя корневой элемент, так как у тебя не полноценный XML файл то инструкций препроцессора(типа <?xml version="1.0"?>) в нем не будет, т.е. корень один
  finally
    Free;
  end;
end;

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)