c++ localization string.format printf

String.Format para C++



localization printf (13)

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

String.Format ("Hola, {0}. Tienes {1} años. ¿Qué se siente ser {1}?", Nombre, edad);

Esto es necesario porque vamos a intentar facilitar la localización de nuestra aplicación, y darles a los traductores {0} y {1} la posición en cualquier lugar 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 hay una solución sólida, sería preferible.

Gracias :)


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)


Si vas a escribir el tuyo propio, buscar y reemplazar no es probablemente el mejor enfoque, ya que la mayoría de los métodos de búsqueda / reemplazo solo te 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 resultado.

Es mucho mejor escribir su propia máquina de estado finito para caminar a través de la cadena de entrada, generando una cadena de salida sobre la marcha en una sola pasada. Esto le permite manejar caracteres de escape y funciones de salida más complicadas (como las fechas localizadas {0:dd/MM/yyyy} por ejemplo). Te dará mucho más flexibilidad además de ser más rápido que un enfoque de búsqueda / reemplazo o de expresión regular.


Varias opciones:

  • biblioteca de formato boost (ya mencionada)
  • cadenas
  • funciones heredadas 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 ++.



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


Creo que puedes 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 sintaxis idéntica.


Muchas de las buenas recomendaciones anteriores funcionarían en la mayoría de las situaciones. En mi caso, en última instancia, quería cargar cadenas desde un recurso, Y mantener los recursos de cadena lo más cerca posible de .NET String.Format, por lo que rodé 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 derivar de cualquier clase de cadena. También hay una clase StringArg: su función es tomar cualquier tipo de parámetro y convertirlo en una cadena (es decir, imita a ToString en .NET). Si un objeto nuevo necesita ser ToString''d, simplemente agrega otro constructor. El constructor permite un especificador de formato de estilo printf para el formato no predeterminado.

La clase String acepta una ID de tabla de cadenas para la cadena de origen, una serie de parámetros de StringArg y finalmente un HINSTANCE opcional (yo uso muchos DLL, cualquiera de los cuales podría alojar la tabla de cadenas, así que esto me permitió pasarlo, o use un HINSTANCE específico de DLL por defecto).

Ejemplos de uso:

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

Tal como está, solo se 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 grupo de funciones miembro y constructores que toman hasta 10 argumentos. Estos finalmente llaman a 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 (es de esperar que esté bien publicar esto mucho en , 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; }


Si debiera ser multiplataforma, votaría por el formato boost ::, o tal vez ICU. Si solo debería ser compatible con Windows, entonces FormatMessage (o el envoltorio conveniente de eso, CString :: FormatMessage, si usa MFC)

Puede echar un vistazo aquí para 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 hay soporte para el formato posicional (así que supongo que es un no ir 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 sprintf)

He fallado, y está totalmente sin terminar, pero aún puedes ver algunos resultados:

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

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


Puede echar un vistazo a la FastFormat -library.



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 totalmente segura y es mucho más rápida que Boost Format.

Descargo de responsabilidad: soy el autor de esta biblioteca.