2010-03-30 14 views
8

Quiero una biblioteca simple de plantillas basadas en cadenas C++ para reemplazar cadenas en tiempo de ejecución.Biblioteca de plantillas de cadenas C++

Por ejemplo, voy a utilizar

string template = "My name is {{name}}"; 

En tiempo de ejecución, quiero que el nombre se cambió basado en uno real.

me encontré con un ejemplo, www.stringtemplate.org pero poco de miedo cuando habla de sus antlr etc.

+4

La etiqueta 'plantillas' no es apropiada aquí. Reemplazar cadenas dentro de una cadena de plantilla no es lo mismo que el significado habitual de las plantillas en un contexto de C++. –

+0

Por 'actual', ¿quiere decir una variable llamada 'nombre' definida en el alcance de la plantilla de cadena? –

+0

¿Cuál es la pregunta? –

Respuesta

14

Actualización: El proyecto se ha trasladado a Github y renombrado en ctemplate: https://github.com/OlafvdSpek/ctemplate

Desde la nueva página del proyecto:

originalmente fue llamado Google plantillas, debido a su origen que el sistema de plantillas utilizado para las páginas de resultados de búsqueda de Google. Ahora tiene un nombre más general que coincide con su naturaleza de propiedad comunitaria.


Ha intentado biblioteca ctemplate de Google? Parece ser exactamente lo que busca: http://code.google.com/p/google-ctemplate/

Tu ejemplo podría ser implementado como esto:

En el ejemplo.TPL:

Mi nombre es {{nombre}}

En example.cc:

#include <stdlib.h> 
#include <string> 
#include <iostream> 
#include <google/template.h> 

int main(int argc, char** argv) 
{ 
    google::TemplateDictionary dict("example"); 
    dict.SetValue("name", "John Smith"); 
    google::Template* tpl = google::Template::GetTemplate("example.tpl", 
                 google::DO_NOT_STRIP); 
    std::string output; 
    tpl->Expand(&output, &dict); 
    std::cout << output; 
    return 0; 
} 

continuación:

$ gcc example.cc -lctemplate -pthread 

$ ./a.out 

Mi nombre es John Smith

Tenga en cuenta que también hay una forma de escribir plantillas como cadenas de texto si no desea molestarse en escribir sus plantillas en archivos separados.

+1

https://github.com/OlafvdSpek/ctemplate ¿es este el proyecto? La página de ejemplo tiene un ejemplo similar https://htmlpreview.github.io/?https://github.com/OlafvdSpek/ctemplate/blob/master/doc/index.html – TankorSmash

5

¿Se puede utilizar sprintf?

También hay boost::format si desea incluir boost.

+3

Me sorprende que no recibas amenazas de muerte por mencionar siquiera' sprintf'. Pero eso es lo que uso, así que +1 :) –

+1

'snprintf' para evitar saturaciones de búfer. Ver http://libslack.org/manpages/snprintf.3.html – Atmocreations

+1

\ * death threat \ * @John: Ahí, ahora esta respuesta está completa. – GManNickG

4

Si usted tiene una función que reemplaza todas las apariciones de una cadena con otra cadena:

std::string replace_all(std::string str, const std::string &remove, const std::string &insert) 
{ 
    std::string::size_type pos = 0; 
    while ((pos = str.find(remove, pos)) != std::string::npos) 
    { 
     str.replace(pos, remove.size(), insert); 
     pos++; 
    } 

    return str; 
} 

entonces usted puede hacer esto:

std::string pattern = "My name is {{first_name}} {{last_name}} and I live in {{location}}"; 

std::string str = replace_all(replace_all(replace_all(pattern, 
         "{{first_name}}", "Homer"), 
         "{{last_name}}", "Simpson"), 
         "{{location}}", "Springfield"); 
+0

solo para ser delicado (muy poco probable que suceda en la práctica): si en lugar de 'Homer' pones '{{last_name}}' como valor de '{{first_name}}' no obtendrás el resultado deseado ... –

+2

@Andre Holzner - ¡Depende del resultado deseado! –

0
string skeleton = "My name is {{name}}"; 
string placeholder = "{{name}}"; 
string::size_type pos = skeleton.find(placeholder); 
while(pos != string::npos) { 
    skeleton.replace(pos, placeholder.length(), "Gopalakrishnan"); 
    pos = skeleton.find(placeholder, ++pos); 
} 
+0

Si tiene otras etiquetas, puede almacenarlas en un contenedor y luego concluir el ciclo con otro ciclo que atraviese el contenedor, aplicando así el ciclo de reemplazo con cada una de las etiquetas en la cadena de entrada. Avíseme si tiene otras etiquetas y actualizaré el código de muestra. – wilhelmtell

0

Podría ser una exageración, pero también puede echar un vistazo a boost::spirit, y más específicamente, la parte 'karma' que es un generador de texto.

0

Si tiene muchos marcadores de posición, por ejemplo, si tiene una plantilla de macro para una letra que desea expandir automáticamente, una tokenización básica sería más fácil de mantener, implementar y extender más adelante. Por ejemplo

//pseudocode 
foreach word in line 
{ 
    if word=={{name}} print getFromDB(name,entry) 
    else if word=={{address}} print getFromDB(address,entry) 
    .. 
    .. 
    else print word 

/* 
* to get rid of if-else-if tree, you can just check if starts with {{ and ends with }} and directly query the string against a db/map/hash 
*/ 

} 

Sin embargo, si el problema es una bastante simple, y la plantilla es lo suficientemente pequeño, sólo tiene que ir a una de las respuestas mencionadas anteriormente.

1

¿Ha considerado un conjunto de funciones en línea que utilizan ostringstram en lugar de "plantillas de cadena"?

inline std::string name_template(const std::string& name) 
{ 
    std::ostringstream os; 
    os << "My name is " << name; 
    return os.str(); 
} 

Hay otros enfoques alternativos si necesita más generalidad. Por ejemplo, una jerarquía de clases donde la base proporciona una interfaz de "formato" y las clases secundarias la implementan con la implementación variable correspondiente.

3

Si es nuevo en C++, agregar nuevas bibliotecas (plantilla u otras) a su instalación solo aumentará la curva de aprendizaje. Esto es algo que puede hacer de manera simple, elegante y eficiente con las características incorporadas.

A diferencia de respuestas similares, este código hace sólo un pase de entrada y escalas bien con grandes diccionarios:

// header 
#include <map> 
#include <sstream> 

typedef std::map< std::string, std::string > subst_map; 

// implementation 
using namespace std; 

string do_substitutions(string const &in, subst_map const &subst) { 
    ostringstream out; 
    size_t pos = 0; 
    for (;;) { 
     size_t subst_pos = in.find("{{", pos); 
     size_t end_pos = in.find("}}", subst_pos); 
     if (end_pos == string::npos) break; 

     out.write(&* in.begin() + pos, subst_pos - pos); 

     subst_pos += strlen("{{"); 
     subst_map::const_iterator subst_it 
      = subst.find(in.substr(subst_pos, end_pos - subst_pos)); 
     if (subst_it == subst.end()) throw runtime_error("undefined substitution"); 

     out << subst_it->second; 
     pos = end_pos + strlen("}}"); 
    } 
    out << in.substr(pos, string::npos); 
    return out.str(); 
} 

// usage 
pair< string, string > substitutions_init[] = { 
    make_pair("firstname", "homer"), 
    make_pair("lastname", "simpson") 
}; 
subst_map substitutions 
    (substitutions_init, substitutions_init + sizeof(substitutions_init)/sizeof(*substitutions_init)); 

int main() { 
    cerr << do_substitutions("Mr. {{lastname}}, {{firstname}} esquire", substitutions) << endl; 
} 
0

Puede echar un vistazo a Inja. Es un motor de plantillas simple y solo de encabezado y hace lo que está pidiendo. Allí puede simplemente llamar al

data["name"] = "world"; 
inja::render("Hello {{ name }}!", data); // Returns "Hello world!" 
Cuestiones relacionadas