Malá hra o Velkého Bajta (2) - Gregoriánský kalendář

Místo pro dotazy a rady ohledně programovacích jazyků (C++, C#, PHP, ASP, Javascript, VBS..) a tvorby webových stránek

Moderátor: Mods_senior

Uživatelský avatar
faraon
Master Level 8.5
Master Level 8.5
Příspěvky: 7356
Registrován: prosinec 10
Pohlaví: Muž
Stav:
Offline

Malá hra o Velkého Bajta (2) - Gregoriánský kalendář

Příspěvekod faraon » 01 lis 2015 23:39

Všechny bity hry:

00000001 - Jednoduchá kalkulačka
00000010 - Odebírání zápalek
00000100 - Gregoriánský kalendář
00001000 - Hádání kombinace
00010000 - Vigenérova šifra
00100000 - Hledání min
01000000 - Házení kostkou
10000000 - Conway's Game of Life




Další měsíc utekl jako voda, nezadržitelně se na nás řítí konec starého roku a začátek nového, takže přišel čas položit si otázku:

A co je vlastně za den?

No přece první neděle v měsíci, takže je právě ta správná chvíle roztočit další kolo hry, tentokrát bit 00000100. A aby se nám ty neděle nepopletly, bude tentokrát úkolem naprogramovat kalendář. Ale ne jen tak nějaký obyčejný, měl by zvládat celkem tři funkce:

  1. Ta nejdůležitější funkce samozřejmě je vypočítat a přehledně zobrazit kalendář na zadaný rok, například ho vypsat do tabulky zformátované podle měsíců a dní v týdnu, asi jako na kapesním kalendáříku.
  2. Pro zadaný datum zjistit kolikátý den v roce to je. Například 1.1.2016 je v pořadí 1. den, a 31.12.2016 zase 366..
  3. Pro dvě zadaná data spočítat kolik dní mezi nimi uplynulo, například od vyhlášení minulého kola dodnes.
Pro třetí funkci se dá výhodně použít ta druhá, stačí se jenom trochu lépe podívat jak se vlastně ta uplynulá doba bude počítat.

Všechny údaje platí pro Gregoriánský kalendář, který většina evropských zemí přijala na konci šestnáctého století, takže pro jednoduchost stačí počítat až od roku 1600 a ty dřívější odmítnout jako chybu. Přepočítáváním na Juliánský nebo nějaký jiný starší kalendář se zabývat nebudeme, to by už byla příliš složitá matematika.

Na závěr ještě poznámka k přestupným rokům, to jsou ty dělitelné čtyřmi, s výjimkou staletí nedělitelných čtyřmi sty. Proto roky 1600 a 2000 byly přestupné, zatímco roky 1700, 1800 a 1900 nebyly, a rok 2100 také nebude, teprve až 2400.

Jako příklad jednoho z možných zobrazení dám výstup linuxového programu cal, který pro letošní rok vypíše tohle:

Kód: Vybrat vše

                               2015

        leden                  únor                  březen
Po Út St Čt Pá So Ne   Po Út St Čt Pá So Ne   Po Út St Čt Pá So Ne
          1  2  3  4                      1                      1
 5  6  7  8  9 10 11    2  3  4  5  6  7  8    2  3  4  5  6  7  8
12 13 14 15 16 17 18    9 10 11 12 13 14 15    9 10 11 12 13 14 15
19 20 21 22 23 24 25   16 17 18 19 20 21 22   16 17 18 19 20 21 22
26 27 28 29 30 31      23 24 25 26 27 28      23 24 25 26 27 28 29
                                              30 31
        duben                 květen                 červen
Po Út St Čt Pá So Ne   Po Út St Čt Pá So Ne   Po Út St Čt Pá So Ne
       1  2  3  4  5                1  2  3    1  2  3  4  5  6  7
 6  7  8  9 10 11 12    4  5  6  7  8  9 10    8  9 10 11 12 13 14
13 14 15 16 17 18 19   11 12 13 14 15 16 17   15 16 17 18 19 20 21
20 21 22 23 24 25 26   18 19 20 21 22 23 24   22 23 24 25 26 27 28
27 28 29 30            25 26 27 28 29 30 31   29 30

      červenec                 srpen                  září
Po Út St Čt Pá So Ne   Po Út St Čt Pá So Ne   Po Út St Čt Pá So Ne
       1  2  3  4  5                   1  2       1  2  3  4  5  6
 6  7  8  9 10 11 12    3  4  5  6  7  8  9    7  8  9 10 11 12 13
13 14 15 16 17 18 19   10 11 12 13 14 15 16   14 15 16 17 18 19 20
20 21 22 23 24 25 26   17 18 19 20 21 22 23   21 22 23 24 25 26 27
27 28 29 30 31         24 25 26 27 28 29 30   28 29 30
                       31
        říjen                listopad               prosinec
Po Út St Čt Pá So Ne   Po Út St Čt Pá So Ne   Po Út St Čt Pá So Ne
          1  2  3  4                      1       1  2  3  4  5  6
 5  6  7  8  9 10 11    2  3  4  5  6  7  8    7  8  9 10 11 12 13
12 13 14 15 16 17 18    9 10 11 12 13 14 15   14 15 16 17 18 19 20
19 20 21 22 23 24 25   16 17 18 19 20 21 22   21 22 23 24 25 26 27
26 27 28 29 30 31      23 24 25 26 27 28 29   28 29 30 31
Naposledy upravil(a) faraon dne 03 dub 2016 23:13, celkem upraveno 4 x.
"Král Lávra má dlouhé oslí uši, král je ušatec!

(pravil K. H. Borovský o cenzuře internetu)

Reklama
Denis V.
Level 3.5
Level 3.5
Příspěvky: 992
Registrován: květen 13
Pohlaví: Muž
Stav:
Offline

Re: Malá hra o Velkého Bajta (2)

Příspěvekod Denis V. » 02 lis 2015 13:33

Tak tady toto už je trošku moc na mě :D :)
Počkám až tady bude nějaké řešení a podívám se jak na to, poté to kdyžtak zkusím přepsat do C++ :)

Uživatelský avatar
faraon
Master Level 8.5
Master Level 8.5
Příspěvky: 7356
Registrován: prosinec 10
Pohlaví: Muž
Stav:
Offline

Re: Malá hra o Velkého Bajta (2)

Příspěvekod faraon » 02 lis 2015 17:49

Tak složité to zase není, na tu tabulku ti stačí vnořené cykly, a potřebuješ vědět jenom dvě věci:
  1. kterým dnem v týdnu rok začíná,
  2. jestli je rok přestupný, kvůli délce února.
Ta druhá se dá snadno několika podmínkami zjistit z letopočtu, a ta první z něj jen o něco málo složitěji vypočítat.
Ale kdybys nemohl přijít na to jak to spočítáš, pořád je tu možnost začít 1.1.1600, což byla sobota, a obyčejným cyklem dojít až k zadanému roku, přičítáním počtu dní v každém přeskočeném roce (k tomu zase potřebuješ vědět jestli je přestupný). Výsledek modulo sedmi ti dá opět den v týdnu... ;-)

Na zjištění kolikátý den v roce je zadaný datum opět potřebuješ jenom dvě věci:
  1. počet dní v jednotlivých měsících,
  2. je-li rok přestupný, kvůli délce února.
Takže je to vlastně stavebnice, tvořená několika malými kostičkami, které můžeš poskládat na několik různých způsobů, a tím získáš všechny tři požadované funkce programu.

Z toho vyplývá že nejdůležitější část programu bude tohle:

Kód: Vybrat vše

int prestupny(int rok)
    {
    if (rok%4)
       ...
    }

Touhle kostičkou můžeš začít, a stejným způsobem si vyrobíš i zbytek té stavebnice...
"Král Lávra má dlouhé oslí uši, král je ušatec!

(pravil K. H. Borovský o cenzuře internetu)

Rutherther
Level 2
Level 2
Příspěvky: 227
Registrován: říjen 14
Pohlaví: Muž
Stav:
Offline

Re: Malá hra o Velkého Bajta (2)

Příspěvekod Rutherther » 05 lis 2015 21:15

Tohle zadání se mi líbí nejvíce, zúčastním se také. :D

Stačí mi už jen vypsání a bude to hotové, jestli nemám někde chybu. :D

Chci se jen zeptat, jestli to mám správně, vychází mi, že když 1. 1. 1 je pondělí, tak 1. 1. 1600 je sobota, ale ve 2 kalendářích jsem našel další rozdílné dny, co je tedy správně? :D je 1. 1. 1 pondělí?

Uživatelský avatar
faraon
Master Level 8.5
Master Level 8.5
Příspěvky: 7356
Registrován: prosinec 10
Pohlaví: Muž
Stav:
Offline

Re: Malá hra o Velkého Bajta (2)

Příspěvekod faraon » 06 lis 2015 00:03

Ano, 1.1.1 vychází pondělí, ale protože se v té době používal Juliánský kalendář, tak ve skutečnosti to byl jiný den (odpovídá neděli 14.1). Jenže nebyl ani rok 1, křesťanský letopočet vznikl až o pár staletí později, takže to tenkrát nikoho nezajímalo.

Gregoriánský začal platit až od října 1582 a některým zemím jeho přijetí pár let tvalo (třeba Řecku až do dvacátého století!), proto je hranice pro program rok 1600. A víc než 9999 také asi nebude potřeba :)

Jinak kdyby někdo třeba chtěl dodělat ještě nějakou jinou vlastní funkčnost, třeba rozpis sousedů na mytí schodů v paneláku, klidně může. S kalendářem se dají dělat různé zajímavé věci, tak proč se trochu nepobavit...
"Král Lávra má dlouhé oslí uši, král je ušatec!

(pravil K. H. Borovský o cenzuře internetu)

Rutherther
Level 2
Level 2
Příspěvky: 227
Registrován: říjen 14
Pohlaví: Muž
Stav:
Offline

Re: Malá hra o Velkého Bajta (2)

Příspěvekod Rutherther » 07 lis 2015 18:29

Tak úkol jsem vyřešil a posílám zdroják. :D

Edit: v 22:57 jsem opravil jednu chybku, v nějakých měsících se neukazovala data v 6. týdnu
Edit 2: Omlouvám se, včera jsem si trošku upravoval funkci getFirstDayOfMonth a díky tomu se ukazovaly špatně dny, např. leden končil pondělím a únor začínal středou atd. Posílám snad už verzi bez chyb :lol:
PS takovéhle chyby normálně nedělam :smile:

Kód: Vybrat vše

  #include<stdio.h>
  #include<stdlib.h>
  #include<string.h>
  int isLeapYear(int year){
   
    if(year % 100 == 0)
      return year % 400 == 0;
   
    return year % 4 == 0;
  }
 
  int getLeapYearsToYear(int year){
    int leapYears = (year - 1) / 4;
   
    for(int i = 100; i < year; i += 100){
      if(i % 400 != 0)
   leapYears--;
    }
    return leapYears;
  }
 
  int getDaysInMonth(int leap, int month){
    if(month == 1)
      return 28 + leap;
    int x = 0;
    if(month > 6)
      x = 1;
    return (month + 1) % 2 == x ? 30 : 31;
  }
 
    int* getDateFromString(char *date){
      char* dayStr;
      char* monthStr;
      char* yearStr;
      int day, month, year;
     
      char dateCopy[strlen(date)];
      strncpy(dateCopy, date, strlen(date));
     
      int dots = 0;
      for(int i = 0; i < strlen(date); i++){
     
   if(date[i] == '.')
     dots++;
   
      }
      if(dots != 2){
   day = month = year = 10000;
      }
      else {
      dayStr = strtok(dateCopy, ".");
      monthStr = strtok(NULL, ".");
      yearStr = strtok(NULL, ".");
      day = atoi(dayStr);
      month = atoi(monthStr);
      year = atoi(yearStr);
      }
      int* realDate = (int*)malloc(3 * sizeof(int));
      realDate[0] = day;
      realDate[1] = month;
      realDate[2] = year;
      return realDate;
  }
  int getDateSequenceInYear(int day, int month, int leap){
    int days = day;
    month--;
    for(int i = 0; i < month; i++){
      days += getDaysInMonth(leap, i);
    }
   
    return days;
  }
 
  int getDateSequence(int day, int month, int year){
    int days = getDateSequenceInYear(day, month, isLeapYear(year));
    days += year * 365 + getLeapYearsToYear(year);
    return days;
  }
 
  int getDateSequenceFromString(char *date){
    int *realDate = getDateFromString(date);
    if(realDate[0] < 1 || realDate[0] > getDaysInMonth(isLeapYear(realDate[2]), realDate[1] - 1)){
      printf("Zadaný den neexistuje.\n");
      return -1;
    } else if(realDate[1] < 1 || realDate[1] > 12){
      printf("Zadaný měsíc neexistuje.\n");
      return -1;
    } else if(realDate[2] < 1600 || realDate[2] > 9999){
      printf("Zadaný rok není podporován.\n");
      return -1;
    }
    return getDateSequence(realDate[0], realDate[1], realDate[2]);
  }
 
  int getDateSequenceInYearFromString(char* date){
    if(getDateSequenceFromString(date) == -1)
      return -1;
    int *realDate = getDateFromString(date);
   
    int days = realDate[0];
    int leap = isLeapYear(realDate[2]);
    for(int i = 0; i < realDate[1] - 1; i++){
      days += getDaysInMonth(leap, i);
    }
    return getDateSequenceInYear(realDate[0], realDate[1], isLeapYear(realDate[2]));
  }
 
  char* getDayCode(int dayInWeek){
    switch(dayInWeek){
      case 0:
   return "Po";
      case 1:
   return "Út";
      case 2:
   return "St";
      case 3:
   return "Čt";
      case 4:
   return "Pá";
      case 5:
   return "So";
      case 6:
   return "Ne";
     
    }
    return "Un";
  }
 
  char* getMonth(int month){
    switch(month){
      case 0:
   return "leden";
      case 1:
   return "únor";
      case 2:
   return "březen";
      case 3:
   return "duben";
      case 4:
   return "květen";
      case 5:
   return "červen";
      case 6:
   return "červenec";
      case 7:
   return "srpen";
      case 8:
   return "září";
      case 9:
   return "říjen";
      case 10:
   return "listopad";
      case 11:
   return "prosinec";
    }
    return "neznámý";
   
   
  }
    char* getMonthWithoutUni(int month){
    switch(month){
      case 0:
   return "leden";
      case 1:
   return "unor";
      case 2:
   return "brezen";
      case 3:
   return "duben";
      case 4:
   return "kveten";
      case 5:
   return "cerven";
      case 6:
   return "cervenec";
      case 7:
   return "srpen";
      case 8:
   return "zari";
      case 9:
   return "rijen";
      case 10:
   return "listopad";
      case 11:
   return "prosinec";
    }
    return "neznámý";
   
   
  }
  int getFirstDayOfMonth(int year, int month){
   
    int full, module;
   
    full = getDateSequence(0, month + 1, year) - 1;
   
    module = full % 7;
   
    return module;
  }
 
  int getDifferenceOfTwoDates(char *date1, char *date2){
    int seq1 = getDateSequenceFromString(date1), seq2 = getDateSequenceFromString(date2);
    if(seq1 == -1 || seq2 == -1)
      return -1;
    int difference = seq1 - seq2;
    return difference > 0 ? difference : -difference;
   
  }
 
 
 
  void printVoid(int count){
    for(int i = 0; i < count; i++){
      printf(" ");
    }
  }
 
  int getCenter(int max, int length){
    return max / 2 - length / 2;
  }
 
  int printDays(int startColumn, int endColumn, int startDay, int daysOffset){
 
    for(int column = 0; column < 7; column++){
      if(column < startColumn || column > endColumn){
   printVoid(daysOffset + 2 - (column == 6 ? 1 : 0));
   continue;
      }
   
      printf("%s%d", startDay + column - startColumn < 10 ? " " : "", startDay + column - startColumn); 
      if(column != 6)
   printVoid(daysOffset);
    }
    int count = endColumn - startColumn + 1;
    return count < 8 ? count : 7;
  }
 
  void printDayCodes(int daysOffset){
    for(int day = 0; day < 7; day++){
      printf("%s", getDayCode(day));
      if(day != 6)
   printVoid(daysOffset);
    }
  }
 
  void printMonths(int from, int to, int daysOffset, int betweenMonthDaysOffset){
    if(from > to)
      from = to;
    int monthDaysLength = 14 + daysOffset * 6;
    for(int i = from; i <= to; i++){
      char* month = getMonth(i);
      int monthLength = strlen(getMonthWithoutUni(i));
      int center = getCenter(monthDaysLength, monthLength);
      printVoid(center);
      printf("%s", month);
      if(i != to)
   printVoid(betweenMonthDaysOffset + monthDaysLength - (center + monthLength));

    }
  }
 
  void printAll(int year, int monthsOnRow){
    char yearStr[4];
    sprintf(yearStr, "%d", year);
    int rows = 12 / monthsOnRow + (12 % monthsOnRow!= 0 ? 1 : 0),
      dayOffset = 1,
      betweenMonthDaysOffset = 3,
      yearOffset = getCenter(monthsOnRow * (14 + dayOffset * 6 + betweenMonthDaysOffset) - betweenMonthDaysOffset, strlen(yearStr));
   
    printVoid(yearOffset);
    printf("%d\n\n", year);
   
    int firstDay[12];
    for(int i = 0; i < 12; i++){
      firstDay[i] = getFirstDayOfMonth(year, i);
    }
   
    for(int row = 0; row < rows; row++){     
      printMonths(row * monthsOnRow, monthsOnRow * (row + 1) - 1, dayOffset, betweenMonthDaysOffset);
      printf("\n");
      int nextDay[monthsOnRow];
   for(int i = 0; i < monthsOnRow; i ++)
     nextDay[i] = 0;
      for(int x = 0; x < 8; x++){
   for(int month = 0; month < monthsOnRow; month++){
     if(x == 0){
       printDayCodes(dayOffset);
       printVoid(betweenMonthDaysOffset);
       continue;
     }
      
       int printed, leap = isLeapYear(year), currentMonth = month + row * monthsOnRow;
       if(nextDay[month] > getDaysInMonth(leap, currentMonth)){
         printDays(7, 7, 0, dayOffset);
              printVoid(betweenMonthDaysOffset);
              continue;
       }
       if(nextDay[month] == 0){
         nextDay[month]++;
         printed = printDays(firstDay[currentMonth], 6, nextDay[month], dayOffset);
       }    
       else if(nextDay[month] + 7 >= getDaysInMonth(leap, currentMonth)){
         printed = printDays(0, getDaysInMonth(leap, currentMonth)- nextDay[month], nextDay[month], dayOffset);
       }
       else
         printed = printDays(0, 6, nextDay[month], dayOffset);
       nextDay[month] += printed;
       printVoid(betweenMonthDaysOffset);
     }
     printf("\n");
      }
      printf("\n");
    }
   
   
    //        leden                  únor
    //Po Út St Čt Pá So Ne   Po Út St Čt Pá So Ne   Po Út St Čt Pá So Ne
    // 1  2  3  4  5  6  7             1  2  3  4             1  2  3  4
    // 8  9 10 11 12 13 14    5  6  7  8  9 10 11    5  6  7  8  9 10 11
    //15 16 17 18 19 20 21   12 13 14 15 16 17 18   12 13 14 15 16 17 18
    //22 23 24 25 26 27 28   19 20 21 22 23 24 25   19 20 21 22 23 24 25
    //29 30 31               26 27 28               26 27 28 29 30 31
   
  }

  void printCalendary(){
    printf("Zadej rok ");
    int year;
    scanf("%d", &year);
    if(year < 1600 || year > 9999){
      printf("Musíš zadat rok mezi 1 599 a 10 000\n");
      printCalendary();
      return;
    }
    printAll(year, 3);
  }
 
  void getDifference(){
    char date1[10];
    char date2[10];
    printf("Zadej datum ve formátu (den.měsíc.rok) ");
    scanf("%s", &date1);
    printf("Zadej datum ve formátu (den.měsíc.rok) ");
    scanf("%s", &date2);
    printf("Zjistím rozdíl dat %s a %s\n", &date1, &date2);
    int diff = getDifferenceOfTwoDates(date1, date2);
    if(diff == -1){
      getDifference();
      return;
    }
     
    printf("Mezi těmito daty uběhlo %d dní.\n", diff);
  }
 
  void getSequence(){
    char date[10];
    printf("Zadej datum ve formátu (den.měsíc.rok) ");
    scanf("%s", &date);
    printf("Zjistím, kolikátý den v roce je v datu %s\n", &date);
    int sequence = getDateSequenceInYearFromString(date);
    if(sequence == -1){
      getSequence();
      return;
    }
    printf("Byl zadán %d. den v roce.\n", sequence);
  }
 
  int main(){
    printf("%d", getDaysInMonth(0, 7));
    printf("Jakou funkci chceš využít? Zadej číslo.\n");
   
    printf("0 - Pro zadaný rok vypsat kalendář.\n");
   
    printf("1 - Pro zadaná data zjistit, kolik mezi nimi uběhlo dnů.\n");
   
    printf("2 - Pro zadané datum zjistí, kolikátý den je to v pořádí v roce.\n");
    int func = 0;
    scanf("%d", &func);
    if(func < 0 || func > 3){
      printf("Musíš zadat číslo od 0 do 2.\n");
      return main();
    }
     
    if(func == 0)
      printCalendary();
    else if(func == 1)
      getDifference();
    else if(func == 2)
      getSequence();
     
    return 0;
  }

Psal jsem to v C, ale nejsem v něm zase tak zkušený, tak prosím omluvte, jestli se něco dalo vyřešit jednodušeji.

Uživatelský avatar
faraon
Master Level 8.5
Master Level 8.5
Příspěvky: 7356
Registrován: prosinec 10
Pohlaví: Muž
Stav:
Offline

Re: Malá hra o Velkého Bajta (2)

Příspěvekod faraon » 08 lis 2015 12:06

Teda to bylo errorů, také jsi mohl zmínit že to mám kompilovat jako C99 :lol:
Potom už vyskočilo jenom pár warningů, ale Céčko si naštěstí nechá docela dost líbit, takže to tentokrát funguje i s nimi:

Kód: Vybrat vše

kalendar2.c: In function ‘getDifference’:
kalendar2.c:343:5: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘char (*)[10]’ [-Wformat]
kalendar2.c:345:5: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘char (*)[10]’ [-Wformat]
kalendar2.c:346:5: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘char (*)[10]’ [-Wformat]
kalendar2.c:346:5: warning: format ‘%s’ expects argument of type ‘char *’, but argument 3 has type ‘char (*)[10]’ [-Wformat]
kalendar2.c: In function ‘getSequence’:
kalendar2.c:359:5: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘char (*)[10]’ [-Wformat]
kalendar2.c:360:5: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘char (*)[10]’ [-Wformat]

Máš trochu zmatek v ukazatelích, název pole je ukazatel na jeho první prvek, takže ukazatel na pole je ukazatel na ukazatel, což by mohlo v jiném případě skončit zajímavými vedlejšími efekty...

Něco dalšího tam ale funguje špatně, narazil jsem na tohle:

Kód: Vybrat vše

Jakou funkci chceš využít? Zadej číslo.
0 - Pro zadaný rok vypsat kalendář.
1 - Pro zadaná data zjistit, kolik mezi nimi uběhlo dnů.
2 - Pro zadané datum zjistí, kolikátý den je to v pořádí v roce.
1         
Zadej datum ve formátu (den.měsíc.rok) 31.12.9999
Zadej datum ve formátu (den.měsíc.rok) 1.1.1600
Zjistím rozdíl dat 31.12.9999 a 1.1.1600
Zadaný rok není podporován.
Zadej datum ve formátu (den.měsíc.rok) 1.1.1600
Zadej datum ve formátu (den.měsíc.rok) 31.12.9999
Zjistím rozdíl dat  a 31.12.9999
Zadaný den neexistuje.

Nefunguje to i pro jiné roky, začátek ledna a konec prosince to prostě odmítá, funkce 2 tahle data normálně zpracuje.
"Král Lávra má dlouhé oslí uši, král je ušatec!

(pravil K. H. Borovský o cenzuře internetu)

Rutherther
Level 2
Level 2
Příspěvky: 227
Registrován: říjen 14
Pohlaví: Muž
Stav:
Offline

Re: Malá hra o Velkého Bajta (2)

Příspěvekod Rutherther » 08 lis 2015 12:34

Dobře, pokusím se ty chyby opravit, s těmi ukazateli to zase tolik neumím, asi jsem to nějak popletl, no :D
Díky za hodnocení

Dodatečně přidáno po 11 minutách 5 vteřinách:
Zvláštní, že mně to 1.1.1600 a 31.12.9999 bere :D

Uživatelský avatar
faraon
Master Level 8.5
Master Level 8.5
Příspěvky: 7356
Registrován: prosinec 10
Pohlaví: Muž
Stav:
Offline

Re: Malá hra o Velkého Bajta (2)

Příspěvekod faraon » 08 lis 2015 17:17

Rutherther píše:Zvláštní, že mně to 1.1.1600 a 31.12.9999 bere :D

Tak já to schválně zkusím otrasovat, abych zjistil ve kterém okamžiku se ta data změní nebo co se s nimi vlastně děje. Mám GCC 4.7.2.
"Král Lávra má dlouhé oslí uši, král je ušatec!

(pravil K. H. Borovský o cenzuře internetu)

Rutherther
Level 2
Level 2
Příspěvky: 227
Registrován: říjen 14
Pohlaví: Muž
Stav:
Offline

Re: Malá hra o Velkého Bajta (2)

Příspěvekod Rutherther » 08 lis 2015 17:57

Já mám GCC 4.9.2, tam možná bude ten problém, třeba tam nějaká chybka byla, ale je to vážně zvláštní, protože to bere jako špatné dny menší než 1

Dodatečně přidáno po 16 minutách 54 vteřinách:
Ještě dodam, mně to nepsalo ani jeden warning :D

Edit: na netbooku mám taky gcc 4.7.2 a tam mi to jde taky

Uživatelský avatar
faraon
Master Level 8.5
Master Level 8.5
Příspěvky: 7356
Registrován: prosinec 10
Pohlaví: Muž
Stav:
Offline

Re: Malá hra o Velkého Bajta (2)

Příspěvekod faraon » 08 lis 2015 19:37

Warningy to píše jenom když jsou vážné, těmi nedůležitými normálně neotravuje. Proto kompiluji takhle, ať se GCC pěkně vykecá:
gcc -std=c99 -Wall -pedantic ...

No, není úplně bezpečné používat scanf() pro načítání řetězce, vlastně je to pěkná blbost, protože nekontroluje přetečení pole. Takže máš:

Kód: Vybrat vše

char date1[10];
char date2[10];

A kolik znaků má "31.12.2015" včetně ukončujícího '\0'? Nemluvě o tom když tam nějaký BFU (Brain Free User) mezi to iniciativně nadělá mezery, aby to ten počítač mohl lépe přečíst :lol:
Mrkni se na funkci fgets(), ta to řeší.

Dynamická alokace ve funkci vracející pole je fajn, ale neměl bys tu paměť zase někde uvolnit? Céčko nemá Garbage Collector. Tady to sice spácháš jenom jednou, jenže někdy v budoucnu třeba budeš řídit databázi se stovkami milonů položek (což není zase tak moc) běžící 365/24, a při každé operaci s datumem sežereš další 24 bajty paměti (ono to kvůli segmentaci bude pravděpodobně ještě víc)... To jsem se před časem dozvěděl pár důvěrných informací o tom jak je naprogramovaný ten slavný registr vozidel, a naštěstí jsem nebyl zrovna po jídle, jinak bych asi zvracel! Tady by stačilo mít to tříprvkové pole ve volající funkci, a klidně i staticky, aby se ti nemuselo pokaždé dynamicky alokovat na zásobníku.

Zatím to dělá tohle:

Kód: Vybrat vše

Zadej datum ve formátu (den.měsíc.rok) Zadej datum ve formátu (den.měsíc.rok) Zjistím rozdíl dat 31.12.2015 a 1.1.2016
31.12.2015
1.2.2016
_getDifferenceOfTwoDates_31.12.2015_1.2.2016_
_getDateSequenceFromString_31_12_2015_
_getDateSequenceFromString_1_2_201615_
Zadaný rok není podporován.
_getDifferenceOfTwoDates_736328_-1_

Tohle je ten problém, už tušíš kde je zrada? Já jsem se v getDateFromString() trochu ztratil (ty tvoje názvy funkcí jsou fakt něco strašného, a na odsazování bys také měl ještě trochu zapracovat). Vzpomněl jsem si přitom na jeden starý fór:

- Kolik programátorů ve Windows je potřeba na výměnu žárovky?
- 472. Jeden napíše WinGetLightBulbHandle, jeden napíše WinQueryStatusLightBulb, jeden napíše WinGetLightSwitchHandle...


Aha, to mi řekni proč ten řetězec kopíruješ? A ještě navíc voláš strlen() na třech místech na jednu věc, dokonce v cyklu! On by si to sice měl kompilátor zoptimalizovat, ale nespoléhej na to. Jedna proměnná navíc tě nezabije, když si tu délku do ní zjistíš jednou ;-)

Tak tohle se mi fakt nelíbí, co s tím ten strtok() vyvádí?

Kód: Vybrat vše

_getDifferenceOfTwoDates_31.12.2015_1.2.2016_
_getDateFromString_date_31.12.2015_
_dateCopy_31.12.2015_
_getDateFromString_date_31.12.2015_
_dateCopy_31.12.2015_
_getDateFromString_date_31.12.2015_
_dateCopy_31_
_getDateFromString_date_31.12.2015_strlen_10_dateCopy_31_dayStr_31_day_31_monthStr_12_month_12_yearStr_2015_year_2015_
_getDateSequenceFromString_31_12_2015_
_getDateFromString_date_1.2.2016_
_dateCopy_1.2.201615_
_getDateFromString_date_1.2.2016_
_dateCopy_1.2.201615_
_getDateFromString_date_1.2.2016_
_dateCopy_1_
_getDateFromString_date_1.2.2016_strlen_8_dateCopy_1_dayStr_1_day_1_monthStr_2_month_2_yearStr_201615_year_201615_
_getDateSequenceFromString_1_2_201615_

Druhý řetězec je kratší než první, takže za ním zůstane kousek minulého letopočtu. RTFM: http://linux.die.net/man/3/strncpy
"The strncpy() function is similar, except that at most n bytes of src are copied. Warning: If there is no null byte among the first n bytes of src, the string placed in dest will not be null-terminated."

Takže tady bych naopak doporučil funkci sscanf(), protože ta umí při načítání čísel a znaků docela zajímavé věci (stejné jako scanf()). Vlastně bys mohl celé to načítání do řetězců a vnořování do mnoha závisejících funkcí nahradit dvěma cykly se scanf() (a ušetřit tak 200 řádků kódu), a stačí k tomu udělat jedinou věc, kontrolovat návratovou hodnotu:

Kód: Vybrat vše

#include <stdio.h>

int main(void)
    {
    int den,mesic,rok;
    char z,datum[100];

    printf("Zadej datum ve tvaru d.m.r:\n");
    while (fgets(datum,99,stdin),
           5 != sscanf(datum, "%d %1[.,-/] %d %1[.,-/] %d", &den, &z, &mesic, &z, &rok))
          {
          printf("Špatný formát!\n");
          }
    printf("%d.%d.%d\n",den,mesic,rok);

    return 0;
    }

Ta podivnost "%1[.,-/]" znamená že se načte jeden ze zadaných znaků nebo nic, stejnou fintu jsem použil i v kalkulačce, akorát že tam je to trochu víc schované :twisted:

Vyzkoušej co to provede u tebe, tohle je s těmi kontrolními výpisy:

Kód: Vybrat vše

    int* getDateFromString(char *date){
      char* dayStr;
      char* monthStr;
      char* yearStr;
      int day, month, year;
     
      char dateCopy[strlen(date)];
      strncpy(dateCopy, date, strlen(date));
printf("_getDateFromString_date_%s_\n_dateCopy_%s_\n",date,dateCopy);
     
      int dots = 0;
      for(int i = 0; i < strlen(date); i++){
     
   if(date[i] == '.')
     dots++;
   
      }
      if(dots != 2){
   day = month = year = 10000;
      }
      else {
printf("_getDateFromString_date_%s_\n_dateCopy_%s_\n",date,dateCopy);
      dayStr = strtok(dateCopy, ".");
      monthStr = strtok(NULL, ".");
      yearStr = strtok(NULL, ".");
printf("_getDateFromString_date_%s_\n_dateCopy_%s_\n",date,dateCopy);
      day = atoi(dayStr);
      month = atoi(monthStr);
      year = atoi(yearStr);
printf("_getDateFromString_date_%s_strlen_%d_dateCopy_%s_dayStr_%s_day_%d_monthStr_%s_month_%d_yearStr_%s_year_%d_\n",date,strlen(date),dateCopy,dayStr,day,monthStr,month,yearStr,year);
      }
      int* realDate = (int*)malloc(3 * sizeof(int));
      realDate[0] = day;
      realDate[1] = month;
      realDate[2] = year;
      return realDate;
  }
"Král Lávra má dlouhé oslí uši, král je ušatec!

(pravil K. H. Borovský o cenzuře internetu)

Rutherther
Level 2
Level 2
Příspěvky: 227
Registrován: říjen 14
Pohlaví: Muž
Stav:
Offline

Re: Malá hra o Velkého Bajta (2)

Příspěvekod Rutherther » 08 lis 2015 20:21

faraon píše:Warningy to píše jenom když jsou vážné, těmi nedůležitými normálně neotravuje. Proto kompiluji takhle, ať se GCC pěkně vykecá:
gcc -std=c99 -Wall -pedantic ...

No, není úplně bezpečné používat scanf() pro načítání řetězce, vlastně je to pěkná blbost, protože nekontroluje přetečení pole. Takže máš:

Kód: Vybrat vše

char date1[10];
char date2[10];

A kolik znaků má "31.12.2015" včetně ukončujícího '\0'? Nemluvě o tom když tam nějaký BFU (Brain Free User) mezi to iniciativně nadělá mezery, aby to ten počítač mohl lépe přečíst :lol:
Mrkni se na funkci fgets(), ta to řeší.


Určitě se kouknu, na \0 jsem zapomněl, ale jinak jsem to chtěl mít tak, že právě i 31.12.9999 je 10 znaků (když jsem zapomněl na \0) a proto jsem to tam dal takhle, taky jsem si myslel, že když ten řetězec bude delší než 10, prostě ho to neuloží a bude to tomu jedno, ale to asi špatně. S mezerami jsem počítal, scanf mezery takto nebere, takže jsem to bral tak, že když dá mezeru, napíše mu to, že den neexxistuje, což je podle mne dostačující

faraon píše:Dynamická alokace ve funkci vracející pole je fajn, ale neměl bys tu paměť zase někde uvolnit? Céčko nemá Garbage Collector. Tady to sice spácháš jenom jednou, jenže někdy v budoucnu třeba budeš řídit databázi se stovkami milonů položek (což není zase tak moc) běžící 365/24, a při každé operaci s datumem sežereš další 24 bajty paměti (ono to kvůli segmentaci bude pravděpodobně ještě víc)... To jsem se před časem dozvěděl pár důvěrných informací o tom jak je naprogramovaný ten slavný registr vozidel, a naštěstí jsem nebyl zrovna po jídle, jinak bych asi zvracel! Tady by stačilo mít to tříprvkové pole ve volající funkci, a klidně i staticky, aby se ti nemuselo pokaždé dynamicky alokovat na zásobníku.

Zatím to dělá tohle:

Kód: Vybrat vše

Zadej datum ve formátu (den.měsíc.rok) Zadej datum ve formátu (den.měsíc.rok) Zjistím rozdíl dat 31.12.2015 a 1.1.2016
31.12.2015
1.2.2016
_getDifferenceOfTwoDates_31.12.2015_1.2.2016_
_getDateSequenceFromString_31_12_2015_
_getDateSequenceFromString_1_2_201615_
Zadaný rok není podporován.
_getDifferenceOfTwoDates_736328_-1_

Tohle je ten problém, už tušíš kde je zrada? Já jsem se v getDateFromString() trochu ztratil (ty tvoje názvy funkcí jsou fakt něco strašného, a na odsazování bys také měl ještě trochu zapracovat). Vzpomněl jsem si přitom na jeden starý fór:

Na to uvolnění paměti se kouknu, to mi trošku nedošlo, asi jsem měl více číst
Jo, už tuším, díky :D
Nejsem si úplně jistý, proč jsem se to rozhodl psát v C, ale jeden z důvodů bude asi ten, že jsem doufal v to, že se nějak takto někdo rozepíše a tím mi napíše chyby, kterých se v C dopouštím. Názvy funkcí jsou strašné možná jen jednotlivá slova, ne např. getDateSequence je trochu divné, ale nevěděl jsem, čím nahradit to slovo "Sequence"? A k tomu odsazování, nedělám moc v C, ale když v něm dělám, tak dělám v editoru Kate a nejsem na něj moc zvyklý, mohl jsem si na to odsazování dávat více pozor, no, poučení pro příště, když budu chtít, aby se někdo v kódu lépe orientoval, budu psát lepší názvy funkcí a lépe odsazovat :lol: nejlepší asi bude, když pro příště zůstanu u českých názvů.

faraon píše:- Kolik programátorů ve Windows je potřeba na výměnu žárovky?
- 472. Jeden napíše WinGetLightBulbHandle, jeden napíše WinQueryStatusLightBulb, jeden napíše WinGetLightSwitchHandle...


Aha, to mi řekni proč ten řetězec kopíruješ? A ještě navíc voláš strlen() na třech místech na jednu věc, dokonce v cyklu! On by si to sice měl kompilátor zoptimalizovat, ale nespoléhej na to. Jedna proměnná navíc tě nezabije, když si tu délku do ní zjistíš jednou ;-)

Tak tohle se mi fakt nelíbí, co s tím ten strtok() vyvádí?

Kód: Vybrat vše

_getDifferenceOfTwoDates_31.12.2015_1.2.2016_
_getDateFromString_date_31.12.2015_
_dateCopy_31.12.2015_
_getDateFromString_date_31.12.2015_
_dateCopy_31.12.2015_
_getDateFromString_date_31.12.2015_
_dateCopy_31_
_getDateFromString_date_31.12.2015_strlen_10_dateCopy_31_dayStr_31_day_31_monthStr_12_month_12_yearStr_2015_year_2015_
_getDateSequenceFromString_31_12_2015_
_getDateFromString_date_1.2.2016_
_dateCopy_1.2.201615_
_getDateFromString_date_1.2.2016_
_dateCopy_1.2.201615_
_getDateFromString_date_1.2.2016_
_dateCopy_1_
_getDateFromString_date_1.2.2016_strlen_8_dateCopy_1_dayStr_1_day_1_monthStr_2_month_2_yearStr_201615_year_201615_
_getDateSequenceFromString_1_2_201615_


Pravda, to kopírování je tam docela na nic, má chyba, zapomněl jsem na to, když jsem zjišťoval, co mi neoprávněně přistupuje do paměti, pak jsem to tam nechal

Jak myslíš to, co s tím strtok vyvádí? mně se to zdá docela v pořádku, kde je podle tebe chyba?

faraon píše:Druhý řetězec je kratší než první, takže za ním zůstane kousek minulého letopočtu. RTFM: http://linux.die.net/man/3/strncpy
"The strncpy() function is similar, except that at most n bytes of src are copied. Warning: If there is no null byte among the first n bytes of src, the string placed in dest will not be null-terminated."

Takže tady bych naopak doporučil funkci sscanf(), protože ta umí při načítání čísel a znaků docela zajímavé věci (stejné jako scanf()). Vlastně bys mohl celé to načítání do řetězců a vnořování do mnoha závisejících funkcí nahradit dvěma cykly se scanf() (a ušetřit tak 200 řádků kódu), a stačí k tomu udělat jedinou věc, kontrolovat návratovou hodnotu:

Kód: Vybrat vše

#include <stdio.h>

int main(void)
    {
    int den,mesic,rok;
    char z,datum[100];

    printf("Zadej datum ve tvaru d.m.r:\n");
    while (fgets(datum,99,stdin),
           5 != sscanf(datum, "%d %1[.,-/] %d %1[.,-/] %d", &den, &z, &mesic, &z, &rok))
          {
          printf("Špatný formát!\n");
          }
    printf("%d.%d.%d\n",den,mesic,rok);

    return 0;
    }

Ta podivnost "%1[.,-/]" znamená že se načte jeden ze zadaných znaků nebo nic, stejnou fintu jsem použil i v kalkulačce, akorát že tam je to trochu víc schované :twisted:

Na něco podobného jsem narazil, když jsem hledal něco o scanf, určitě se mi to bude hodit do budoucna, díky.

faraon píše:Vyzkoušej co to provede u tebe, tohle je s těmi kontrolními výpisy:

Kód: Vybrat vše

    int* getDateFromString(char *date){
      char* dayStr;
      char* monthStr;
      char* yearStr;
      int day, month, year;
     
      char dateCopy[strlen(date)];
      strncpy(dateCopy, date, strlen(date));
printf("_getDateFromString_date_%s_\n_dateCopy_%s_\n",date,dateCopy);
     
      int dots = 0;
      for(int i = 0; i < strlen(date); i++){
     
   if(date[i] == '.')
     dots++;
   
      }
      if(dots != 2){
   day = month = year = 10000;
      }
      else {
printf("_getDateFromString_date_%s_\n_dateCopy_%s_\n",date,dateCopy);
      dayStr = strtok(dateCopy, ".");
      monthStr = strtok(NULL, ".");
      yearStr = strtok(NULL, ".");
printf("_getDateFromString_date_%s_\n_dateCopy_%s_\n",date,dateCopy);
      day = atoi(dayStr);
      month = atoi(monthStr);
      year = atoi(yearStr);
printf("_getDateFromString_date_%s_strlen_%d_dateCopy_%s_dayStr_%s_day_%d_monthStr_%s_month_%d_yearStr_%s_year_%d_\n",date,strlen(date),dateCopy,dayStr,day,monthStr,month,yearStr,year);
      }
      int* realDate = (int*)malloc(3 * sizeof(int));
      realDate[0] = day;
      realDate[1] = month;
      realDate[2] = year;
      return realDate;
  }


Tak tady je výpis:

Kód: Vybrat vše


Zadej datum ve formátu (den.měsíc.rok) 1.1.1600
Zadej datum ve formátu (den.měsíc.rok) 31.12.9999
Zjistím rozdíl dat 1.1.1600 a 31.12.9999
_getDateFromString_date_1.1.1600_
_dateCopy_1.1.1600_
_getDateFromString_date_1.1.1600_
_dateCopy_1.1.1600_
_getDateFromString_date_1.1.1600_
_dateCopy_1_
_getDateFromString_date_1.1.1600_strlen_8_dateCopy_1_dayStr_1_day_1_monthStr_1_month_1_yearStr_1600_year_1600_
_getDateFromString_date_31.12.9999_
_dateCopy_31.12.9999_
_getDateFromString_date_31.12.9999_
_dateCopy_31.12.9999_
_getDateFromString_date_31.12.9999_
_dateCopy_31_
_getDateFromString_date_31.12.9999_strlen_10_dateCopy_31_dayStr_31_day_31_monthStr_12_month_12_yearStr_9999_year_9999_
Mezi těmito daty uběhlo 3068036 dní.


Díky za tohle hodnocení, alespoň vím, co dělám špatně a mohu se poučit pro příště. :)


  • Mohlo by vás zajímat
    Odpovědi
    Zobrazení
    Poslední příspěvek
  • Malá sestava do 30 tisíc
    od kupjec » 20 zář 2023 21:24 » v Rady s výběrem hw a sestavením PC
    14
    2184
    od kupjec Zobrazit poslední příspěvek
    25 zář 2023 11:26
  • Výběr skříně (zakoupená je malá) Příloha(y)
    od DaveMove » 09 čer 2023 15:35 » v Problémy s hardwarem
    8
    790
    od meda2016 Zobrazit poslední příspěvek
    09 čer 2023 20:27

Zpět na “Programování a tvorba webu”

Kdo je online

Uživatelé prohlížející si toto fórum: Žádní registrovaní uživatelé a 4 hosti