Properties - pliki tekstowe

Koziołek

1 Wstęp
2 Pliki .properties
3 Ładowanie plików .properties
4 Zapisywanie do plików .properties
5 Podsumowanie

Wstęp

Zapewne każdy z was spotkał się z tym problemem. Problemem, który dręczył pokolenia informatyków i zapewne będzie dręczył kolejne. Gdzie należy trzymać konfigurację potrzebną do prawidłowego działania programu.
Pierwszym i najbardziej naturalnym postępowaniem jest "zaszycie" konfiguracji w kodzie. Jest to najgorsza z możliwych metod. Do jej głównych wad należą:

  • trudność wprowadzania zmian. Zmiana wymaga ponownej kompilacji kodu
  • możliwa redundancja kodu
    Jednak ma ona jedną zaletę. Jest bardzo szybka na etapie implementacji oraz bardzo bezpieczna. Zazwyczaj używa się jej tam gdzie:
  • nie ma obsługi plików
  • są bardzo duże ograniczenia w zakresie wykorzystania pamięci / szybkości procesora
    Drugą znacznie bardziej poprawną metodą jest przeniesienie konfiguracji do osobnego pliku. W ten sposób uzyskujemy bardzo dużą elastyczność i możliwość zmiany ustawień bez potrzeby ingerencji w kod. W języku Java istnieją dwie główne metody tworzenia konfiguracji za pomocą plików. Pierwsza z nich to wykorzystanie dokumentów XML. Druga, i tą się tu zajmiemy dokładnie, to wykorzystanie obiektu <font name="Courier New" size="2">Properties</span>.

Pliki .properties

Podstawą działania obiektu Properties są pliki tekstowe. Zgodnie z konwencją mają one rozszerzenie <font name="Courier New" size="2">.properties</span>. Ich zawartość to pary << klucz >>=<< wartość >> każda para w osobnej i pojedynczej linii. Ostatnie oznacza iż wartość nie zależnie jak długa musi znajdować się w jednej linii. Przykład pliku:

# komentarz
klucz=wartość

Istotną cechy to

  • plik powinien być zapisany w ISO-8859-1
  • należy unikać polskich znaków w nazwach kluczy
  • klucze muszą być unikalne

Jak widać kodowanie powoduje że nie będziemy mogli w prosty sposób używać polskich znaków. Rozwiązaniem jest zamiana polskich znaków na kody UTF-8:

  1. Ą - \u0104; ą - \u0105
  2. Ć - \u0106; ć - \u0107
  3. Ę - \u0118; ę - \u0119
  4. Ł - \u0141; ł - \u0142
  5. Ń - \u0143; ń - \u0144
  6. Ó - \u00d3; ó - \u00f3
  7. Ś - \u015a; ś - \u015b
  8. Ź - \u0179; ź - \u017a
  9. Ż - \u017b; ż - \u017c

Ładowanie plików .properties

Jak mamy już utworzony nasz plik .properties to czas załadować go do obiektu. W tym celu musimy:

  • utworzyć strumień wejściowy <font name="Courier New" size="2">InputStream</span>
  • utworzyć obiekt <font name="Courier New" size="2">Properties</span>
  • załadować dane ze strumienia wejścia do obiektu <font name="Courier New" size="2">Properties</span>

Dodatkowo wypiszemy jego zawartość.
I oto klasa to realizująca:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
 
public class PropertiesTest {
    //Plik z konfiguracją
    private File f = new File("conf.properties");
    //przyszły obiekt Properties
    private Properties properties = new Properties();
 
    public static void main(String[] args) {
        System.setProperty("file.encoding", "UTF-8");
        PropertiesTest pt = new PropertiesTest();
        pt.loadProperties();
        System.out.println(pt.properties.getProperty("klucz"));
    }
 
    public void loadProperties(){
        //Strumień wejściowy
        InputStream is;
        try {
            is = new FileInputStream(f);
            //ładujemy nasze ustawienia
            properties.load(is);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Jak widać jest to bardzo proste.

Zapisywanie do plików .properties

Została jeszcze jedna kwestia do poruszenia. Jak zapisywać zmienione ustawienia do plików? Obiekt <font name="Courier New" size="2">Properties</span> posiada dwie metody, które pozwalają na tą operację.

  • <font name="Courier New" size="2">save(FileOutputStream, String)</span> - oznaczoną jako przestarzała
  • <font name="Courier New" size="2">store(FileOutputStream, String)</span> - zalecaną

Pierwszy argument to strumień wyjścia do którego zostaną zapisane dane. Drugi to komentarz. Tu istotna uwaga jeżeli podamy komentarz jako pusty to zostanie do pliku wstawiona data utworzenia i pusta linia. Jeżeli komentarz będzie równy null to wstawiona zostanie tylko data utworzenia.
Na koniec dodatkowa metoda zapisująca nasze ustawienia:

public void saveProperties(String key, String value){
    OutputStream os;
    try {
        os = new FileOutputStream(f);
        properties.setProperty(key, value);
        properties.store(os, null);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
}

Podsumowanie

Pliki .properties są podstawowym sposobem zapisu danych konfiguracyjnych. Ich istotną wadą jest konieczność stosowania kodowania ISO-8859-1 co utrudnia stosowanie polskich znaków.

5 komentarzy

@Olamagato, mówisz o zupełnie innym mechanizmie i zupełnie innym API. Zgadzam się, ze nowszym, ale Properties API nadal jest szeroko używane.

Artykuł w całości zdezaktualizowany przez istniejącą od Javy 1.4 klasę java.util.prefs.Preferences, która właśnie służy do niezależnego od platformy i języka trzymania swoich ustawień. Ustawienia można przechowywać w danych prywatnych konta użytkownika lub w danych systemowych (dla wszystkich kont wspólnie). Rozwiązuje to więc całkowicie problem z kodowaniem polskich znaków. Jedynym ograniczeniem jest limit 8 KB danych, który jak na dane konfiguracyjne jest spory.

Wyrzucone zostanie wtedy FNFE, które zostanie wyłapane przez catch(IOE...) a printStackTrace wypisze nam przyczynę (brak dostępu). Sprawdziłem ;-)

Niekoniecznie. Jeżeli plik istnieje, a nie masz praw do jego odczytu to IOE nie powie ci tego, a FNFE już tak.

Świetny artykuł! Tylko jedna uwaga - w tym przypadku (skoro nie obsługujemy błędów) nie jest niezbędne wyłapywanie wyjątków FileNotFoundException, wystarczy IOException. Pozdrawiam.