none
CStdioFile::WriteString bei Unicode mit Umlauten RRS feed

  • Frage

  • Hallo an alle,

    seitdem ich mein SDI-Projekt unter VS 2013 auf Unicode umstellt habe, werden die Umlaute mit der Funktion CStdioFile::WriteString nicht mehr mit dem Inhalt in eine Datei gschrieben, wie es die Schnittstelle verlangt.

    Konkret wird zunächst der Text TestÄÖÜäöü so in den Text TestŽ™š„” konvertiert:

    CString strTextInh = "TestÄÖÜäöü";
    CStringT<char, StrTraitATL<char, ChTraitsCRT<char>>> strtTextInhULet(strTextInh.GetBuffer(UCHAR_MAX));
    strtTextInhULet.AnsiToOem();
    CString strTextInhULet = strtTextInhULet; // Inhalt = "TestŽ™š„”"
    

    Zum Schreiben der Datei verwende ich diese Klasse:

    // DateiASCII.h: Schnittstelle für die Klasse CDateiASCII.
    //
    //////////////////////////////////////////////////////////////////////
    
    #if !defined(AFX_DateiASCII_H__237E9979_7754_11D5_9DF7_AA0004000806__INCLUDED_)
    #define AFX_DateiASCII_H__237E9979_7754_11D5_9DF7_AA0004000806__INCLUDED_
    
    #if _MSC_VER > 1000
    #pragma once
    #endif // _MSC_VER > 1000
    
    #include "StringListDbh.h"
    
    class CDateiASCII  
    {
    public:
    	BOOL Vorbelegen(CString& rstrVerzeichnis, CString& rstrDatei, BOOL bVerzAnwendung);
    	int Schreiben(CString strASCIIDatei,CStringListDbh& strlASCIIDatei);
    	BOOL Lesen(CString strASCIIDatei, BOOL bMeldungDialog, CStringListDbh& strlASCIIDatei);
    	CDateiASCII();
    	virtual ~CDateiASCII();
    };
    
    #endif // !defined(AFX_DateiASCII_H__237E9979_7754_11D5_9DF7_AA0004000806__INCLUDED_)
    

    // DateiASCII.cpp: Implementierung der Klasse CDateiASCII. // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "DateiASCII.h" #include "StringArrayDbh.h" #include <Shlwapi.h> #include "TCHAR.h" #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif ////////////////////////////////////////////////////////////////////// // Konstruktion/Destruktion ////////////////////////////////////////////////////////////////////// CDateiASCII::CDateiASCII() { } CDateiASCII::~CDateiASCII() { } BOOL CDateiASCII::Lesen(CString strASCIIDatei, BOOL bMeldungDialog, CStringListDbh& strlASCIIDatei) { // Die ASCII-Datei gegebenenfalls lesen und in eine Liste schreiben BOOL bLesen = FALSE; strlASCIIDatei.RemoveAll(); // alten Inhalt löschen if (strASCIIDatei != "") { CStdioFile stdiofASCIIDatei; int nVersuche = 5; int nZler = 1; do { // VS60 stdiofASCIIDatei.m_pStream = fopen(strASCIIDatei,"r"); // errno_t eKon = fopen_s(&stdiofASCIIDatei.m_pStream,strASCIIDatei,"r"); errno_t eKon = _tfopen_s(&stdiofASCIIDatei.m_pStream,strASCIIDatei,_T("r")); if (stdiofASCIIDatei.m_pStream == NULL && nZler == nVersuche) { if (bMeldungDialog) { CString strMessage = _T("Fehler beim Öffnen der Datei '") + strASCIIDatei + _T("'."); int nKon = AfxMessageBox(strMessage,MB_ICONINFORMATION | MB_OK); } return bLesen; } else if (stdiofASCIIDatei.m_pStream == NULL) { int nSekunden = 1; int nMilliSekunden = nSekunden * 1000; Sleep(nMilliSekunden); } nZler++; } while (stdiofASCIIDatei.m_pStream == NULL); CString strZeile; while (BOOL bReturn = stdiofASCIIDatei.ReadString(strZeile) != NULL) { bLesen = TRUE; strlASCIIDatei.AddTail(strZeile); } int nSchliessen = fclose(stdiofASCIIDatei.m_pStream); } return bLesen; } int CDateiASCII::Schreiben(CString strASCIIDatei, CStringListDbh&strlASCIIDatei) { // Die ASCII-Datei schreiben int nAnzWrite = 0; CStdioFile stdiofASCIIDatei; int nVersuche = 10; int nZler = 1; do { errno_t eKon = _tfopen_s(&stdiofASCIIDatei.m_pStream,strASCIIDatei,_T("w")); if (stdiofASCIIDatei.m_pStream == NULL && nZler == nVersuche) { CString strMessage = _T("Fehler beim Schreiben der Datei '") + strASCIIDatei + _T("'."); int nKon = AfxMessageBox(strMessage,MB_ICONINFORMATION | MB_OK); return nAnzWrite; } else if (stdiofASCIIDatei.m_pStream == NULL) { int nSekunden = 1; int nMilliSekunden = nSekunden * 1000; Sleep(nMilliSekunden); } nZler++; } while (stdiofASCIIDatei.m_pStream == NULL); int nAnzahl = (int)strlASCIIDatei.GetCount(); for (nZler = 0; nZler < nAnzahl; nZler++) { POSITION pZeile = strlASCIIDatei.FindIndex(nZler); CString strZeile = strlASCIIDatei.GetAt(pZeile); CString strZeileSchreiben = strZeile;   strZeileSchreiben += "\n";

    stdiofASCIIDatei.WriteString(strZeileSchreiben); // <----- hier der Fehler nAnzWrite++; } int nSchliessen = fclose(stdiofASCIIDatei.m_pStream); return nAnzWrite; } BOOL CDateiASCII::Vorbelegen(CString& rstrVerzeichnis, CString& rstrDatei, BOOL bVerzAnwendung) { // Die Werte vorbelegen BOOL bDebug = FALSE; if (bVerzAnwendung) { // Das Verzeichnis der Anwendung vorbelegen TCHAR szVerzeichnis[MAX_PATH]; DWORD dwModulFileName = GetModuleFileName(NULL,szVerzeichnis,MAX_PATH); if (dwModulFileName == NULL) { DWORD dwLastError = GetLastError(); CString strMessage; strMessage.Format(_T("Fehler bei GetModuleFileName: %d"),dwLastError); int nKon = AfxMessageBox(strMessage,MB_ICONINFORMATION | MB_OK); } CString strVerzeichnis; strVerzeichnis.Format(_T("%s"),szVerzeichnis); LPTSTR lpVerzeichnis = strVerzeichnis.GetBuffer(MAX_PATH); BOOL bKon = PathRemoveFileSpec(lpVerzeichnis); // Dateiname und Erweiterung entfernen strVerzeichnis.ReleaseBuffer(); CString strEntfernen = _T("Debug"); int nKonDebug = strVerzeichnis.Find(strEntfernen); // sonst teilweise Fehler bei TrimRight if (nKonDebug >= 0) { strVerzeichnis.TrimRight(strEntfernen); bDebug = TRUE; } strEntfernen = "Release"; int nKonRelease = strVerzeichnis.Find(strEntfernen); // sonst teilweise Fehler bei TrimRight if (nKonRelease >= 0) { strVerzeichnis.TrimRight(strEntfernen); } lpVerzeichnis = strVerzeichnis.GetBuffer(MAX_PATH); LPTSTR lpKon = PathAddBackslash(lpVerzeichnis); strVerzeichnis.ReleaseBuffer(); rstrVerzeichnis = strVerzeichnis; // Die Datei vorbelegen LPTSTR lpDatei = rstrDatei.GetBuffer(MAX_PATH); PathStripPath(lpDatei); // Verzeichnis entfernen rstrDatei.ReleaseBuffer(); rstrDatei = rstrVerzeichnis + rstrDatei; } else { // Das Verzeichnis der Datei übernehmen CString strVerzeichnis = rstrDatei; LPTSTR lpVerzeichnis = strVerzeichnis.GetBuffer(MAX_PATH); BOOL bKon = PathRemoveFileSpec(lpVerzeichnis); // Dateiname und Erweiterung entfernen strVerzeichnis.ReleaseBuffer(); lpVerzeichnis = strVerzeichnis.GetBuffer(MAX_PATH); LPTSTR lpKon = PathAddBackslash(lpVerzeichnis); strVerzeichnis.ReleaseBuffer(); rstrVerzeichnis = strVerzeichnis; } return bDebug; }

    In der Datei steht aber dann der falsche Text Testc203 1 0.00, der dann zu einer fehlhaften Weiterverwendung der Datei führt. Ich habe im Moment keine Idee mehr für die Lösung.

    Wie kann ich in meinem SDI-Projekt eine Datei beschreiben, die den gewünschten Text TestŽ™š„” enthält?

    Danke im Voraus und viele Grüße

    Bernd

    Mittwoch, 2. Mai 2018 13:23

Antworten

  • Hallo,

    das kenne ich aus der Standard Bibliothek mit std::ofstream. Irgendwo kann man zwar auch die Bibliothek auf UNICODE umstellen, aber es geht auch anders.

    CString ist UNICODE. Also benutze CStringA, um in die Datei zu schreiben.

    Beispielhaft für std:

    CString sz = L"TextÄÖÜäöü";
    CStringA szA = (CStringA)sz;
    std::ofstream fo(szFilename);
    if (fo.is_open())
    {
      fo << CStringA(sz) << " und " << szA << '\n';
    }
    fo.close();
    Gruß Guido




    Donnerstag, 3. Mai 2018 05:52

Alle Antworten

  • Hallo,

    das kenne ich aus der Standard Bibliothek mit std::ofstream. Irgendwo kann man zwar auch die Bibliothek auf UNICODE umstellen, aber es geht auch anders.

    CString ist UNICODE. Also benutze CStringA, um in die Datei zu schreiben.

    Beispielhaft für std:

    CString sz = L"TextÄÖÜäöü";
    CStringA szA = (CStringA)sz;
    std::ofstream fo(szFilename);
    if (fo.is_open())
    {
      fo << CStringA(sz) << " und " << szA << '\n';
    }
    fo.close();
    Gruß Guido




    Donnerstag, 3. Mai 2018 05:52
  • Hallo Guido,

    danke für Deine Antwort. Die Informationen haben mir sehr weiter geholfen.

    Hier mein neuer Programmcode entsprechend Deinen Informationen:

    int CDateiASCII::Schreiben(CString strASCIIDatei, CStringListDbh& strlASCIIDatei)
    {
     	// Die ASCII-Datei öffnen
    	int nAnzWrite = 0;
    	int nVersuche = 10;
    	int nZler = 1;
    // VS2005 	CStdioFile stdiofASCIIDatei;
    // VS2005	BOOL bOpen;
    	std::ofstream sosASCIIDatei(strASCIIDatei);
    	do
     	{
    // VS2005		errno_t eKon = _tfopen_s(&stdiofASCIIDatei.m_pStream, strASCIIDatei, _T("w"));
    // VS2005		if (stdiofASCIIDatei.m_pStream == NULL && nZler == nVersuche)
    		if (sosASCIIDatei.is_open() == FALSE && nZler == nVersuche)
    		{			
    			CString strMessage = _T("Fehler beim Schreiben der Datei '") + strASCIIDatei + _T("'.");
    			int nKon = AfxMessageBox(strMessage,MB_ICONINFORMATION | MB_OK);
    			return nAnzWrite;
    		}
    // VS2005		else if (stdiofASCIIDatei.m_pStream == NULL)
    		else if (sosASCIIDatei.is_open() == FALSE)
    		{			
    			int nSekunden = 1;
     			int nMilliSekunden = nSekunden * 1000;
    			Sleep(nMilliSekunden);  
    		}
    		nZler++;
    // VS2005	} while (stdiofASCIIDatei.m_pStream == NULL == FALSE);
    	} while (sosASCIIDatei.is_open() == FALSE);
    
    	// Die ASCII-Datei schreiben
    	int nAnzahl = (int)strlASCIIDatei.GetCount();
    	for (nZler = 0; nZler < nAnzahl; nZler++)
        {
    		POSITION pZeile = strlASCIIDatei.FindIndex(nZler);
    		CString strZeile = strlASCIIDatei.GetAt(pZeile);
    // VS2005	CString strZeileSchreiben = strZeile;
    		CStringA  strZeileSchreiben = (CStringA)strZeile;
    		strZeileSchreiben += "\n";
    // VS2005	stdiofASCIIDatei.WriteString(strZeileSchreiben);
    		sosASCIIDatei << strZeileSchreiben;
    		nAnzWrite++;
        }
    
    	// Die ASCII-Datei schließen
    // VS2005	int nSchliessen = fclose(stdiofASCIIDatei.m_pStream);
    	sosASCIIDatei.close();
    
    	return nAnzWrite;
    }
    

    Viele Grüße

    Bernd

    Donnerstag, 3. Mai 2018 07:03
  • Wie kann ich in meinem SDI-Projekt eine Datei beschreiben, die den gewünschten Text TestŽ™š„” enthält?

    Indem Du zum schreiben mit CStdioFile auch einen UNICODE stream nutzt, dann sollte es wieder kompatibel sein. Die Dokumentation zu fopen_s, _wfopen_s enthält die Details dazu.

    Bsp.:

    CString strTextInh = _T("TestÄÖÜäöü");
    CStringT<char, StrTraitATL<char, ChTraitsCRT<char>>> strtTextInhULet(strTextInh.GetBuffer(UCHAR_MAX));
    strtTextInhULet.AnsiToOem();
    CString strTextInhULet (strtTextInhULet);
    
    // Open the file with the specified encoding
    FILE *fStream;
    errno_t e = _tfopen_s(&fStream, _T("bsp.txt"),
    	_T("wt,ccs=UNICODE"));
    if (e != 0)
    	; // fail
    CStdioFile f(fStream);  // open the file from this stream
    
    f.WriteString(strTextInhULet);
    f.Close();
    

    Trotzdem ist es sicher nicht verkehrt ofstream zu nutzen, bei wide character strings wäre dies allerdings nicht std::ofstream sondern std::wofstream (erspart den unschönen C-Cast... Anm.: es gibt auch passende C++ Casts) - eine t.. Variante des ofstream gibt es leider nicht, daher muss dran gedacht werden, falls doch nochmal für Multibyte kompiliert wird, dies geeignet zu behandeln.


    - Gruß Florian

    Freitag, 4. Mai 2018 10:59
  • Hallo Florian,

    danke für Deine Antwort.

    In Deiner Version steht der gewünschte Text auch in der Datei. Leider kann aber die externe Software diese Datei nicht lesen/importieren. Darauf habe ich keinen Einfluss und ich kann auch nichts kontrollieren. Ich lasse es deshalb bei der Version von Guido.

    Viele Grüße

    Bernd

    Montag, 7. Mai 2018 07:47