Skocz do zawartości

Biblioteka SDFat - polskie znaki w nazwach plików


Pomocna odpowiedź

Napisano

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!

 

(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

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...

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?

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.

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();

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.

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?

 

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.

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...

 

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?

 

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...