Search code examples
stringc++builderindyvcl

How to use escape sequences in strings that work with both TMemo and TIdLogFile


Using C++Builder XE6, I wrote a log file class that derives from TIdLogFile:

#include <IdLogFile.hpp>

class TIdLogFileEx : public TIdLogFile
{
    typedef TIdLogFile inherited;

    public:
        inline __fastcall TIdLogFileEx (TComponent* AOwner) : inherited(AOwner) {}
        inline __fastcall TIdLogFileEx () : inherited() {}

        virtual void __fastcall LogInfo (const System::UnicodeString AText);
};

void __fastcall TIdLogFileEx::LogInfo (const System::UnicodeString AText)
{
    LogWriteString(_D("Info : ") + AText + EOL);
}

This works as expected for plaintext strings, but not for strings that contain escape sequences.

The following code demonstrates the problem:

void __fastcall TForm1::Button1Click (TObject *Sender)
{
    UnicodeString U = "\x95" + UnicodeString("Test") + "\x85";
    AnsiString A = U;

    TIdLogFileEx* File = new TIdLogFileEx;

    try
    {
        File->Filename = "Test.log";
        File->Active   = true;

        File->LogInfo(U);
        File->LogInfo(A);
        Memo1->Lines->Add(U);
        Memo1->Lines->Add(A);

        U = _D("\x95") + UnicodeString("Test") + _D("\x85");
        A = U;

        File->LogInfo(U);
        File->LogInfo(A);
        Memo1->Lines->Add(U);
        Memo1->Lines->Add(A);
    }
    __finally
    {
        delete File;
    }
}

The output to Memo1 is:

•Test…
•Test…
Test        (actually <U+0095>Test<U+0085>)
?Test?

The log file contains:

Info : ?Test?
Info : ?Test?
Info : •Test…
Info : ?Test?

How do I use escape sequences that work with both TMemo and TIdLogFile / TIdLogFileEx, and are also portable?

Note that _D("\x2022") and _D("\x2026") don't work as expected when the default string type is AnsiString.


Solution

  • I changed my TIdLogFileEx class to write to the log file in UTF-8 encoding.

    Here's the code:

    #include <IdLogFile.hpp>
    
    class TIdLogFileEx : public TIdLogFile
    {
        typedef TIdLogFile inherited;
    
        protected:    
            virtual void __fastcall LogWriteString (const System::UnicodeString AText);
    
        public:
            inline __fastcall TIdLogFileEx (TComponent* AOwner) : inherited(AOwner) {}
            inline __fastcall TIdLogFileEx () : inherited() {}
    
            virtual void __fastcall LogInfo (const System::UnicodeString AText);
    };
    
    void __fastcall TIdLogFileEx::LogInfo (const System::UnicodeString AText)
    {
        LogWriteString(_D("Info : ") + AText + EOL);
    }
    
    void __fastcall TIdLogFileEx::LogWriteString (const System::UnicodeString AText)
    {
        if (FFileStream)
        {
            _di_IIdTextEncoding Encoding = IndyTextEncoding_UTF8();
    
            WriteStringToStream(FFileStream, AText, Encoding);
        }
    }
    

    This solved the problem for my specific use case.