Pregunta

Buscando una implementación para C ++ de una función como String.Format de .NET. Obviamente hay printf y sus variedades, pero estoy buscando algo que sea posicional como en:

  

String.Format (" Hola, {0}. Estás   {1} años de edad. Como se siente ser   {1}? & Quot ;, nombre, edad);

Esto es necesario porque vamos a tratar de facilitar la localización de nuestra aplicación, y darles a los traductores {0} y {1} la posición en cualquier parte de la oración es mucho más fácil que darles un% s,% d,% d que debe colocarse en ese orden en su traducción.

Supongo que buscar y reemplazar con entradas variables (va_start, va_end, etc.) es lo que terminaré construyendo, pero si ya existe una solución sólida, sería preferible.

Gracias :)

¿Fue útil?

Solución 6

Muchas buenas recomendaciones anteriores funcionarían en la mayoría de las situaciones. En mi caso, en última instancia, quería cargar cadenas de un recurso, Y mantener los recursos de cadena lo más cerca posible de .NET String.Format como pude, así que hice el mío. Después de ver algunas de las implementaciones anteriores para obtener ideas, la implementación resultante fue bastante corta y fácil.

Hay una clase String, que en mi caso deriva de CString de Microsoft, pero podría derivarse de cualquier clase de cadena. También hay una clase StringArg: su trabajo es tomar cualquier tipo de parámetro y convertirlo en una cadena (es decir, imita ToString en .NET). Si un nuevo objeto necesita ser ToString'd, simplemente agrega otro constructor. El constructor permite un especificador de formato de estilo printf para formateo no predeterminado.

La clase String luego acepta una ID de tabla de cadena para la cadena de origen, una serie de parámetros de StringArg y, finalmente, un HINSTANCE opcional (uso muchas DLL, cualquiera de las cuales podría alojar la tabla de cadena, por lo que esto me permitió pasar o use un HINSTANCE específico de DLL de forma predeterminada).

Ejemplos de uso:

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

Tal como está, solo necesita una ID de cadena para una entrada, pero sería trivial agregar una cadena de entrada en lugar de una ID de cadena:

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

Ahora para la clase StringArg que hace el equivalente de ToString en las variables:

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

Para la clase String, hay un montón de funciones miembro y constructores que toman hasta 10 argumentos. En última instancia, se llama CentralFormat, que hace el trabajo real.

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);
};

Finalmente, la implementación (espero que esté bien publicar esto en StackOverflow, aunque la mayor parte es muy simple):

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'\0' != *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;
}

Otros consejos

QString de QT le permite hacer esto:

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

Créalo o no, printf y sus amigos apoyan los argumentos posicionales.

 #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;
 }

Puede echar un vistazo a la FastFormat -library.

Creo que puede usar FastFormat , como

std::string result;

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

que es casi la misma sintaxis.

Varias opciones:

  • biblioteca de formatos de impulso (ya mencionada)
  • cadenas de cadena
  • funciones antiguas de printf / sprintf
  • implementación personalizada utilizando expresiones regulares o funciones de cadena incorporadas

En una nota relacionada, de lo que estás hablando es completamente inadecuado para la localización.

iostream:

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

Puede formatear como sprintf (google para " formato iostream ") y está en el estándar C ++.

Si va a escribir el suyo propio, buscar y reemplazar probablemente no sea el mejor enfoque, ya que la mayoría de los métodos de búsqueda / reemplazo solo le permiten reemplazar uno a la vez, y hacen un trabajo muy pobre al permitir caracteres escpae ( como si desea incluir la cadena literal {0} en su salida.

Es mucho mejor escribir su propia máquina de estado finito para recorrer la cadena de entrada, generando una cadena de salida sobre la marcha en una pasada. Esto le permite manejar caracteres de escape y funciones de salida más complicadas (como fechas localizadas {0: dd \ MM \ aaaa} por ejemplo). Le dará más flexibilidad además de ser más rápido que un enfoque de búsqueda / reemplazo o regex.

¿Apuntando a Windows? FormatMessage () es tu amigo

Si fuera multiplataforma, votaría por el formato boost ::, o quizás por la UCI. Si solo debe admitir Windows, FormatMessage (o el contenedor conveniente de eso, CString :: FormatMessage, si usa MFC)

Puede ver aquí una comparación: http: // www. mihai-nita.net/article.php?artID=20060430a

Hace algún tiempo, estaba tratando de hacer algo así, pero con algunas suposiciones adicionales:

  • no es compatible con el formato posicional (así que supongo que es un no para ti)
  • c ++ 2k3 (para poder incorporarlo con algunos códigos más antiguos)
  • (casi) sin dependencias (incluso crt, por lo que no hay dependencias de sprintf)

He fallado y está totalmente inacabado, pero aún puede ver algunos de los resultados:

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

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

Además de las opciones sugeridas por otros, puedo recomendar la biblioteca fmt que implementa un formato de cadena similar a str.format en Python y String.Format en C #. Aquí hay un ejemplo:

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

La biblioteca es completamente segura y es mucho más rápido que Boost Format.

Descargo de responsabilidad: soy el autor de esta biblioteca.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top