Каков наилучший способ разбора этого файла конфигурации? - PullRequest
4 голосов
/ 17 августа 2010

Я работаю над личным проектом, который использует пользовательский файл конфигурации. Основной формат файла выглядит так:

[users]
name: bob
attributes:
    hat: brown
    shirt: black
another_section:
    key: value
    key2: value2

name: sally
sex: female
attributes:
    pants: yellow
    shirt: red

Может быть произвольное количество пользователей, и у каждого могут быть разные пары ключ / значение, и в разделе могут быть вложенные ключи / значения с использованием табуляции. Я знаю, что я могу использовать json, yaml или даже xml для этого конфигурационного файла, однако сейчас я бы хотел сохранить его на заказ.

Синтаксический анализ не должен быть сложным, поскольку я уже написал код для его анализа. Мой вопрос заключается в том, как лучше всего выполнить синтаксический анализ этого кода, используя clean и структурированный код, а также писать так, чтобы в будущем не было затруднений ( может быть несколько гнезд в будущем). Прямо сейчас мой код выглядит крайне отвратительно. Например,

private void parseDocument() {  
    String current;
    while((current = reader.readLine()) != null) {
        if(current.equals("") || current.startsWith("#")) {
            continue; //comment
        } 
        else if(current.startsWith("[users]")) {
            parseUsers();
        }
        else if(current.startsWith("[backgrounds]")) {
            parseBackgrounds();
        }
    }
}

private void parseUsers()  {        
    String current;
    while((current = reader.readLine()) != null) {
        if(current.startsWith("attributes:")) {
            while((current = reader.readLine()) != null) {
                if(current.startsWith("\t")) {
                    //add user key/values to User object
                }
                else if(current.startsWith("another_section:")) {
                    while((current = reader.readLine()) != null) {
                        if(current.startsWith("\t")) {
                            //add user key/values to new User object
                        } 
                        else if (current.equals("")) {
                            //newline means that a new user is up to parse next
                        }
                    }
                }
            }
        }
        else if(!current.isEmpty()) {
            //
        }


    }
}

Как видите, код довольно грязный, и я сократил его для презентации здесь. Я чувствую, что есть и лучшие способы сделать это, возможно, не используя BufferedReader. Может ли кто-нибудь, возможно, предложить лучший способ или подход, который не так запутан, как мой?

Ответы [ 6 ]

6 голосов
/ 17 августа 2010

Я бы предложил не создавать пользовательский код для конфигурационных файлов.То, что вы предлагаете, не слишком далеко от YAML ( начало работы ).Используйте это вместо этого.

См. Какую библиотеку Java YAML мне следует использовать?

3 голосов
/ 17 августа 2010

Каждый будет рекомендовать использовать XML, потому что он просто лучше.

Однако, если вы стремитесь доказать ценность своего программиста для себя ...

... в коде, который вы выложили, нет ничего действительно плохого в том смысле, что он понятен и понятен потенциальным читателям, что происходит, и если я не выхожу из цикла над файловыми операциями, он должен выполнить почти так же хорошо, как мог.

Единственная критика, которую я могу предложить, это то, что она не рекурсивна. Каждый уровень требует нового уровня кода для поддержки. Я бы, вероятно, сделал бы рекурсивную функцию (функцию, которая вызывает себя с суб-контентом в качестве параметра, а затем снова, если есть суб-суб-контент и т. Д.), Который можно вызывать, читая весь этот материал в хеш-таблицу с хеш-таблицами или чем-то еще и затем я использовал бы эту хеш-таблицу в качестве объекта конфигурации.

Опять же, в этот момент я, вероятно, перестал бы видеть точку и использовал бы XML. ;)

2 голосов
/ 17 августа 2010

Это выглядит достаточно просто для конечного автомата.

while((current = reader.readLine()) != null) {
  if(current.startsWith("[users]"))
    state = PARSE_USER;
  else if(current.startsWith("[backgrounds]"))
    state = PARSE_BACKGROUND;
  else if (current.equals("")) {
    // Store the user or background that you've been building up if you have one.
    switch(state) {
      case PARSE_USER:
      case USER_ATTRIBUTES:
      case USER_OTHER_ATTRIBUTES:
        state = PARSE_USER;
        break;
      case PARSE_BACKGROUND:
      case BACKGROUND_ATTRIBUTES:
      case BACKGROUND_OTHER_ATTRIBUTES:
        state = PARSE_BACKGROUND;
        break;
    }
  } else switch(state) {
    case PARSE_USER:
    case USER_ATTRIBUTES:
    case USER_OTHER_ATTRIBUTES:
      if(current.startsWith("attributes:"))
        state = USER_ATTRIBUTES;
      else if(current.startsWith("another_section:"))
        state = USER_OTHER_ATTRIBUTES;
      else {
        // Split the line into key/value and store into user
        // object being built up as appropriate based on state.
      }
      break;
    case PARSE_BACKGROUND:
    case BACKGROUND_ATTRIBUTES:
    case BACKGROUND_OTHER_ATTRIBUTES:
      if(current.startsWith("attributes:"))
        state = BACKGROUND_ATTRIBUTES;
      else if(current.startsWith("another_section:"))
        state = BACKGROUND_OTHER_ATTRIBUTES;
      else {
        // Split the line into key/value and store into background
        // object being built up as appropriate based on state.
      }
      break;
  }
}
// If you have an unstored object, store it.
2 голосов
/ 17 августа 2010

Если бы вы могли использовать XML, JSON или другую широко известную кодировку данных в качестве формата данных, вам будет намного проще анализировать / десериализовать текстовое содержимое и извлекать значения.Например.

name: bob
attributes:
    hat: brown
    shirt: black
another_section:
    key: value
    key2: value2

Может быть выражен как следующий XML (есть и другие варианты для его выражения в XML)

<config>
  <User hat="brown" shirt="black" >
    <another_section>
      <key>value</key>
      <key2>value</key2>
    </another_section>
  </User>
</config>

Пользовательский (Чрезвычайно простой) Как я уже упоминал в комментарии ниже, вы можете просто сделать их все пары имя и значение.например,

name                 :bob
attributes_hat       :brown
attributes_shirt     :black
another_section_key  :value
another_section_key2 :value2

, а затем выполнить разбиение строки на '\ n' (новая строка) и ':', чтобы извлечь ключ и значение или создать словарь / объект карты.

1 голос
/ 17 августа 2010

Хороший способ убрать это - использовать таблицу, то есть заменить ваши условные выражения на карту. Затем вы можете вызывать методы анализа с помощью рефлексии (просто) или создать еще несколько классов, реализующих общий интерфейс (больше работы, но более надежно).

1 голос
/ 17 августа 2010

Я бы рекомендовал изменить формат файла конфигурации на JSON и использовать существующую библиотеку для анализа таких объектов JSON, как FlexJSON .

{
"users": [
    {
        "name": "bob",
        "hat": "brown",
        "shirt": "black",
        "another_section": {
            "key": "value",
            "key2": "value2" 
        } 
    },
    {
        "name": "sally",
        "sex": "female",
        "another_section": {
            "pants": "yellow",
            "shirt": "red" 
        } 
    } 
] 

}

...