Строка.Формат для C++

Поиск реализации для C++ функции, такой как строка .NET.Формат. Очевидно, что есть printf и это разновидности, но я ищу что-то позиционное, как в:

строку.Формат("Привет {0}. Ты {1} лет. Каково это-быть? {1}?"имя, возраст);

Это необходимо, потому что мы собираемся попытаться упростить локализацию нашего приложения и предоставить переводчикам {0} и {1} положение в любом месте предложение намного проще, чем дать им %s, %d, %d, которые должны быть расположены в этом порядке в их переводе.

Я полагаю, что поиск и замена с переменными входами (va_start, va_end и т. д.) - Это то, что я в конечном итоге построю, но если уже есть твердое решение, это было бы предпочтительнее.

спасибо :)

13 ответов



QString QT позволяет вам сделать это:

QString("Hi there %1. You are %2 years old. How does it feel \
         to be %2?").arg(name).arg(age)

Верьте или нет, printf и друзья поддерживают позиционные аргументы.

 #include <stdio.h>

 int main() {
   char *name = "Logan";
   int age = 25;
   printf("Hi there %1$s, you are %2$d years old. How does it feel to be %2$d?\n", name, age);
  return 0;
 }

вы могли бы взглянуть на FastFormat-библиотеки.


Я думаю, вы можете использовать FastFormat, as

std::string result;

fastformat::fmt(result, "Hi there {0}. You are {1} years old. How does it feel to be {1}?", name, age);

, который является почти идентичным синтаксисом.


много хороших рекомендаций, что будет работать в большинстве ситуаций. В моем случае я в конечном итоге хотел загрузить строки из ресурса и сохранить строковые ресурсы как можно ближе к строке .NET.Формат, как я мог, поэтому я свернул свой. После рассмотрения некоторых из приведенных выше реализаций для идей, полученная реализация была довольно короткой и легкой.

существует строка класса, которая в моем случае является производной от CString Microsoft, но она может быть производной от любой строки класс. Существует также класс StringArg -- его задача-взять любой тип параметра и превратить его в строку (т. е. он имитирует ToString в .NET). Если новый объект должен быть Tostring'D, вы просто добавляете другой конструктор. Конструктор позволяет спецификатор формата в стиле printf для форматирования не по умолчанию.

класс String затем принимает идентификатор таблицы string для исходной строки, ряд параметров StringArg и, наконец, необязательный HINSTANCE (я использую много DLL, любой из которых может хост таблицы строк, так что это позволило мне передать его, или использовать DLL-специфическую помеху по умолчанию).

примеры использования:

dlg.m_prompt = String(1417); //"Welcome to Stackoverflow!"
MessageBox(String(1532, m_username)); //"Hi {0}"

как есть, для ввода требуется только идентификатор строки, но было бы тривиально добавить входную строку вместо идентификатора строки:

CString s = String.Format("Hi {0}, you are {1} years old in Hexidecimal", m_userName, StringArg(m_age, "%0X"));

теперь для класса StringArg, который делает эквивалент ToString для переменных:

class StringArg
{
StringArg(); //not implemented
        StringArg(const StringArg&); //not implemented
        StringArg& operator=(const StringArg&); //not implemented

    public:
        StringArg(LPCWSTR val);
    StringArg(const CString& val);
    StringArg(int val, LPCWSTR formatSpec = NULL);
    StringArg(size_t val, LPCWSTR formatSpec = NULL);
    StringArg(WORD val, LPCWSTR formatSpec = NULL);
    StringArg(DWORD val, LPCWSTR formatSpec = NULL);
    StringArg(__int64 val, LPCWSTR formatSpec = NULL);
    StringArg(double val, LPCWSTR formatSpec = NULL);
    CString ToString() const;
private:
    CString m_strVal;
};

extern HINSTANCE GetModuleHInst(); //every DLL implements this for getting it's own HINSTANCE -- scenarios with a single resource DLL wouldn't need this

для класса String существует множество функций-членов и конструкторов, которые занять до 10 аргументов. Они в конечном счете называют CentralFormat, который делает реальную работу.

class String : public CString
{
public:
    String() { }
    String(WORD stringTableID, HINSTANCE hInst = GetModuleHInst()) { Format(stringTableID, hInst); }
    String(WORD stringTableID, const StringArg& arg1, HINSTANCE hInst = GetModuleHInst()) { Format(stringTableID, arg1, hInst); }
    String(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, HINSTANCE hInst = GetModuleHInst()) { Format(stringTableID, arg1, arg2, hInst); }
    String(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, HINSTANCE hInst = GetModuleHInst()) { Format(stringTableID, arg1, arg2, arg3, hInst); }
    String(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, HINSTANCE hInst = GetModuleHInst()) { Format(stringTableID, arg1, arg2, arg3, arg4, hInst); }
    String(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, HINSTANCE hInst = GetModuleHInst()) { Format(stringTableID, arg1, arg2, arg3, arg4, arg5, hInst); }
    String(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, const StringArg& arg6, HINSTANCE hInst = GetModuleHInst()) { Format(stringTableID, arg1, arg2, arg3, arg4, arg5, arg6, hInst); }
    String(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, const StringArg& arg6, const StringArg& arg7, HINSTANCE hInst = GetModuleHInst()) { Format(stringTableID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, hInst); }
    String(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, const StringArg& arg6, const StringArg& arg7, const StringArg& arg8, HINSTANCE hInst = GetModuleHInst()) { Format(stringTableID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, hInst); }
    String(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, const StringArg& arg6, const StringArg& arg7, const StringArg& arg8, const StringArg& arg9, HINSTANCE hInst = GetModuleHInst()) { Format(stringTableID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, hInst); }


    CString& Format(WORD stringTableID, HINSTANCE hInst = GetModuleHInst());
    CString& Format(WORD stringTableID, const StringArg& arg1, HINSTANCE hInst = GetModuleHInst());
    CString& Format(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, HINSTANCE hInst = GetModuleHInst());
    CString& Format(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, HINSTANCE hInst = GetModuleHInst());
    CString& Format(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, HINSTANCE hInst = GetModuleHInst());
    CString& Format(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, HINSTANCE hInst = GetModuleHInst());
    CString& Format(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, const StringArg& arg6, HINSTANCE hInst = GetModuleHInst());
    CString& Format(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, const StringArg& arg6, const StringArg& arg7, HINSTANCE hInst = GetModuleHInst());
    CString& Format(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, const StringArg& arg6, const StringArg& arg7, const StringArg& arg8, HINSTANCE hInst = GetModuleHInst());
    CString& Format(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, const StringArg& arg6, const StringArg& arg7, const StringArg& arg8, const StringArg& arg9, HINSTANCE hInst = GetModuleHInst());
private:
    void CentralFormat(WORD stringTableID, std::vector<const StringArg*>& args, HINSTANCE hInst);
};

наконец, реализация (надеюсь, это нормально, чтобы опубликовать это на StackOverflow, хотя основная часть этого очень проста):

StringArg::StringArg(LPCWSTR val)
{
    m_strVal = val;
}

StringArg::StringArg(const CString& val)
{
    m_strVal = (LPCWSTR)val;
}

StringArg::StringArg(int val, LPCWSTR formatSpec)
{
    if(NULL == formatSpec)
        formatSpec = L"%d"; //GLOK
    m_strVal.Format(formatSpec, val);
}

StringArg::StringArg(size_t val, LPCWSTR formatSpec)
{
    if(NULL == formatSpec)
        formatSpec = L"%u"; //GLOK
    m_strVal.Format(formatSpec, val);
}

StringArg::StringArg(WORD val, LPCWSTR formatSpec)
{
    if(NULL == formatSpec)
        formatSpec = L"%u"; //GLOK
    m_strVal.Format(formatSpec, val);
}

StringArg::StringArg(DWORD val, LPCWSTR formatSpec)
{
    if(NULL == formatSpec)
        formatSpec = L"%u"; //GLOK
    m_strVal.Format(formatSpec, val);
}

StringArg::StringArg(__int64 val, LPCWSTR formatSpec)
{
    if(NULL == formatSpec)
        formatSpec = L"%I64d"; //GLOK
    m_strVal.Format(formatSpec, val);
}

StringArg::StringArg(double val, LPCWSTR formatSpec)
{
    if(NULL == formatSpec)
        formatSpec = L"%f"; //GLOK
    m_strVal.Format(formatSpec, val);
}

CString StringArg::ToString() const
{ 
    return m_strVal; 
}


void String::CentralFormat(WORD stringTableID, std::vector<const StringArg*>& args, HINSTANCE hInst)
{
    size_t argsCount = args.size();
    _ASSERT(argsCount < 10); //code below assumes a single character position indicator

    CString tmp;
    HINSTANCE hOld = AfxGetResourceHandle();
    AfxSetResourceHandle(hInst);
    BOOL b = tmp.LoadString(stringTableID);
    AfxSetResourceHandle(hOld);
    if(FALSE == b)
    {
#ifdef _DEBUG

        //missing string resource, or more likely a bad stringID was used -- tell someone!!
    CString s;
        s.Format(L"StringID %d could not be found!  %s", stringTableID, hInst == ghCommonHInst ? L"CommonHInst was passed in" : L"CommonHInst was NOT passed in"); //GLOK
        ::MessageBeep(MB_ICONHAND);
        ::MessageBeep(MB_ICONEXCLAMATION);
        ::MessageBeep(MB_ICONHAND);
        _ASSERT(0);
        ::MessageBox(NULL, s, L"DEBUG Error - Inform Development", MB_ICONSTOP | MB_OK | MB_SERVICE_NOTIFICATION); //GLOK
        }
#endif //_DEBUG

    CString::Format(L"(???+%d)", stringTableID); //GLOK
        return;
    }

    //check for the degenerate case
    if(0 == argsCount)
    {
        CString::operator=(tmp);
        return;
    }

    GetBuffer(tmp.GetLength() * 3); //pre-allocate space
    ReleaseBuffer(0);
    LPCWSTR pStr = tmp;
    while(L'' != *pStr)
    {
        bool bSkip = false;

        if(L'{' == *pStr)
        {
            //is this an incoming string position?
            //we only support 10 args, so the next char must be a number
            if(wcschr(L"0123456789", *(pStr + 1))) //GLOK
            {
                if(L'}' == *(pStr + 2)) //and closing brace?
                {
                    bSkip = true;

                    //this is a replacement
                    size_t index = *(pStr + 1) - L'0';
                    _ASSERT(index < argsCount);
                    _ASSERT(index >= 0);
                    if((index >= 0) && (index < argsCount))
                        CString::operator+=(args[index]->ToString());
                    else
                    {
//bad positional index

                        CString msg;
                        msg.Format(L"(??-%d)", index); //GLOK
                        CString::operator+=(msg);
                    }
                    pStr += 2; //get past the two extra characters that we skipped ahead and peeked at
                }
            }
        }

        if(false == bSkip)
            CString::operator+=(*pStr);
        pStr++;
    }
}


CString& String::Format(WORD stringTableID, HINSTANCE hInst)
{
    std::vector<const StringArg*> args;
    CentralFormat(stringTableID, args, hInst);
    return *this;
}

CString& String::Format(WORD stringTableID, const StringArg& arg1, HINSTANCE hInst)
{
    std::vector<const StringArg*> args;
    args.push_back(&arg1);
    CentralFormat(stringTableID, args, hInst);
    return *this;
}

CString& String::Format(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, HINSTANCE hInst)
{
    std::vector<const StringArg*> args;
    args.push_back(&arg1);
    args.push_back(&arg2);
    CentralFormat(stringTableID, args, hInst);
    return *this;
}

CString& String::Format(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, HINSTANCE hInst)
{
    std::vector<const StringArg*> args;
    args.push_back(&arg1);
    args.push_back(&arg2);
    args.push_back(&arg3);
    CentralFormat(stringTableID, args, hInst);
    return *this;
}

CString& String::Format(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, HINSTANCE hInst)
{
    std::vector<const StringArg*> args;
    args.push_back(&arg1);
    args.push_back(&arg2);
    args.push_back(&arg3);
    args.push_back(&arg4);
    CentralFormat(stringTableID, args, hInst);
    return *this;
}

CString& String::Format(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, HINSTANCE hInst)
{
    std::vector<const StringArg*> args;
    args.push_back(&arg1);
    args.push_back(&arg2);
    args.push_back(&arg3);
    args.push_back(&arg4);
    args.push_back(&arg5);
    CentralFormat(stringTableID, args, hInst);
    return *this;
}

CString& String::Format(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, const StringArg& arg6, HINSTANCE hInst)
{
    std::vector<const StringArg*> args;
    args.push_back(&arg1);
    args.push_back(&arg2);
    args.push_back(&arg3);
    args.push_back(&arg4);
    args.push_back(&arg5);
    args.push_back(&arg6);
    CentralFormat(stringTableID, args, hInst);
    return *this;
}

CString& String::Format(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, const StringArg& arg6, const StringArg& arg7, HINSTANCE hInst)
{
    std::vector<const StringArg*> args;
    args.push_back(&arg1);
    args.push_back(&arg2);
    args.push_back(&arg3);
    args.push_back(&arg4);
    args.push_back(&arg5);
    args.push_back(&arg6);
    args.push_back(&arg7);
    CentralFormat(stringTableID, args, hInst);
    return *this;
}

CString& String::Format(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, const StringArg& arg6, const StringArg& arg7, const StringArg& arg8, HINSTANCE hInst)
{
    std::vector<const StringArg*> args;
    args.push_back(&arg1);
    args.push_back(&arg2);
    args.push_back(&arg3);
    args.push_back(&arg4);
    args.push_back(&arg5);
    args.push_back(&arg6);
    args.push_back(&arg7);
    args.push_back(&arg8);
    CentralFormat(stringTableID, args, hInst);
    return *this;
}

CString& String::Format(WORD stringTableID, const StringArg& arg1, const StringArg& arg2, const StringArg& arg3, const StringArg& arg4, const StringArg& arg5, const StringArg& arg6, const StringArg& arg7, const StringArg& arg8, const StringArg& arg9, HINSTANCE hInst)
{
    std::vector<const StringArg*> args;
    args.push_back(&arg1);
    args.push_back(&arg2);
    args.push_back(&arg3);
    args.push_back(&arg4);
    args.push_back(&arg5);
    args.push_back(&arg6);
    args.push_back(&arg7);
    args.push_back(&arg8);
    args.push_back(&arg9);
    CentralFormat(stringTableID, args, hInst);
    return *this;
}

несколько вариантов:

  • библиотека формата boost (уже упоминалось)
  • stringstreams
  • устаревшие функции printf / sprintf
  • пользовательская реализация с использованием регулярных выражений или встроенных строковых функций

на соответствующей ноте то, о чем вы говорите, совершенно неадекватно для локализации.


iostream:

stringstream s;
string a;
s << "this is string a: " << a << endl;

вы можете форматировать как sprintf (google для "формата iostream") и его в стандарте C++.


Если вы собираетесь писать свои собственные, поиск и замена, вероятно, не лучший подход, так как большинство методов поиска/замены позволяют заменять только по одному за раз и делать очень плохую работу, разрешая символы escpae (например, если вы хотите включить литеральную строку {0} в свой выходной.

вам гораздо лучше написать свою собственную конечную машину, чтобы пройти через входную строку, генерируя выходную строку на лету за один проход. Это позволяет обрабатывать escape символы и более сложные функции вывода (например, локализованные даты {0:dd\MM\yyyy} например). Это даст вам больше гибкости в дополнение к тому, что быстрее, чем поиск/замена или регулярное выражение.


Ориентация Окна? FormatMessage () - твой друг


Если вы должны быть кросс-платформенным, то я бы проголосовал за boost::format или, возможно, ICU. Если вы должны поддерживать только Windows, то FormatMessage (или удобную оболочку этого, CString::FormatMessage, если вы используете MFC)

можно посмотреть ЗДЕСЬ Для сравнения:http://www.mihai-nita.net/article.php?artID=20060430a


некоторое время назад, я пытался сделать нечто подобное, но с некоторыми дополнительными предположениями:

  • нет поддержки позиционного форматирования (поэтому я думаю, что это не для вас)
  • c++2k3 (чтобы иметь возможность включать его с некоторыми старыми кодами)
  • (почти) нет зависимостей (даже crt, поэтому нет зависимостей sprintf)

Я потерпел неудачу в этом, и это полностью незакончено, но вы все еще можете посмотреть на некоторые из результаты:

http://code.google.com/p/pileofcrap/source/browse/tests_format.cpp

http://code.google.com/p/pileofcrap/source/browse/format/Format.h


В дополнение к вариантам, предложенным другими, я могу рекомендовать библиотека fmt, который реализует форматирование строк, похожими на str.format в Python и String.Format в C#. Вот пример:

string result = fmt::format("Hi {0}. You are {1} years old.", name, age);

библиотека полностью безопасна и является быстрее чем формат Boost.

отказ от ответственности: я автор этой библиотеки.