Dateinamen Encoding

Hi,
ich würde gerne die Diskussion über das Encoding der Dateiname aus dem Tonuino Forum hier weiterführen, da @Christian auch schon hier unterwegs ist :slight_smile:

@Christian: Ich finde es cool, dass du den Filebrowser auf UTF-8 umgestellt hast und mit einer bidirektionalen Konvertierung gute Erfahrung gemach hast. Wenn du jemanden zum weiteren Testen brauchst, würde ich mich gerne anbieten. Bräuchte nur nen Link zu deinem Repo :slight_smile:

1 „Gefällt mir“

Jungs: Schön, dass ihr hier seid. Ich freue mich auf regen Austausch :smiley:

Ja, total gerne, zumal ich ja bisher nur experimentiere.

Repo ist schwierig. Ich komme von einem ESP32 +VS1053 und baue meine Software / Hardware gerade komplett um. Da ich Euer Webinterface klasse finde, habe ich angefangen das bei mir zu implementieren. Die Codebasis ist aktuell nicht wirklich kompatibel. Die HTML-Files liegen bei mir z.B. auf der SD.
Ich müsste einen neuen Fork vom ESPuino erstellen und die Änderungen dort noch einmal vornehmen - das wird grundsätzlich wohl das beste sein, muss ich aber dann erstmal vorbereiten.
Bis dahin würde ich vorschlagen, meine Änderungen hier zu posten? Dann können wir darauf rumdenken.

HTML / JS
Alle „charset“ auf utf-8 geändert

Arduino-Code
Ich habe mir zwei Helper-Funktionen gebaut. Zum Testen konvertiere ich einfach erstmal nur die Umlaute.

void Utf8UmlautsToAscii(String utf8String, char *asciiString) {

  int k=0;

  bool f_C3_seen = false;
  
  for (int i=0; i<utf8String.length(); i++)
  {

    if(utf8String[i] == 195){                                   // C3
      f_C3_seen = true;
      continue;
    } else {
      if (f_C3_seen == true) {
        f_C3_seen = false;
        switch (utf8String[i]) {
          case 0x84: asciiString[k++]=0x8e;  break; // Ä
          case 0xa4: asciiString[k++]=0x84;  break; // ä
          case 0x9c: asciiString[k++]=0x9a;  break; // Ü
          case 0xbc: asciiString[k++]=0x81;  break; // ü
          case 0x96: asciiString[k++]=0x99;  break; // Ö
          case 0xb6: asciiString[k++]=0x94;  break; // ö
          case 0x9f: asciiString[k++]=0xe1;  break; // ß
          default: asciiString[k++]=0xdb;  // Unknow... 
        }
      } else {
        asciiString[k++]=utf8String[i];
      }

    }
  }

  asciiString[k]=0;

}
// Conversion routine 
void AsciiUmlautsToUtf8(String asciiString, char *utf8String) {

  int k=0;

  bool f_C3_seen = false;
  
  for (int i=0; i<asciiString.length(); i++)
  {
    switch (asciiString[i]) {
      case 0x8e: utf8String[k++]=0xc3; utf8String[k++]=0x84;  break; // Ä
      case 0x84: utf8String[k++]=0xc3; utf8String[k++]=0xa4;  break; // ä
      case 0x9a: utf8String[k++]=0xc3; utf8String[k++]=0x9c;  break; // Ü
      case 0x81: utf8String[k++]=0xc3; utf8String[k++]=0xbc;  break; // ü
      case 0x99: utf8String[k++]=0xc3; utf8String[k++]=0x96;  break; // Ö
      case 0x94: utf8String[k++]=0xc3; utf8String[k++]=0xb6;  break; // ö
      case 0xe1: utf8String[k++]=0xc3; utf8String[k++]=0x9f;  break; // ß
      default: utf8String[k++]=asciiString[i];   
    }
  }

  utf8String[k]=0;

}


In Deiner Funktion explorerHandleListRequest sieht die while-Schleife jetzt so aus. Ich verwende zum Testen einfach asciiFilePath doppelt.

while(file) {
        JsonObject entry = obj.createNestedObject();
        AsciiUmlautsToUtf8(file.name(), asciiFilePath);
        std::string path = asciiFilePath; //file.name();
        std::string fileName = path.substr(path.find_last_of("/") + 1);
        
        entry["name"] = fileName;
        entry["dir"].set(file.isDirectory());

        file = root.openNextFile();

        //esp_task_wdt_reset();

    }

    serializeJson(obj, serializedJsonString);
    request->send(200, "application/json; charset=utf-8", serializedJsonString);

Damit werden Files, die ich unter Windows kopiert habe korrekt angezeigt.

Für die andere Richtung habe ich Deine Funktion explorerHandleCreateRequest angepasst.

void(AsyncWebServerRequest * request) {
  AsyncWebParameter * param;
  char asciiFilePath[256];
  if (request -> hasParam("path")) {
    param = request -> getParam("path");
    Utf8UmlautsToAscii(param -> value(), asciiFilePath);
    Serial.print("Incomming: ");
    Serial.println(param -> value());
    Serial.print("2ASCII: ");
    Serial.println(asciiFilePath);
    if (FSystem.mkdir(asciiFilePath)) {
      //snprintf(logBuf, serialLoglength, "CREATE:  %s created", asciiFilePath);
      //loggerNl(serialDebug, logBuf, LOGLEVEL_INFO);
    } else {
      //snprintf(logBuf, serialLoglength, "CREATE:  Cannot create %s", asciiFilePath);
      //loggerNl(serialDebug, logBuf, LOGLEVEL_ERROR);
    }
  } else {
    //loggerNl(serialDebug, "CREATE: No path variable set", LOGLEVEL_ERROR);
  }
  request -> send(200);
}

Das ist mein aktueller Stand.

Über Filebrowser einen Ordner anlegen mit Sonderzeichen → Windows zeigt ihn korrekt an

Unter Windows einen Ordner/File anlegen → FileBrowser zeigt ihn korrekt an.

Hallo Christian,
danke für den Input. Ich bin noch nicht dazu gekommen es mir detailiert anzusehen. Aber das Prinzip habe ich verstanden :slight_smile:
Das Format für die Umlaute ist aber CP437 und nicht ASCII oder? Deswegen wird es ja unter Windows auch korrekt angezeigt.
Was es dann für FTP bedeutet müsste man dann schauen…

Achja, Wolle hat nun die Audio-Lib angepasst. Die kommt nun auch mit UTF-8 Dateien zurecht. Ein zum ESPuino kompatiblen Stand ist in meinem Repo. Allerdings noch nicht getestet.

BTW: @Christian du hast ja schon erwähnt, dass du den Upload ans Bootstrap Design angepasst hast. Schaut schick aus! Könntest du mir da auch den Code geben, dann würde ich das einbauen?

Hallo @Christian,
habs mir jetzt angesehen. Das Ganze funktioniert recht gut muss ich sagen :).
Für FTP kann man als Zeichensatz CP437 eintragen, dann passt das auch.
Das wäre zwar für den Nutzer ein weiter Konfigurationsaufwand, aber ich habe gesehen, dass man in FileZilla die Server Konfiguration exportieren kann. @biologist man könnte dann eine solche Beispiel Konfigurations ins Repository legen. Dann werden die initialen FTP Probleme vielleicht auch weniger :wink:

Der einzige Nachteil ist, dass im Seriellen Monitor die Umlaute nicht korrekt angezeigt werden, aber damit könnte ich leben.

@biologist wäre für dich CP437 als Datei Encoding ok?

Ich muss zugeben, dass ich in die ganze Diskussion per CC ja quasi involviert war, es aber, wegen der ganzen Arbeit hier mit dem Forum, nur so am Rande gewissermaßen verfolgt habe.

Was spricht denn aus deiner Sicht für CP437? Dass die meisten Leute Windows verwenden und diese wahrscheinlich, mindestens mal initial, die SD direkt am Rechner befüllen? Also für mich ist quasi die Frage: Was passiert, wenn man unter Mac OS z.B. eine SD befüllt? Passen der Zeichensatz dann nicht mehr?

Kurzum: Ich bin mir jetzt der Konsequenzen unsicher, daher muss ich jetzt erstmal dumm fragen :slight_smile:

Das Grundproblem ist ja, dass je nach dem wie die Daten auf die SD Karte kommen der Zeichensatz anders ist. Um eine korrekte Darstellung zu gewährleisten, sollte man diesen wenn es geht vereinheitlichen. HTTP und FTP via FileZilla bekommt man denke ich gut in den Griff.
Für das Schreiben von Daten über den Rechner hat man keinen Einfluss. Unter Windows und Linux ist es CP437. Unter Mac müsste es sich wahrscheinlich ähnlich verhalten. Da der Zeichensatz vom Dateisystem abhängt: Dateiname – Wikipedia

CP437 ist für mich der gemeinsame Nenner für HTTP, FTP und das direkte kopieren auf die SD Karte unter Windows/Linux/Mac.
Der Nachteil ist halt, dass die z.B. Umlaute über den Seriellen Monitor nicht mehr korrekt angezeigt werden.

Ursprünglich wollte ich ja UTF-8 nehmen. Da würde es mit dem Seriellen Monitor wieder klappen. Allerdings würde CP437 immernoch über das direkte Kopieren von Daten auf das Dateisystem kommen und man müsste sich eine Lösung für dieses Problem überlegen. Außerdem hat mich Wolle von der Audio-Lib verunsichert, da er meinte UTF-8 wird nur von der SD_MMC lib unsterstützt und nicht von der SD lib. Ob das wirklich stimmt kann ich nicht nachprüfen, da ich hier mit SD_MMC unterwegs bin.

Ja, also wenn es der kleinste gemeinsame Nenner ist, dann passt das für mich. Ich sag mal das serial Logging ist jetzt auch nicht unendlich wichtig. Wobei generell läuft das Logging ja durch eine Funktion. Vielleicht kann man die Ausgabe auch durch einen Converter laufen lassen und dann isses kein Problem mehr. Damit es dann nicht mit dem Kram knall, der in logmessages.h drin ist, kann man dort aus ü als ue machen und so weiter. Oder klappt das so nicht, was ich mir da vorstelle?

Aber wie gesagt: serial Logging ist jetzt auch nicht kriegsentscheident. Wenn die Kiste läuft guckt da keiner mehr drauf (außer die Entwickler), hehe.

Du bist also jetzt generell auf CP437 gegangen? Kein UTF-8 und Konvertierung?

Aktuell wird bei mir in der Konsole der korrekte Name angezeigt, weil ich da noch mit UTF-8 arbeite.

Und ja, CP437 - Aber die Zeichenwerte sind für die entsprechenden Umlaute wohl gleich, oder?

Ich mach mal ein neues Thema auf. Ich hätte da noch zwei Ideen zur Änderung.

Die Konfigurationsseite stellt aber UTF-8 dar. Das ist auch die Grundeinstellung in Linux und Mac.
CP437 nutzt doch noch nicht mal Windows, das war glaube ich noch DOS.
Auch diese Webseite nutzt UTF-8.

<html lang="de" class="desktop-view not-mobile-device text-size-normal">
  <head>
    <meta charset="utf-8">
    <title>Dateinamen Encoding - #10 von Christian - Software - ESPuino :: Rfid-controlled musicplayer</title>

Ich wäre für den Vorschlag alles in UTF-8 umzusetzen, auch Windows kann damit umgehen und wenn der Filezilla nicht explizit auf von UTF-8 verdrängt wird nutzt auch der das.

Man muss vielleicht noch dazu sagen, dass wir hier über das encoding der Dateinamen sprechen.
Und Windows speichert die so ohne weiteres nicht in UTF-8 auf eine SD-Karte.
Oben ist der Wikipedia Beitrag verlinkt.

Ergänzung zu der Antwort von Christian

Ja unter Linux hat er UTF-8, aber unter Windows nutzt er bei mir lustigerweise ISO 8859-1.

Falls du einen Vorschlag hast, wie man die Problematik mit dem unterschiedlichen Encoding elegant mit UTF-8 lösen kann, bin ich da offen und auch dankbar. V.a. dann hätte sich mein Bemühen die UTF-8 Unterstützung in der Audio-Lib zu verbessern gelohnt :smiley:

Ne, dass würde ich so lassen. Aber wenn an anderer Stelle, z.B. in der Audio-Lib der Filename geprintet wird, weil gerader dieser Titel abgespielt wird, dann zeigt es diesen natürlich nicht mehr korrekt an.

Das Problem ist glaube ich, dass jeder extended ASCII anders definiert. Wahrscheinlich heißt das nur, dass das 8. Bit verwendet wird, ob für CP437, ISO-8859-1 etc. Und die unterscheiden sich bei den Umlauten :slight_smile:

Ich würde dann mal weiter testen und die Konvertierungen in die anderen Funktionen vom Filebrowser nehmen.

@Christian noch ne Frage: Der Filebrowser bekommt ja derzeit die Dateinamen in ISO 8859. Dazu musste ich keine Konvertierung machen und habe lediglich als charset iso-8859-1 angegeben. Der Browser hat es dann korrekt angezeigt. Bei CP437 (bzw. IBM437) funktioniert das nicht. Hast du da eine Ahnung? Weil so könnte man sich die Konvertierung auf ESP Seite sparen. Oder ist das eine schlechte Idee?

EDIT: Hab den Filebrowser jetzt ganz auf deine Konvertierung umgestellt und getestet. Es funktioniert :). Die automatische Konvertierung von ESP zu Client habe ich aber noch nicht hinbekommen

Hey,
@Christian ich habs mir nochmal durch den Kopf gehen lassen. Die JSON Liste würde ich so konvertiert lassen, wie du es vorgeschlagen hast, also utf-8. Die Konvertiererei fällt ja kaum ins Gewicht. Die CP437 Unterstützung würde ich auch erstmal bei den Umlauten und ß belassen. Das sollte eigentlich die meisten Fälle abdecken.
Ich würde dann noch ein define erstellen, damit man beim HTTP Upload zwischen UTF-8 und CP437 für die Dateien wählen kann.
Bei den Dateioperationen vom Filebrowser würde ich dann noch einen Fallback einbauen. Also falls die Datei im CP437 Encoding nicht gefunden wird, wird der UTF-8 Name versucht.

EDIT: Beim Testen habe ich festgestellt, dass die letzten zwei Punkte zu unschönen Effekten führen können und die Supportanfragen eher erhöhen ^^. Deswegen habe ich diese wieder ausgebaut und mal was eingecheckt.

1 „Gefällt mir“

Ich weiß es auch nicht genau. Ich vermute, dass der ESP intern „immer“ UTF-8 spricht und das encoding im HTML Transfer sich wirklich nur auf den Transfer zwischen Browser und AsyncWebServer bezieht. Das würde erklären, warum es keine Auswirkungen hat. Bei meinen Versuchen, klappte es auch mit jeder encoding-Einstellung (HTML + im ESP-Code) wenn sie identisch waren.

Aber wenn es jetzt so klappt…Top!

@Harry Ich habe mal ein cherry-pick auf deine cp437-Implementierung gemacht und kann bestätigen, dass CP437 läuft. Getestet habe ich auf einen MAC.
D.h. stellt man im Filezilla auf CP437 um, dann ist das zwischen HTTP-Upload, FTP-Upload und direkten SD-Transfer kompatibel. Man kann aus der GUI raus auch Files mit Umlauten abspielen.

Was noch nicht geht: RFIDs anlernen, die auf Files mit Umlauten verweisen. Also anlernen kann man, aber das wird dann nicht gefunden. Ich vermute mal, dass man dafür sorgen muss, dass man beim Speichern in der Funktion processJsonRequest() auch eine Konversion von utf8 nach ascii machen muss. Dort wird der Dateiname samt RFID-Zuordnung ins NVS geschrieben. Und vermutlich in handleUpload() auch das Gleiche - das ist das Handling für den Backup-File-Importer. Unsicher bin ich mir bei dumpNvsToSd(). Aber ich vermute mal, wenn es eh schon durch die Konversion in processJsonRequest() ASCII ist, dann muss man es nicht nochmal konvertieren.

Was meinst du dazu? Du bist in der Konvertierung gerade tiefer drin :slight_smile:

Danke fürs Testen. Dann sind die Tests für den Mac auch erledigt! :slight_smile:
Übrigens ist das ja hauptsächlich die Implementierung von @Christian. Er hatte ja diese gute Idee. Ich hab da nur wenig hinzugefügt.

Das mit dem URL Pfad für die RFIDs hast du natürlich vollkommen Recht. Das hatte ich nicht auf dem Schirm und habe da noch schnell einen Commit nachgeschoben. Danke!
Allerdings würde ich für handleUpload() und dumpNvsToSd() nichts machen. Ich sehe das als Import/Export von Inhalten, die im NVS stehen und da sehe ich keinen Grund diese zu modifizieren. Oder will man die Backup.txt außerhalb anpassen? Wenn die Konvertierung gemacht werden soll, dann empfehle ich sie in beiden Funktionen zu machen, um immer einen konsistenten Stand zu haben. Das nur in eine Richtung zu machen führt erfahrungsgemäß zu Problemen.

Ok, also das mit der Zuweisung klappt jetzt. @Harry und @Christian Danke an euch beide an dieser Stelle!
Wegen handleUpload() und dumpNvsToSd() war ich mir jetzt einfach nicht sicher, ob da irgendwo nochmal was konvertiert wird (vom Browser oder so). Wie auch immer: Ich habe die backup.txt mal runtergeladen, re-importiert und eine „problematische Karte“ aufgelegt. Das hat einwandfrei funktioniert :ok_hand:
Im serial-Logging sieht’s an der Stelle dann komisch aus (wie bereits angekündigt), aber es kommt auf die Funktion und nicht aufs Logging an :slight_smile:

1 „Gefällt mir“