Skocz do zawartości
ElektronPL_WiTu

Biblioteka SDFat - polskie znaki w nazwach plików

Pomocna odpowiedź

Witam, chcę, aby zegarek jaki tworzę mógł otwierać pliki/foldery z polskimi znakami. Już trochę udało mi się zrobić, ale niestety doszedłem do bariery, której jak na razie nie jestem wstanie pokonać. Zacznę od tego, że domyśle biblioteka SDFat (mowa o funkcji GetName) wszystkie znaki powyżej numeru 127 zamienia na '_' i teraz w czym największy problem - gdy wyświetlę listę plików i wybiorę taki z polskimi literami to zamiast np. "jaźń.txt" będzie "ja__.txt" i teraz jeśli będę chciał go otworzyć to mi się to nie uda bo biblioteka nie znajdzie na karcie pliku "ja__.txt" bo on się tak nie nazywa.

Mi się udało doprowadzić że funkcja GetName zwraca nazwę pliku z polskimi znakami. Na początku sprawdziłem jakie numery daje każdy polski znak. Udało mi się ustalić coś takiego:

ć = 263
ł = 322
ś = 347
ó = 243
ą = 261
ę = 281
ź = 378
ż = 380
ń = 324

Ć = 262
Ł = 321
Ś = 346
Ó = 211
Ą = 260
Ę = 280
Ź = 377
ż = 379
Ń = 323

Te numery są większe niż 255 bo znaki mają 16 bitów a nie 8 - i to jest pierwszy problem, który można prosto rozwiązać mianowicie w funkcji GetName zrobiłem wykrywanie tych wartości i zamiana ich na standardowe dla ASCII znaki. Dzięki temu na liście plików wyświetlane są poprawnie polskie znaki. 

Oczywiście nie rozwiązuje to problemu z funkcją open i próbowałem to rozwiązać, niestety bezskutecznie.

Oto funkcja open:

bool FatFile::open(FatFile* dirFile, fname_t* fname, oflag_t oflag) {
  bool fnameFound = false;
  uint8_t lfnOrd = 0;
  uint8_t freeNeed;
  uint8_t freeFound = 0;
  uint8_t ord = 0;
  uint8_t chksum = 0;
  uint16_t freeIndex = 0;
  uint16_t curIndex;
  dir_t* dir;
  ldir_t* ldir;
  size_t len = fname->len;

  if (!dirFile->isDir() || isOpen()) {
    DBG_FAIL_MACRO;
    goto fail;
  }
  // Number of directory entries needed.
  freeNeed = fname->flags & FNAME_FLAG_NEED_LFN ? 1 + (len + 12)/13 : 1;

  dirFile->rewind();
  while (1) {
    curIndex = dirFile->m_curPosition/32;
    dir = dirFile->readDirCache(true);
    if (!dir) {
      if (dirFile->getError()) {
        DBG_FAIL_MACRO;
        goto fail;
      }
      // At EOF
      goto create;
    }
    if (dir->name[0] == DIR_NAME_DELETED || dir->name[0] == DIR_NAME_FREE) {
      if (freeFound == 0) {
        freeIndex = curIndex;
      }
      if (freeFound < freeNeed) {
        freeFound++;
      }
      if (dir->name[0] == DIR_NAME_FREE) {
        goto create;
      }
    } else {
      if (freeFound < freeNeed) {
        freeFound = 0;
      }
    }
    // skip empty slot or '.' or '..'
    if (dir->name[0] == DIR_NAME_DELETED || dir->name[0] == '.') {
      lfnOrd = 0;
    } else if (DIR_IS_LONG_NAME(dir)) {
      ldir_t *ldir = reinterpret_cast<ldir_t*>(dir);
      if (!lfnOrd) {
        if ((ldir->ord & LDIR_ORD_LAST_LONG_ENTRY) == 0) {
          continue;
        }
        lfnOrd = ord = ldir->ord & 0X1F;
        chksum = ldir->chksum;
      } else if (ldir->ord != --ord || chksum != ldir->chksum) {
        lfnOrd = 0;
        continue;
      }
      size_t k = 13*(ord - 1);
      if (k >= len) {
        // Not found.
        lfnOrd = 0;
        continue;
      }
      for (uint8_t i = 0; i < 13; i++) {
        uint16_t u = lfnGetChar(ldir, i);
        if (k == len) {
          if (u != 0) {
            // Not found.
            lfnOrd = 0;
          }
          break;
        }
        if (u > 255 || lfnToLower(u) != lfnToLower(fname->lfn[k++])) {
          // Not found.
          lfnOrd = 0;
          break;
        }
      }
    } else if (DIR_IS_FILE_OR_SUBDIR(dir)) {
      if (lfnOrd) {
        if (1 == ord && lfnChecksum(dir->name) == chksum) {
          goto found;
        }
        DBG_FAIL_MACRO;
        goto fail;
      }
      if (!memcmp(dir->name, fname->sfn, sizeof(fname->sfn))) {
        if (!(fname->flags & FNAME_FLAG_LOST_CHARS)) {
          goto found;
        }
        fnameFound = true;
      }
    } else {
      lfnOrd = 0;
    }
  }

found:
  // Don't open if create only.
  if (oflag & O_EXCL) {
    DBG_FAIL_MACRO;
    goto fail;
  }
  goto open;

create:
  // don't create unless O_CREAT and write mode.
  if (!(oflag & O_CREAT) || !isWriteMode(oflag)) {
    DBG_FAIL_MACRO;
    goto fail;
  }
  // If at EOF start in next cluster.
  if (freeFound == 0) {
    freeIndex = curIndex;
  }

  while (freeFound < freeNeed) {
    dir = dirFile->readDirCache();
    if (!dir) {
      if (dirFile->getError()) {
        DBG_FAIL_MACRO;
        goto fail;
      }
      // EOF if no error.
      break;
    }
    freeFound++;
  }
  while (freeFound < freeNeed) {
    // Will fail if FAT16 root.
    if (!dirFile->addDirCluster()) {
      DBG_FAIL_MACRO;
      goto fail;
    }
    // Done if more than one block per cluster.  Max freeNeed is 21.
    if (dirFile->m_vol->blocksPerCluster() > 1) {
      break;
    }
    freeFound += 16;
  }
  if (fnameFound) {
    if (!dirFile->lfnUniqueSfn(fname)) {
      goto fail;
    }
  }
  if (!dirFile->seekSet(32UL*freeIndex)) {
    DBG_FAIL_MACRO;
    goto fail;
  }
  lfnOrd = freeNeed - 1;
  for (uint8_t ord = lfnOrd ; ord ; ord--) {
    ldir = reinterpret_cast<ldir_t*>(dirFile->readDirCache());
    if (!ldir) {
      DBG_FAIL_MACRO;
      goto fail;
    }
    dirFile->m_vol->cacheDirty();
    ldir->ord = ord == lfnOrd ? LDIR_ORD_LAST_LONG_ENTRY | ord : ord;
    ldir->attr = DIR_ATT_LONG_NAME;
    ldir->type = 0;
    ldir->chksum = lfnChecksum(fname->sfn);
    ldir->mustBeZero = 0;
    lfnPutName(ldir, fname->lfn, len);
  }
  curIndex = dirFile->m_curPosition/32;
  dir = dirFile->readDirCache();
  if (!dir) {
    DBG_FAIL_MACRO;
    goto fail;
  }
  // initialize as empty file
  memset(dir, 0, sizeof(dir_t));
  memcpy(dir->name, fname->sfn, 11);

  // Set base-name and extension lower case bits.
  dir->reservedNT =  (DIR_NT_LC_BASE | DIR_NT_LC_EXT) & fname->flags;

  // set timestamps
  if (m_dateTime) {
    // call user date/time function
    m_dateTime(&dir->creationDate, &dir->creationTime);
  } else {
    // use default date/time
    dir->creationDate = FAT_DEFAULT_DATE;
    dir->creationTime = FAT_DEFAULT_TIME;
  }
  dir->lastAccessDate = dir->creationDate;
  dir->lastWriteDate = dir->creationDate;
  dir->lastWriteTime = dir->creationTime;

  // Force write of entry to device.
  dirFile->m_vol->cacheDirty();

open:
  // open entry in cache.
  if (!openCachedEntry(dirFile, curIndex, oflag, lfnOrd)) {
    DBG_FAIL_MACRO;
    goto fail;
  }
  return true;

fail:
  return false;
}

Co tu pozmieniać, żeby otworzyła plik np. "offł.bmp" ('ł' w argumencie "fname" niech się równa 179 jak w ASCII) jeśli ktoś to rozpracuje, wystarczy mi naprawienie 1 polskiej litery, resztę analogicznie sobie już napisze...

Z góry dziękuje za pomoc, pozdrawiam!

 

Udostępnij ten post


Link to post
Share on other sites
(edytowany)

 Może zamiast "wykrywać" zajrzysz choćby do Wikipedii i sprawdzisz sobie hasła:

  • Unicode
  • UTF-8
  • UTF-16
4 godziny temu, ElektronPL_WiTu napisał:

zrobiłem wykrywanie tych wartości i zamiana ich na standardowe dla ASCII znaki

W standardzie ASCII nie ma polskich znaków, więc jeśli na cokolwiek te wartości zamieniasz nie jest to ASCII. Prawdopodobnie powinieneś to zamienić na UTF-8, ale nie jestem pewien.

A tak przy okazji: o ile pamiętam biblioteka SDFat nie obsługuje LFN - chyba że od czasu kiedy się tym interesowałem coś się zmieniło?

 

Edytowano przez ethanak

Udostępnij ten post


Link to post
Share on other sites

Obsługuje LFN, tak jest to UTF-8 a nie ASCII racja. Tylko, że ja naprawdę na bardzo różne sposoby próbowałem sprawić, aby funkcja open znalazła plik z polskimi znakami i już brakuje mi pomysłów. 

Cytat

Może zamiast "wykrywać" zajrzysz choćby do Wikipedii i sprawdzisz sobie hasła:

Dodałem coś takiego do getName i wszystkie litery działają poprawnie tylko oczywiście po polskim znaku jesrt problem z długością nazwy pliku. Zegarek rozumie takie "podwójne" znaki tylko że np. zamiast "testowy_plikł.txt" jest "testowy_plikł.xt". 

else if(c <= 0xFFFF)
    {
        name[k++] = 0xC0 | (c >> 6);
        name[k++] = 0x80 | ((c >> 0) & 0x3F);
        //n++;
    }

To mogę naprawić tylko, że z tym nie ma problemu, problem jest z funkcją open która nie widzi plików z polskimi znakami...

Udostępnij ten post


Link to post
Share on other sites
46 minut temu, ElektronPL_WiTu napisał:

próbowałem sprawić, aby funkcja open znalazła plik z polskimi znakami i już brakuje mi pomysłów. 

Teoretyzuję (nie mam na czym sprawdzić): a gdyby do funkcji open wrzucić nazwę w postaci 8.3?

Zresztą dziwne że nie jest to już gdzieś rozwiązane - w końcu nie tylko Amerykanie korzystają z Arduino... a jeśli komuś to zadziała na chińskim czy rosyjskim, to i na polskim powinno... szukałeś pod tym kątem?

Udostępnij ten post


Link to post
Share on other sites

Poszukałem trochę, tylko jeszcze po angielsku wyszukam ale po rosyjsku u mnie już nie jest tak łatwo. Szukałem trochę z translatorem ale nic nie znalazłem. Co do 8.3 to działa ale w moim zastosowaniu nie jest do użycia. Gdy stworzyłem plik "off_plik_testowył.bmp" i otworzyłem go tak "OFF_PL~1.BMP" no to zadziałało. Ale 1 - gdy plik (bez rozszerzenia) ma mniej niż 9 znaków no to ta metoda już nie działa a 2 - ja wybieram plik z listy i go otwieram - bardzo trudno było by mi zmodyfikować kod tak aby nie otwierał LFN tylko SFN.

Udostępnij ten post


Link to post
Share on other sites

A co będzie jak podasz "off_plik_testowy\xc5\x82.bmp"? Oczywiście open() nie może wtedy zmieniać sobie kodów UTF-8 na podkreślenia...

Udostępnij ten post


Link to post
Share on other sites

Niestety nie działa. Swoja drogą taki sam efekt daje w Arduino IDE napisanie "off_plik_testowył.bmp", tak czy inaczej zamienia 'ł' na 197 i 130 bo 

Udostępnij ten post


Link to post
Share on other sites

Coś mi tu nie pasuje. Popatrzyłem sobie na kod funkcji parsePathName i nic tam nie widzę, co mogłoby wpływać na istnienie w nazwie znaków innych niż jakieś jedynie słuszne... W każdym razie metoda open(FatFile* dirFile, fname_t* fname, oflag_t oflag) nie ma tu nic do rzeczy bo operuje na nazwach 8.3.

Pokaż kawałek kodu, jak wywołujesz open();

Udostępnij ten post


Link to post
Share on other sites

Ale nie chodzi o kod funkcji parsePathName  tylko lfnGetName i ją poprawiłem o obsługę polskich znaków, ale ta funkcja nie ma znaczenia gdy wpisuje ręcznie nazwę pliku do funkcji open i kombinowałem już na różne sposoby ale bezskutecznie.

String filename = "off_plik_testowy\197\130.bmp";
SdFile bmpImage;
bmpImage.open(filename.c_str());

Tylko, że to nie ma żadnego znaczenia. Bo wg mnie to nie ma prawa zadziałać. Ponieważ znaki w char czy string są bajtami od 0x00 do 0xFF, a polskie znaki w utf-16 są wyższe od 255 (poza jednym wyjątkiem). A kod funkcji open w kluczowym miejscu wygląda tak:

      size_t k = 13*(ord - 1);
      if (k >= len) {
        // Not found.
        lfnOrd = 0;
        continue;
      }
      for (uint8_t i = 0; i < 13; i++) {
        uint16_t u = lfnGetChar(ldir, i);
        if (k == len) {
          if (u != 0) {
            // Not found.
            lfnOrd = 0;
          }
          break;
        }
        if (u > 255 || lfnToLower(u) != lfnToLower(fname->lfn[k++])) {
          // Not found.
          lfnOrd = 0;
          break;
        }
      }

I jak w nazwie jest np. ł to funkcja lfnGetChar da 322 więc co by nie wpisać w fname to i tak nie znajdzie. Tu trzeba coś zmienić i raczej w tym punkcie funkcji.

Udostępnij ten post


Link to post
Share on other sites

No przecież lfnGetChar operuje na 16-bitowych znakach, prawda? I 322 doskonale się w tym, mieści. Nie wiem co tam poprawiłeś, ale jeśli nikt nie zgłasza że biblioteka nie działa z cyrylicą/umlautami/hiraganą to coś jest nie tak.

A ten kawałek kodu wcale nie jest kluczowy, tylko dotyczy sytuacji, gdy nazwa nie może być długą nazwą i szuka się 8.3. Szukasz dziury w całym i "poprawiasz" miejsca, które poprawiania nie wymagają.

Pytanie kontrolne: czy Ty w ogóle wiesz, jak przechowywane są w FAT16/32 długie nazwy?

Pokażesz kod gdzie wywołujesz open czy to taka wielka tajemnica?

 

Udostępnij ten post


Link to post
Share on other sites

No pokazałem, cały kod jest bardzo długi ale w gruncie rzeczy tak to wygląda:

String filename = "off_plik_testowy\197\130.bmp";
SdFile bmpImage;
bmpImage.open(filename.c_str());

lfnGetChar daje 16 bitowy znak, ale mówię że zmienna fname przechowująca nazwę pliku już nie i w tym jest problem bo lfnGetChar da wartość np. 322, której nie da się zapisać w fname bo przechowuje 8-bitowe znaki.

Cytat

Nie wiem co tam poprawiłeś

W open jeszcze nic. Poprawiłem lfnGetName bo wszystkie znaki powyżej 127 zamieniał na podłogę.

Cytat

Nie wiem co tam poprawiłeś, ale jeśli nikt nie zgłasza że biblioteka nie działa z cyrylicą/umlautami/hiraganą to coś jest nie tak.

Chciałbym to zobaczyć, to może rozwiąże problem.

Udostępnij ten post


Link to post
Share on other sites
3 minuty temu, ElektronPL_WiTu napisał:

"off_plik_testowy\197\130.bmp";

 

To jakaś bzdura... mylisz zapis dziesiętny z ósemkowym. Powinno być:

"off_plik_tekstowy\305\202.bmp"

albo

"off_plik_tekstowy\xc5\x82.bmp"

Nie dziwię się, że nie znajduje pliku którego nie ma...

 

Udostępnij ten post


Link to post
Share on other sites

Faktycznie, żyłem w błędnym przeświadczeniu że to są wartości dziesiętne, ale czy wpisałem \305\202 czy \xc5\x82 to nie działa.

Udostępnij ten post


Link to post
Share on other sites

To szukaj błędu w innym miejscu, a nie usiłuj wpychać polskich znaków do nazwy 8.3.

Udostępnij ten post


Link to post
Share on other sites

dobra mam już coś - małe ó w utf-16 to 243 - mieści się w bajcie. I teraz gdy nazwałem plik "off_plik_testowyó.bmp" i otworzyłem go tak: "off_plik_testowy\363.bmp" no to zadziałało. Teraz muszę wykombinować jak sprawić aby zadziałały też wartości > 255

fname_t* fname przechowuje znaki w bajtach czy 16 bitowych znakach?

 

Udostępnij ten post


Link to post
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.


×
×
  • Utwórz nowe...