Herzlich Willkommen, lieber Gast!
  Sie befinden sich hier:

  Forum » C / C++ (ANSI-Standard) » Key Tree aus Datei auslesen

Forum | Hilfe | Team | Links | Impressum | > Suche < | Mitglieder | Registrieren | Einloggen
  Quicklinks: MSDN-Online || STL || clib Reference Grundlagen || Literatur || E-Books || Zubehör || > F.A.Q. < || Downloads   

Autor Thread - Seiten: > 1 <
000
10.03.2008, 18:03 Uhr
LRS3link



Hallo zusammen, ich habe ein Problem mit einer Datei, die ich einlesen soll. Die Daten sind als Schlüssel-Wert Paare in einer Art Liste, dem key tree abgelegt. Wobei der Schlüssel immer ein String ist. Der key tree sieht dann so aus:

(.(."Keyname1",value1),("Keyname2",value2),("Keyname3",value3),......)

"Keyname" ist immer ein String variabler Länge, der durch " " begrenzt wird.

value kann entweder ein Integer, ein String unterschiedlicher Länge, ein weiterer key tree oder eine durch Kommas getrennte Liste in der Form (."Keyname",(value1, value2, value3,...) sein.

Die Namen der Schlüssel sind bekannt und es sind so um die 200 Stück, der key tree ist also recht groß.

Hat jemand von euch vielleicht eine Idee, wie ich die Daten sinnvoll einlesen kann, so dass ich auf den Wert, der zu einem bestimmten Schlüssel gehört, zugreifen kann ??

Vielen Dank schon mal für eure Antworten


PS: Ich benutze Visual C++ 6 falls das wichtig ist
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
001
10.03.2008, 21:51 Uhr
0xdeadbeef
Gott
(Operator)


Ich rate dir dringendst, dir einen echten C++-Compiler zuzulegen. MSVC6 wurde vor der Herausgabe des C++-Standards geschrieben und ist dementsprechend nicht immer in der Lage, standardkonformen Code zu übersetzen.

Prinzipiell willst du das ganze nachher logischerweise in einer Baumstruktur speichern, sinnigerweise wohl in einem B-Baum. Das könnte zum Beispiel so aussehen:

C++:
struct key_tree;

struct value_t {
  union {
    int n;
    std::string str;
    key_tree *kt;
    std::vector<value_t> ls;
  } val;

  enum type_t {
    int_id,
    string_id,
    key_tree_id;
    value_list_id;
  }

  type_t type;
};

struct key_tree {
  std::string key;
  value_t value;
};


Wenn dir da oben kein Tippfehler unterlaufen ist, wirst du dir wohl für die äußerste Klammer noch eine besondere Struktur bauen müssen - das sieht so mehr nach einem Wald als nach einem Baum aus - aber das sollte ja einfach sein.

Für die Umsetzung würde ich mir an deiner Stelle einen Parsergenerator schnappen, zum Beispiel Boost.Spirit, und den Baum/Wald dann in einer EBNF zu beschreiben. (Wobei MSVC6 nicht in der Lage ist, spirit zu übersetzen). Andere Möglichkeiten wären zum Beispiel bison oder yacc. Du kannst natürlich auch von Hand einen recursive descent parser schreiben, aber das ist immer ein elendes Gefummel.
--
Einfachheit ist Voraussetzung für Zuverlässigkeit.
-- Edsger Wybe Dijkstra
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
002
11.03.2008, 10:42 Uhr
LRS3link



Danke für die Antwort, der Grund weshalb ich VC++ 6 verwende ist der, dass aich auf bestehendem Code aufbauen muss und der eben mit VC++ 6 erstellt wurde, inklusive einiger nicht standardkonformen Ausdrücken
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
003
13.03.2008, 22:52 Uhr
0xdeadbeef
Gott
(Operator)


Eh, ich hab aus Spaß mal etwas in der Art zusammengehackt. Es handelt sich hierbei um eine einfache LL(2)-Grammatik, die aus einem String parst (Der Scanner sollte aber recht einfach auf eine Datei umschreibbar sein). An einigen Stellen hab ich aus Performance- und Speichersparsamkeitsgründen weniger saubere Techniken benutzt (deswegen sieht sie Speicherverwaltung so widerlich aus), aber als Anschauungsbeispiel sollte das eigentlich ganz gut hinhauen. Du wirst feststellen, dass der Code in der parser.cc einer EBNF-Grammatik recht ähnlich sieht, das ist kein Zufall. Also:

C++:
// keytree.hh

#ifndef INCLUDED_KEYTREE_HH
#define INCLUDED_KEYTREE_HH

#include <string>
#include <vector>

class parser;

class keytree;
class kt_value;

typedef std::vector<kt_value> kt_valuelist;

class kt_value {
public:
  enum type_id {
    keytree_id,
    valuelist_id,
    string_id,
    int_id
  };

  std::string  const &str () const { return *data_.str; }
  keytree      const &kt  () const { return *data_.kt ; }
  int                 x   () const { return  data_.x  ; }
  kt_valuelist const &vl  () const { return *data_.vl ; }      

  type_id             type() const { return  type_    ; }

  void destroy();

private:
  union {
    keytree      *kt;
    int           x;
    std::string  *str;
    kt_valuelist *vl;
  } data_;

  type_id type_;

  friend class parser;
};

class keytree {
public:
  std::string const &key  () const { return key_; }
  kt_value    const &value() const { return val_; }

  ~keytree();

private:
  std::string key_;
  kt_value val_;

  friend class parser;
};

#endif




C++:
// keytree.cc

#include "keytree.hh"

void kt_value::destroy() {
  switch(type_) {
  case keytree_id:
    delete data_.kt;
    break;
  case string_id:
    delete data_.str;
    break;
  case valuelist_id:
    for(kt_valuelist::iterator i = data_.vl->begin();
    i != data_.vl->end();
    ++i) {
      i->destroy();
    }
    delete data_.vl;
    break;
  default:
    ;
  }
}

keytree::~keytree() {
  val_.destroy();
}




C++:
// scanner.hh

#ifndef INCLUDED_SCANNER_HH
#define INCLUDED_SCANNER_HH

#include <string>

class scanner {
public:
  scanner(std::string const &data);

  char peek(unsigned lookahead = 0) const;
  void advance();

private:
  std::string data_;
  std::string::size_type pos_;
};

#endif




C++:
// scanner.cc

#include "scanner.hh"

#include <algorithm>
#include <string>
#include <stdexcept>

scanner::scanner(std::string const &data)
  : pos_(0)
{
  // Sanitize data

  bool in_string = false;

  data_.reserve(data.size() + 1);

  for(std::string::const_iterator i = data.begin();
      i != data.end();
      ++i) {
    if(*i == '\"') {
      in_string = !in_string;
    }

    if(in_string || *i != ' ') {
      data_.push_back(*i);
    }
  }
}

char scanner::peek(unsigned lookahead) const {
  if(pos_ + lookahead >= data_.size()) {
    throw std::runtime_error("Unexpected end of input.");
  }
  return data_[pos_ + lookahead];
}

void scanner::advance() {
  peek();
  ++pos_;
}




C++:
// parser.hh

#ifndef INCLUDED_PARSER_HH
#define INCLUDED_PARSER_HH

#include "scanner.hh"
#include "keytree.hh"

#include <string>
#include <vector>

class parser  {
public:
  parser(scanner const &scan);

  keytree *parse();

private:
  bool accept(char c);
  bool accept(bool (*cond)(char));
  void expect(char c);
  void expect(bool (*cond)(char));

  kt_value      parse_value    ();
  keytree      *parse_keytree  ();
  std::string   parse_string   ();
  int           parse_number   ();
  kt_valuelist *parse_valuelist();

  scanner scan_;
};

#endif




C++:
// parser.cc

#include "parser.hh"
#include "keytree.hh"
#include "scanner.hh"

#include <stdexcept>

namespace {
  bool is_number(char c) {
    return c >= '0' && c <= '9';
  }

  bool is_not_number(char c) {
    return !is_number(c);
  }
}

parser::parser(scanner const &scan) : scan_(scan) { }

keytree *parser::parse() {
  return parse_keytree();
}

bool parser::accept(char c) {
  if(c == scan_.peek()) {
    scan_.advance();
    return true;
  }
  
  return false;
}

bool parser::accept(bool (*cond)(char)) {
  if(cond(scan_.peek())) {
    scan_.advance();
    return true;
  }

  return false;
}

void parser::expect(char c) {
  if(!accept(c)) {
    throw std::runtime_error(std::string("unexpected token: \"") + scan_.peek() + std::string("\""));
  }
}

void parser::expect(bool (*cond)(char)) {
  if(!accept(cond)) {
    throw std::runtime_error(std::string("unexpected token: \"") + scan_.peek() + std::string("\""));
  }
}

keytree *parser::parse_keytree() {
  keytree *result;
  std::string key;
  kt_value val;

  expect('(');
  expect('.');

  key = parse_string();

  expect(',');

  val = parse_value();

  expect(')');

  result = new keytree;

  result->key_ = key;
  result->val_ = val;

  return result;
}

kt_value parser::parse_value() {
  kt_value v;

  if(scan_.peek() == '(') {
    if(scan_.peek(1) == '.') {
      v.type_    = kt_value::keytree_id;
      v.data_.kt = parse_keytree();
    } else {
      v.type_    = kt_value::valuelist_id;
      v.data_.vl = parse_valuelist();
    }
  } else if(scan_.peek() == '"') {
    v.type_     = kt_value::string_id;
    v.data_.str = new std::string(parse_string());
  } else if(scan_.peek() == '+' ||
        scan_.peek() == '-' ||
        ::is_number(scan_.peek())) {
    v.type_   = kt_value::int_id;
    v.data_.x = parse_number();
  }

  return v;
}

std::string parser::parse_string() {
  std::string str;

  expect('"');

  while(!accept('"')) {
    str += scan_.peek();
    scan_.advance();
  }

  return str;
}

int parser::parse_number() {
  int x, fac = 1;

  if(accept('-')) {
    fac = -1;
  } else {
    accept('+');
  }

  x = scan_.peek() - '0';

  expect(::is_number);

  while(is_number(scan_.peek())) {
    x *= 10;
    x += scan_.peek() - '0';
    scan_.advance();
  }

  return x;
}

kt_valuelist *parser::parse_valuelist() {
  kt_valuelist tmp, *result;

  expect('(');
  do {
    tmp.push_back(parse_value());    
  } while(accept(','));
  expect(')');

  result = new kt_valuelist;
  result->swap(tmp);

  return result;
}




C++:
// main.cc

#include "parser.hh"
#include "scanner.hh"
#include "keytree.hh"

#include <iostream>

void traverse_tree(keytree const &kt, unsigned depth = 0);

void print_value(kt_value const &val, unsigned depth) {
  std::string offset(depth * 2, ' ');

  switch(val.type()) {
  case kt_value::int_id:
    std::cout << offset << "  " << val.x() << std::endl;
    break;
  case kt_value::string_id:
    std::cout << offset << "  " << val.str() << std::endl;
    break;
  case kt_value::keytree_id:
    traverse_tree(val.kt(), depth + 1);
    break;
  case kt_value::valuelist_id:
    for(kt_valuelist::const_iterator i = val.vl().begin();
    i != val.vl().end();
    ++i) {
      print_value(*i, depth);
    }
    break;
  default:
    std::cout << offset << "  Unexpected." << std::endl;
  }
}

void traverse_tree(keytree const &kt, unsigned depth) {
  std::string offset(depth * 2, ' ');

  std::cout << offset << kt.key() << std::endl;
  print_value(kt.value(), depth);
}

int main() {
  parser p(scanner("(.\"foo bar\",(\"bar\", 2 ,(.\"baz\",123),4))"));
  keytree *kt = p.parse();

  traverse_tree(*kt);

  delete kt;
}


@FloSoft: Meinste, das wär was für einen FAQ-Eintrag? Fragen nach Parsern werden zwar nicht oft gestellt, aber die Source Corner ist leider unten...
--
Einfachheit ist Voraussetzung für Zuverlässigkeit.
-- Edsger Wybe Dijkstra

Dieser Post wurde am 13.03.2008 um 22:54 Uhr von 0xdeadbeef editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
004
14.03.2008, 17:01 Uhr
FloSoft
Medialer Over-Flow
(Administrator)


@0xdeadbeef: wär einer, wenn die frage erledigt ist kann man den ja mal verschieben ;-)
--
class God : public ChuckNorris { };
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
Seiten: > 1 <     [ C / C++ (ANSI-Standard) ]  


ThWBoard 2.73 FloSoft-Edition
© by Paul Baecher & Felix Gonschorek (www.thwboard.de)

Anpassungen des Forums
© by Flo-Soft (www.flo-soft.de)

Sie sind Besucher: