Polskie litery w nazwach plików

0

Jak piszę program w C++, to w pierwszej kolejności korzystam z tego, co daje biblioteka standardowa, w drugiej kolejności to, co daje Qt.

Do testów zrobiłem prostą aplikację zawierająca jeden przycisk i jedno pole tekstowe. Chodzi o otwarcie pliku tekstowego będącego w katalogu z aplikacją.

W systemie Windows 8, jak aplikacja jest w katalogu zawierającym "polskie znaki" w nazwie, to ona nie otwiera pliku. Jak nie ma polskich znaków w nazwie, to plik prawidłowo otwiera i wyświetla zawartość tego pliku. Jednakże, nazwę katalogu wyświetla prawidłowo. W systemie Ubuntu Linux 20.04 nie ma tego problemu i aplikacja czyta plik również wtedy, gdy w nazwie katalogu są polskie znaki.

#include <QMainWindow>
#include <iostream>
#include <fstream>
#include <QDir>
#include <sstream>



void MainWindow::on_pushButton_clicked()
{
    std::string AppDir = QDir::currentPath().toUtf8().constData();

    QString X = "Error ";

    std::string FN = "\\test.txt";
    FN = AppDir + FN;

    std::fstream Fx(FN, std::ios::in);
    if (Fx.is_open())
    {
        X = "Good ";
        std::stringstream SS;
        SS << Fx.rdbuf();
        Fx.close();
        std::string Sx = "[" + SS.str() + "]";
        X = X + QString::fromUtf8(Sx.c_str());
    }


    ui->lineEdit->setText(X + QString::fromUtf8(AppDir.c_str()));
}

Konwersja między QString i std::string zastosowana jest taką, jaką stosuję od lat i bez problemu radzi sobie z polskimi znakami w systemie Windows.

Pewnie w tym przypadku można próbować podawać samą nazwę pliku, ale w jaki sposób podać katalog z polskimi znakami?

Zrobiłem też taki test, jak w nazwie pliku nie ma polskich znaków, to jest dobrze, a jak są polskie znaki, to na Windows nie czyta, na Linux czyta, jednak w obu systemach nazwę pliku w polu tekstowym pokazuje prawidłowo:

void MainWindow::on_pushButton_2_clicked()
{
    QString X = "Error ";

    std::string FN = "C:\\Users\\VM\\Desktop\\aą.txt";

    std::fstream Fx(FN, std::ios::in);
    if (Fx.is_open())
    {
        X = "Good ";
        std::stringstream SS;
        SS << Fx.rdbuf();
        Fx.close();
        std::string Sx = "[" + SS.str() + "]";
        X = X + QString::fromUtf8(Sx.c_str());
    }


    ui->lineEdit->setText(X + QString::fromUtf8(FN.c_str()));
}

Powyższy kod jest specjalnie napisany w celu zobrazowania problemu. Generalnie, chodzi o odczyt i zapis plików tekstowych linia po linii.

3

Czemu tak przekombinwujesz?

    using namespace std;

    ifstream fin(FN);
    string X;
    if(getline(fin,X,0))
    {
/* wczytano do X */
    }

Jeżeli chcesz pracować z UTF w c++ to:

    using namespace std;

    wifstream fin(FN);
    wstring X;
    if(getline(fin,X,0))
    {
/* wczytano do X */
    }
0

@_13th_Dragon: Swego czasu swoje rozwiązanie problemu wczytania linia po linii znalazłem na www.google.com i takie stosowałem. Faktycznie, można prościej i to działa.

Jednak przedmiotowy problem nie dotyczy odczytu zawartości pliku, tylko dotyczy nazwy pliku i to tylko w systemie Windows, niezależnie od tego, czy użyje się fstream czy ifstream czy wifstream. To było do przewidzenia, bo to w dotyczy danych ze strumienia, a nie przekazywanej nazwy pliku tworzącego ten strumień.

0

Stosowanie bibliotek WinApi mija się z celem, jak aplikacja ma być na Windows i Linux. Udało mi się zbadać sprawę i stwierdziłem, że problem jest ewidentnie w kodowaniu polskich znaków w ścieżce.

Postanowiłem zasymulować kodowanie w Windows-1250. Poniższy kod odczytał plik o nazwie "aą.txt":

void MainWindow::on_pushButton_3_clicked()
{
    QString X = "Error ";

    char FN_T[] = "C:\\Users\\VM\\Desktop\\aa.txt";

    // Zmiana "a" na "ą" w kodowaniu Windows-1250
    FN_T[21] = 185;
    std::ifstream Fx((std::string(FN_T)));
    std::string Sx;

    if (std::getline(Fx, Sx))
    {
        X = "Good " + QString::fromUtf8(Sx.c_str());
    }


    ui->lineEdit->setText(X);
}

Teraz poszukałem, jak przekodować string z UTF-8 na Windows 1250, plik się otworzył i wczytał, nie potrzeba dodatkowych bibliotek ani WinApi:

void MainWindow::on_pushButton_4_clicked()
{
    QTextEncoder * Enc = QTextCodec::codecForName("Windows-1250")->makeEncoder();
    std::string FN = "C:\\Users\\VM\\Desktop\\aą.txt";
    QByteArray QBA = Enc->fromUnicode(QString::fromUtf8(FN.c_str()));

    QString X = "Error ";
    std::ifstream Fx(std::string(QBA.constData()));
    std::string Sx;

    if (std::getline(Fx, Sx))
    {
        X = "Good " + QString::fromUtf8(Sx.c_str());
    }

    ui->lineEdit->setText(X);
}

@_13th_Dragon: Czy to, co mi proponujesz, to jest to samo, tylko z innej biblioteki, czy robi coś innego?
Wygląda jakby fstream nie mógł sobie poradzić z nazwą pliku zakodowaną w UTF-8.

0
andrzejlisek napisał(a):

Stosowanie bibliotek WinApi mija się z celem, jak aplikacja ma być na Windows i Linux.

Postanowiłem zasymulować kodowanie w Windows-1250. Poniższy kod odczytał plik o nazwie "aą.txt":
Teraz poszukałem, jak przekodować string z UTF-8 na Windows 1250, plik się otworzył i wczytał, nie potrzeba dodatkowych bibliotek ani WinApi:

A co z przekodowaniem na 1250 - "... jak aplikacja ma być na Windows i Linux ..."?

0
_13th_Dragon napisał(a):
andrzejlisek napisał(a):

Stosowanie bibliotek WinApi mija się z celem, jak aplikacja ma być na Windows i Linux.

Postanowiłem zasymulować kodowanie w Windows-1250. Poniższy kod odczytał plik o nazwie "aą.txt":
Teraz poszukałem, jak przekodować string z UTF-8 na Windows 1250, plik się otworzył i wczytał, nie potrzeba dodatkowych bibliotek ani WinApi:

A co z przekodowaniem na 1250 - "... jak aplikacja ma być na Windows i Linux ..."?

Okazuje się, że w Qt Creator, fstream ani wfstream z niewiadomych powodów nie przyjmuje zmiennej wstring jako parametru.

Na próbę, zamiast fstream użyłem QFile i zadziałało prawidłowo.

0

To chyba wyjaśnia całą sprawę:
https://stackoverflow.com/questions/821873/how-to-open-an-stdfstream-ofstream-or-ifstream-with-a-unicode-filename
https://www.generacodice.com/en/articolo/58747/How-to-open-an-std%3A%3Afstream-(ofstream-or-ifstream)-with-a-unicode-filename
Krótko mówiąc, STL nie jest przewidziany do obsługi Unicode/UTF-8. wersja fstream przyjmująca nazwę pliku typu wstring to wymysł Microsoftu poza standardem i jest to normalne, że nigdzie indziej nie działa.

Jak widać, Qt ma swoją wersję wielu mechanizmów i typów danych, jak np. QFile, QString, QByteArray, QThread itp. i jest alternatywa dla std::fstream, którą jest QFile.

0

To w takim razie, jak programiście C++ na ogół sobie radzą z plikami? Czy używają std::fstream (i jak podają nazwę pliku z folderem), czy używają mechanizmów obsługi plików z Boost lub Qt, czy coś jeszcze innego?

3

Skoro używasz Qt to używaj Qt.
Mieszaj STL i Qt tylko jak to jest konieczne.
Qt ma o wiele bardziej przyjemne API i dokumentację.

Linux domyślnie używa kodowania UTF-8, natomiast Windows preferuje używać kodowania specyficznego dla ustawień systemowych, kompatybilnych z DOS.
Więc jak masz ustawienia polskojęzyczne pliki tekstowe domyślnie będą zapisywane w Windows-1250 (jeden bajt jeden znak). Jednak wiele programów wymusza UTF-8, które praktycznie stało się standardem.

W Qt można zrobić tak:

QFile f("tekst.txt");
if (data.open(QFile::ReadOnly)) {
    QTextStream stream(&f);
    stream.setCodec(QTextCodec::codecForLocale()); // domyślne ustawienie, użyj systemowych ustawień lub detekcji kodowania
    QString line;
    while (stream.readLineInto(&line)) {
        processLine(line);
    }
}
1
andrzejlisek napisał(a):

To w takim razie, jak programiście C++ na ogół sobie radzą z plikami?

Dobre pytanie. Muszę powiedzieć, że nie przypominam sobie, bym kiedykolwiek musiał sobie radzić.
Kiedyś, jak pisało się jeszcze apki desktopowe, to pliki zawsze otwierały jakieś platform-specific biblioteki, jak MFC. Człowiek się w ogóle nie interesował, jak to zrobiono. Natywne stringi były widoczne jako "string ID 1234" i potem tłum tłumaczy przekładał to na japoński czy jakiś inny.
Jeśli chodzi o wielojęzyczność teraz, to cała interakcja z użytkownikiem jaką spotykałem była wydelegowana do HMI (human-machine interface), który jest zewnętrzną aplikacja zewnętrznej firmy napisaną w jakimś języku skryptowym, pewnie w Javie. Programista bawi się stringami tylko do logowania, a to biega na zwykłych ASCII.

Qt to taka nisza, którą mam wrażenie ostatnio obficie zrzuca się do Polski bo nikt tego już nie chce. Jak używasz Qt, to programuj w Qt, ten framework obsługuje większość rzeczy i możesz nawet za wiele C++ nie zobaczyć. To chyba taki "mniejszy .Net" i nie miksuje się dobrze z czyjąś własną twórczością.

0
MarekR22 napisał(a):

Skoro używasz Qt to używaj Qt.

Mieszaj STL i Qt tylko jak to jest konieczne.
Qt ma o wiele bardziej przyjemne API i dokumentację.

Linux domyślnie używa kodowania UTF-8, natomiast Windows preferuje używać kodowania specyficznego dla ustawień systemowych, kompatybilnych z DOS.
Więc jak masz ustawienia polskojęzyczne pliki tekstowe domyślnie będą zapisywane w Windows-1250 (jeden bajt jeden znak). Jednak wiele programów wymusza UTF-8, które praktycznie stało się standardem.

Warto wiedzieć, jednak przedmiotem tego tematu nie jest interpretacja treści pliku (jednak to też ważne w przypadku plików tekstowych), tylko odwołanie się do pliku o nazwie z polskimi znakami, niezależnie od zawartości.

Problem jest taki, że skoro std::string koduje zawartość w formacie UTF-8 (może też w dowolnym innym), to dlaczego nie da się w tym formacie przekazać nazwy pliku do std::fstream? Przekodowywanie do ANSI czy innego kodowania mija się z celem, bo po pierwsze, co system, a nawet język to inaczej, więc nie jest to rozwiązanie międzyplatformowe, po drugie, ANSI nie jest w stanie przechować wszystkich możliwych znaków dozwolonych w nazwie pliku. Z drugiej strony, wersja fstream z nazwą pliku typu wstring nie istnieje w Linux i jest chyba wymysłem Microsoftu.

LongInteger napisał(a):

Dobre pytanie. Muszę powiedzieć, że nie przypominam sobie, bym kiedykolwiek musiał sobie radzić.
Kiedyś, jak pisało się jeszcze apki desktopowe, to pliki zawsze otwierały jakieś platform-specific biblioteki, jak MFC. Człowiek się w ogóle nie interesował, jak to zrobiono. Natywne stringi były widoczne jako "string ID 1234" i potem tłum tłumaczy przekładał to na japoński czy jakiś inny.

Chyba właśnie po to wymyślono i wprowadzono do standardu fstream, żeby sposób obsługi pliku był niezależnuy od sprzętu i OS. Dopiero od niedawna stwierdziłem nieprawidłowości przy obsłudze na Windows i tylko wtedy, gdy w nazwie są polskie znaki.

LongInteger napisał(a):

Qt to taka nisza, którą mam wrażenie ostatnio obficie zrzuca się do Polski bo nikt tego już nie chce. Jak używasz Qt, to programuj w Qt, ten framework obsługuje większość rzeczy i możesz nawet za wiele C++ nie zobaczyć. To chyba taki "mniejszy .Net" i nie miksuje się dobrze z czyjąś własną twórczością.

Ja w Qt programuję bardziej hobbystycznie i też doszedłem do wniosku, że cały Qt to taki trochę .NET, jak dotąd dostarczył mi wszystko, co było i jest potrzebne, a nie ma w standardzie C++. Ja będę sukcesywnie przerabiać fstream do QFile, bo póki co, tylko takie rozwiązanie wchodzi w grę. Takim "konkurencyjnym" frameworkiem jest wxWidgets, jednak go nie znam.

0
andrzejlisek napisał(a):

nieprawidłowości przy obsłudze na Windows i tylko wtedy, gdy w nazwie są polskie znaki.

Ty, a może masz nieprawidłowe kodowanie ustawione w edytorze tekstu programu?
Tzn. ty piszesz i widzisz "wziął" a system widzi "wzi<krzaczek><krzaczek>". Daaawno nie pisałem dla windy, ale może program ma ustawione w systemie jedno kodowanie, twój edytor inne i litery które piszesz są dla systemu niezrozumiale?

0
LongInteger napisał(a):
andrzejlisek napisał(a):

nieprawidłowości przy obsłudze na Windows i tylko wtedy, gdy w nazwie są polskie znaki.

Ty, a może masz nieprawidłowe kodowanie ustawione w edytorze tekstu programu?
Tzn. ty piszesz i widzisz "wziął" a system widzi "wzi<krzaczek><krzaczek>". Daaawno nie pisałem dla windy, ale może program ma ustawione w systemie jedno kodowanie, twój edytor inne i litery które piszesz są dla systemu niezrozumiale?

Edytor na pewno koduje w formacie UTF-8. Nazwa pliku hardkodowana jest tylko na potrzeby testów, katalog tymczasowy też jest w formacie UTF-8 przy zmianie z QString na std::string. Ogólnym założeniem jest, ze tekst jest kodowany w UTF-8, bo to stało się jakby standardem.

Jak wypisuję do konsoli przez std::cout lub do pola tekstowego w formatce, to polskie znaki są prawidłowe.

1 użytkowników online, w tym zalogowanych: 0, gości: 1