Zeilendurchgang einer Datei mit Unicode-Namen
-
Ich stehe vor dem Problem, dass der Compiler MinGW (gcc 4.7.1) weder std::wifstream (mit std::wstring als Eingabe) noch _wfopen kann. Unter den WinAPI Funktionen scheint keine Alternative zu std::getline vorhanden zu sein.
Nun habe ich mir folgendes gedacht:
- ich entsinne mich gelesen zu haben, dass Windows Dateinamen intern unter ANSI speichert und es möglich wäre, diese abzufragen, somit wäre ifstream verwendbar
- ich schreibe einen eigenen STL istream mit dem Windows File Handle
- der HANDLE von Windows lässt sich wie FILE* nutzen
- es gibt doch eine WinAPI Funktion zum zeilenweisen lesen einer Datei
- ich schreibe selber eine ReadLine Funktion für den Windows HandleEs fuchst mich, wie MinGW so Windows-unfreundlich sein kann
-
Ich rate mal wieder ins Blaue: Kann es sein, dass MinGW's wchar_t 4 Byte groß ist wie unter Linux üblich? Das würde die Windows-Unfreundlichkeit erklären, denn std::wifstream und _wfopen würden nicht funktionieren. Du brauchst also eine UTF32->UTF16 Konvertierungsfunktion um die Datei auf zu kriegen und eine UTF16->UTF32 Konvertierungsfunktion, die alles Eingelesene umwandelt. Vielleicht kann man irgendwie wchar_t als 2-Byte groß umdefinieren.
-
wchar_t wird vom Stream gar nicht erst unterstützt. Ich habe mal einen Blick in die Implementierung geworfen und tatsächlich wird überall char bzw. string anstatt der templates verwendet.
Nun habe ich mir eine eigene Klasse geschrieben.
#pragma once #include <windows.h> #include <string> #include <algorithm> class FileReader{ private: // File handle HANDLE file; // Read buffer char buffer[4096]; char* buffer_start, *buffer_end = buffer_start; public: // Constructors FileReader() : file(INVALID_HANDLE_VALUE){} FileReader(std::string& filename) : file(CreateFileA(filename.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)){} FileReader(std::wstring& filename) : file(CreateFileW(filename.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)){} FileReader(FileReader& reader) : file(reader.file){ reader.file = INVALID_HANDLE_VALUE; } // Assign operator FileReader& operator =(FileReader& reader){ if(this->file != INVALID_HANDLE_VALUE) CloseHandle(this->file); this->file = reader.file; this->buffer_start = this->buffer_end; reader.file = INVALID_HANDLE_VALUE; return *this; } // Destructor virtual ~FileReader(){ if(this->file != INVALID_HANDLE_VALUE) CloseHandle(this->file); } // Check file state operator bool(){ return this->file != INVALID_HANDLE_VALUE; } // Reset file pointer void reset(){ if(this->file != INVALID_HANDLE_VALUE){ SetFilePointer(this->file, 0, 0, FILE_BEGIN); this->buffer_start = this->buffer_end; } } // Read one line from file bool getline(std::string& line){ if(this->file != INVALID_HANDLE_VALUE){ // Clear old line content line.clear(); // Fill line content while(true){ // Check for buffer content if(this->buffer_start == this->buffer_end){ // Fill buffer DWORD read; if(!ReadFile(this->file, this->buffer, sizeof(this->buffer), &read, NULL) || read == 0) // Error or EOF break; this->buffer_start = this->buffer; this->buffer_end = this->buffer + read; } // Search newline char* newline = std::find(this->buffer_start, this->buffer_end, '\n'); // Assign content to line if(newline == this->buffer_end){ line.append(this->buffer_start, this->buffer_end); this->buffer_start = this->buffer_end; }else{ line.append(this->buffer_start, newline); this->buffer_start = newline + 1; return true; } } // Read anything? return !line.empty(); } // File handle invalid return false; } };
Könnte nun wie folgt genutzt werden:
FileReader reader("ähm.txt"); if(reader){ string line; while(reader.getline(line)) ...