Nowe posty

xx Dystrybucja pod HP Omen (6)
2024-03-27, 23:30:08
xx [Poradnik] Wyszukiwanie Sterowników (2)
2024-03-27, 21:08:23
lamp Problem z Linux Lite po instalacji (0)
2024-03-27, 19:50:30
xx Ile pingwinów? (1)
2024-03-27, 08:59:24
xx konfiguracja pale moon (0)
2024-03-24, 21:53:42
xx Plasma 6 w Neonie ssie trochę mniej ... (10)
2024-03-23, 02:38:11
xx problem z instalacja sterowników do karty sieciowej (3)
2024-03-18, 18:10:16
xx Plik abc.001 (1)
2024-03-17, 17:48:27
xx Zlecę dopracowanie programu w MatLab (0)
2024-03-13, 15:28:40
xx Linux Mint 21.3 XFCE brak dźwieku po paru minutach (karta muzyczna zintegrowana) (5)
2024-03-12, 23:07:01

Autor Wątek: wczytywanie pliku konfiguracyjnego  (Przeczytany 6378 razy)

msuma

  • Gość
wczytywanie pliku konfiguracyjnego
« dnia: 2010-01-22, 13:39:46 »
Piszę program, który ma wczytywać ustawienia z pliku konfiguracyjnego. Chodzi głównie o deskryptory urządzeń, które są różne na poszczególnych komputerach.
Program otwiera plik i wczytuje kolejno wiersze:
void wczytaj_config()
{
FILE *filepointer;
filepointer = fopen("config.cfg","r");
char *wiersz;
if(filepointer == NULL)
{
puts("Nie można otworzyć pliku z ustawieniami");
/* tutaj przypisać wartości domyślne */
strcpy(keyboard_descriptor, "/dev/input/event5");
strcpy(joystick_descriptor, "/dev/input/js0");

comport_relayboard= 16;
comport_sk= 0;
}

else
{printf("[OTWARCIE PLIKU CONFIG]\\n");
while (fgets(wiersz,80,filepointer) != NULL)
{podziel(wiersz);}

fclose(filepointer);
printf("[ZAMKNIĘCIE PLIKU CONFIG]\\n");
}
}
Następnie (jeśli wiersz zawiera znak równości i nie zaczyna się od hasza) funkcja podziel dzieli wczytane wiersze na nazwę zmiennej (na lewo od znaku równości) i wartość zmiennej (na prawo od znaku równości):
int podziel(char* wiersz)
{  
int i= 0;
int dzielto = 0;
while (wiersz[i]!= '\\n')
{    
if (wiersz[i] == '=')
{dzielto= 1;}
i++;
/* jeśli w wierszu występuje znak równości */
}

if (wiersz[0] == '#')
{dzielto= 0;
/* jeśli wiersz NIE zaczyna się od hasza */
}  
  if (dzielto== 1)
  {
  char* zmienna= strtok (wiersz,"=");
  char* wartosc= strtok (NULL, "=");
przytnij(wartosc);
printf ("zmienna:<%s>, wartosc:<%s>\\n",zmienna, wartosc);
przypisz(zmienna, wartosc);
}

else
{printf ("niedziel: %s", wiersz);}
}
Funkcja przytnij obcina znak przejścia do nowego wiersza na końcu wartości oraz spacje na początku (jeśli występują).
void przytnij(char* wartosc)
{
 if (wartosc[0] == ' ')
{/* sprawdza czy na początku jest spacja */
  while (wartosc[0] == ' ')
  {/* obcina pierwszy znak i przesuwa całość w lewo dopóki nie trafi na pierwszy znak inny niż spacja */
  int i= 0;
  while (wartosc[i]!= '\\n')
  {
  wartosc[i]= wartosc[i+1];
  i++;
  }
  wartosc[i-1]= '\\0';
}
}
else
{
int i= 0;
while (wartosc[i]!= '\\n')
  {i++;}
  wartosc[i]= '\\0';
/* usuwa znak końca wiersza */
}
}
Potem można wydrukować obok siebie nazwę zmiennej i jej wartość:
printf ("zmienna:<%s>, wartosc:<%s>\\n",zmienna, wartosc);
Niestety problem pojawia się w momencie przypisywania wartości pobranych z pliku odpowiednim zmiennym. Ordynarne porównanie niestety nie działa, co objaśniono tutaj:
www.linuxforums.org/forum/linux-programming-scripting/126727-fscanf-read-configuration-files-c.html
if (zmienna == "deskryptor_klawiatury")
{keyboard_descriptor= wartosc;}
Natomiat kiedy używam funkcji strcmp porównującej ciągi znaków ma miejsce inne zjawisko:
if (strcmp(zmienna,"deskryptor_klawiatury")==0)
{keyboard_descriptor= wartosc;}
Program troszkę się zagalopowuje i kończy działanie z fragmentem ostatniej linijki pliku przypisanym jako wartość zmiennej.
Czy ktoś ma jakiś pomysł? Wydaje mi się że problem leży w ostatnim fragmencie. Do linijki wyświetlającej obok siebie nazwę i wartość zmiennej wszystko jest w porządku. Problemy zaczynają się przy przypisywaniu wartości.

Cały projekt można pobrać na forum.ubuntu.pl. Nie śmiejcie się ze sposobu przechwytywania zdarzeń klawiatury. Docelowo będzie to coś bardziej cywilizowanego.

chmooreck

  • Gość
wczytywanie pliku konfiguracyjnego
« Odpowiedź #1 dnia: 2010-01-22, 14:45:36 »
Pierwszy błąd, jaki znalazłem jest w wywołaniu fgets. Dane należy pobrać do swojego bufora, a nie 'nie wiadomo gdzie'.

Poza tym zacznij czytać manuale do funkcji, których używasz. Jeśli w manualu jest napisane, żeby nie używać, to na pewno jest tego jakiś powód. ;-)

Na ostrzeżenia kompilatora tez dobrze jest zwracać uwagę...

msuma

  • Gość
wczytywanie pliku konfiguracyjnego
« Odpowiedź #2 dnia: 2010-01-23, 00:15:17 »
Znalazłem rozwiązanie. Okazało się że zdefiniowałem zmienne przechowujące ścieżki deskryptorów jako zmienne wskaźnikowe:
char* keyboard_descriptor;
char* joystick_descriptor;
a powinienem jako ciągi znaków:
char keyboard_descriptor[20];
char joystick_descriptor[20];
Po naprawieniu tego błędu kompilator przypomniał mi że nie mogę ot tak sobie:
keyboard_descriptor= "/dev/input/event5";
joystick_descriptor= "/dev/js0";
przypisać zmiennym typu char[20] wartości typu char*.

Musiałem więc zmienić przypisania z takich:
if (strcmp(zmienna,"deskryptor_klawiatury")==0)
{keyboard_descriptor= wartosc;}

if (strcmp(zmienna,"deskryptor_joysticka")==0)
{joystick_descriptor= wartosc;}
na takie:
if (strcmp(zmienna,"deskryptor_klawiatury")==0)
{strcpy(keyboard_descriptor, wartosc);}

if (strcmp(zmienna,"deskryptor_joysticka")==0)
{strcpy(joystick_descriptor, wartosc);}
Funkcja strcmp porównuje czy ciągi znaków są takie same. Powtarzana przy każdym wczytaniu nowego wiersza znajduje zmienne użyte w programie w tekście pliku konfiguracyjnego. Następnie funkcja strcpy przypisuje zmiennej (powyżej na przykładzie keyboard_descriptor i joystick_descriptor) wartość pobraną z pliku konfiguracyjnego.
Cały program przechwytuje zdarzenia joysticka i klawiatury (wymaga uruchomienia jako administrator). Następnie odpowiednio wychyla ramię serwomechanizmów sterowanych przez serwokontroler SK18 lub steruje obwodami elektrycznymi za pośrednictwem karty przekaźników. Ścieżki do deskryptorów tych urządzeń i inne parametry wczytuje z pliku konfiguracyjnego.

chmooreck

  • Gość
wczytywanie pliku konfiguracyjnego
« Odpowiedź #3 dnia: 2010-01-23, 21:46:35 »
mam nadzieje, że wczytywanie tez poprawiłeś...
zamiast strcpy polecam używanie strncpy

msuma

  • Gość
wczytywanie pliku konfiguracyjnego
« Odpowiedź #4 dnia: 2010-03-16, 16:11:02 »
Zamiast fgets dałem funkcję getline. Dziękuję chmooreckowi za cenne rady. Zamieszczam kod programu i przykładowy plik konfiguracyjny, może się komuś przyda. Program otwiera plik, wczytuje kolejno wiersze i przypisuje wartości zmiennych łańcuchowych, znakowych i liczbowych (typu int).
#include 
#include
#include

char zmienna_string[20];
char zmienna_char;
int zmienna_int;
Funkcja podziel dzieli wczytane wiersze na nazwę funkcji i jej wartość.
void podziel(char* wiersz)
{  
int i= 0;
int dzielto = 0;
while (wiersz[i]!= '\\n')
  {    
  if (wiersz[i] == '=')
  {dzielto= 1;}
  i++;
  /* jeśli w wierszu występuje znak równości */
  }

if (wiersz[0] == '#')
  {dzielto= 0;
  /* jeśli wiersz NIE zaczyna się od hasza */
  }
 
if (dzielto== 1)
  {/* jeśli obydwa warunki są spełnione to funkcja dzieli wczytaną linię na nazwę zmiennej i jej wartość */
  char* zmienna= strtok (wiersz,"=");
  char* wartosc= strtok (NULL, "=");
  przytnij(wartosc);
  przypisz(zmienna, wartosc);
  }

  else
  {printf ("%s", wiersz);}/* jeśli we wczytanej linii niema znaku równości lub zaczyna się ona od hasza to jest jedynie wyświetlana */
}
Funkcja przytnij usuwa spacje i znaki tabulacji występujące przed wartością zmiennej.
void przytnij(char* wartosc)
{/* obcina spacje i znaki tabulacji na początku ciągu znaków */
if (wartosc[0] == ' ' || wartosc[0] == '\\t')
{/* jeśli pierwszym znakiem ciągu jest spacja lub znak tabulacji */
  while (wartosc[0] == ' ' || wartosc[0] == '\\t')
  {/* powtarza zagnieżdżoną pętlę dopóki nie napotka znaku innego niż spacja lub znak tabulacji */
  int i= 0;
      while (wartosc[i]!= '\\n')
      {
      wartosc[i]= wartosc[i+1];/* przypisuje i-temu znakowi wartość znaku i+1 przesuwając wiersz w lewo */
      i++;
      }
  wartosc[i-1]= '\\0';
  /* zamyka ciąg znaków */
  }
}

else
{
  int i= 0;
  while (wartosc[i]!= '\\n')
  {i++;}
  wartosc[i]= '\\0';
  /* zamyka ciąg znaków */
}
}
Funkcja przypisz przypisuje zmiennej wartość wczytaną z pliku.
void przypisz(char* zmienna, char* wartosc)
/* przypisuje zmiennej występującej w pliku konfiguracyjnym pod nazwą "zmienna" odpowiednią wartość */
/* nazwa zmiennej w kodzie programu może być identyczna z tą w pliku lub różnić się */
{
/* sprawdza kolejno czy wczytana z pliku nazwa zmiennej jest identyczna z nazwą
którejś ze zmiennych, których wartości ma przypisywać */

if (strcmp(zmienna,"zmienna_lancuchowa")==0) /* czy zmienna z pliku ma nazwę zmienna_lancuchowa */
{printf ("?");
 /* UWAGA! znak "ptaszka" wyświetlają się poprawnie tylko gdy terminal ma ustawione kodowanie UTF-8 */
 strcpy(zmienna_string, wartosc); /* przypisuje zmiennej wartość z pliku */
 printf (" %s %s\\n", zmienna, zmienna_string);} /* wyświetla nazwę zmiennej z pliku */
/*else {printf ("nie przypisuje wartości zmiennej łańcuchowej\\n");}*/

if (strcmp(zmienna,"zmienna_znakowa")==0)
{printf ("?");
 zmienna_char= wartosc[0];/* zmienna znakowa przyjmuje wartość pierwszego znaku na prawo od znaku równości (innego niż spacja czy znak tabulacji) */
 printf (" %s %c", zmienna, zmienna_char); /* wyświetla deskryptor oraz jego numer */
    if( strlen(wartosc) > 1)
    {printf (" (Uwaga! wartość w pliku ma długość %hd znaków!)", strlen(wartosc));}
 /* program ostrzega jeśli wartość zmiennej w pliku jest dłuższa niż jeden znak */
 printf ("\\n");
 /* i dopiero wtedy przechodzi do nowego wiersza */
}
/*else {printf ("nie przypisuję karty wartości zmiennej znakowej\\n");}*/

if (strcmp(zmienna,"liczba_calkowita")==0)
{printf ("?");
 zmienna_int = atoi(wartosc); /* funkcja atoi z biblioteki stdlib zamienia ciąg znaków składający się z cyfr na liczbę typu int */
 printf (" %s %hd\\n", zmienna, zmienna_int); /* wyświetla deskryptor oraz jego numer */
}
/*else {printf ("nie przypisuję wartości zmiennej liczbowej\\n");}*/


/* tutaj można wstawić przypisywanie kolejnych wartości zmiennym */
}
Funkcja wczytaj_config otwiera plik i wczytuje kolejne wiersze lub przypisuje zmiennym wartości domyślne jeśli pliku niema w katalogu z którego został uruchomiony program.
void wczytaj_config()
{
FILE *filepointer;
filepointer = fopen("./config.cfg","r");

if(filepointer == NULL)
  {
  puts("? Nie można otworzyć pliku z ustawieniami!\\nPrzypisywanie wartości domyślnych.");
  /* jeśli w katalogu z którego został uruchomiony program niema pliku konfiguracyjnego */
  /* to program przypisuje zmiennym wartości domyślne */
 
  strcpy(zmienna_string, "domyślna wartość");
  /* przypisywanie wartości zmiennej łańcuchowej */
  zmienna_char= 'N';
  /* przypisywanie wartości zmiennej typu char */
  zmienna_int= 16;
  /* przypisywanie wartości zmiennej typu int */
  }

else
  {
  printf("Otwarcie pliku z ustawieniami\\n");
  char * wiersz = NULL;
  size_t rozmiar= 0;
  ssize_t bufor;

    while ((bufor= getline(&wiersz, &rozmiar, filepointer)) != -1)
    {
    podziel(wiersz);/* wczytuje kolejne linie i wywołuje funkcję podziel */
    }
  fclose(filepointer);
  printf("Zamknięcie pliku z ustawieniami\\n");
  }
}
Przykładowa funkcja main:
int main()
{wczytaj_config();
return 0;}
Przykładowy plik config.cfg:
zmienna_lancuchowa=	/home/msuma
zmienna_znakowa= T
liczba_calkowita= 666

# po znaku równości mogą być spacje lub znaki tabulacji
# wszystko na prawo od ostatniego ostępu jest wartością
# puste linie, linie nie zawierające znaku "="
# oraz linie zaczynające się od "#" są tylko wyświetlane
# jeśli wartość zmiennej znakowej jest dłuższa niż jeden znak
# to przypisywany jest tylko pierwszy znak