C ++ – Obtenga la "diferencia" de 2 strings como git

Actualmente estoy trabajando en un proyecto que incluye un progtwig de console Win32 en mi PC con Windows 10 y una aplicación para mi teléfono mobile con Windows 10. Se trata de controlar los volúmenes de la session maestra y de audio en mi PC a través de la aplicación en mi Windows Phone.

El "pequeño" problema que tengo ahora es get la "diferencia" entre 2 cadenas.

Tomemos estas 2 cadenas, por ejemplo:

std::string oldVolumes = "MASTER:50:SYSTEM:50:STEAM:100:UPLAY:100"; std::string newVolumes = "MASTER:30:SYSTEM:50:STEAM:100:ROCKETLEAGUE:80:CHROME:100"; 

Ahora quiero comparar estas 2 strings. Digamos que exploto cada cadena en un vector con el ":" como delimitador (tengo una function llamada explotar para cortar la cadena dada por el delimitador y escribir la cadena antes en un vector).

Suficientemente bueno. Pero como puede ver, en la cadena anterior hay UPLAY con el valor 100, pero falta en la nueva cadena. Además, hay 2 nuevos valores (RocketLeague y Chrome), que faltan en el anterior. Pero no solo las "sesiones de audio / nombres" son diferentes, los valores también son diferentes.

Lo que quiero ahora es que cada session, que está en ambas cadenas (como maestro y sistema), compare los valores y si el nuevo valor es diferente al anterior, quiero agregar este cambio a otra cadena, como:

 std::string volumeChanges = "MASTER:30"; // Cause Master is changed, System not 

Si hay una session en la cadena anterior, pero no en la nueva, quiero agregar:

 std::string volumeChanges = "MASTER:30:REMOVE:UPLAY"; 

Si hay una session en la nueva, que falta en la cadena anterior, quiero adjuntarla así:

 std::string volumeChanges = "MASTER:30:REMOVE:UPLAY:ADD:ROCKETLEAGUE:ROCKETLEAGUE:80:ADD:CHROME:CHROME:100"; 

La cadena volumeChanges es solo para mostrarte lo que necesito. Trataré de hacer uno mejor después.

¿Tiene alguna idea de cómo implementar tal comparación? No necesito un ejemplo de código específico o algo así, solo algunas ideas de cómo podría hacerlo en teoría. Es como GIT al less. Si realiza cambios en un file de text, verá en rojo el text eliminado y en verde el agregado. Algo similar a esto, solo con cadenas o vectores de cadenas.

Digamos que exploto cada cadena en un vector con el ":" como delimitador (tengo una function llamada explotar para cortar la cadena dada por el delimitador y escribir la cadena antes en un vector).

Voy a aconsejarte que amplíes esa lógica para separarlos en objects de property que discretamente mantienen un nombre + valor:

 struct property { std::string name; in32_t value; bool same_name(property const& o) const { return name == o.name; } bool same_value(property const& o) const { return value == o.value; } bool operator==(property const& o) const { return same_name(o) && same_value(o); } bool operator<(property const& o) const { if(!same_name(o)) return name < o.name; else return value < o.value; } }; 

Esto simplificará drásticamente la lógica necesaria para determinar qué properties se cambiaron / agregaron / eliminaron.

La lógica para "tokenizar" este tipo de cadena no es demasiado difícil:

 std::set<property> tokenify(std::string input) { bool finding_name = true; property prop; std::set<property> properties; while (input.size() > 0) { auto colon_index = input.find(':'); if (finding_name) { prop.name = input.substr(0, colon_index); finding_name = false; } else { prop.value = std::stoi(input.substr(0, colon_index)); finding_name = true; properties.insert(prop); } if(colon_index == std::string::npos) break; else input = input.substr(colon_index + 1); } return properties; } 

Entonces, la function para get la diferencia:

 std::string get_diff_string(std::string const& old_props, std::string const& new_props) { std::set<property> old_properties = tokenify(old_props); std::set<property> new_properties = tokenify(new_props); std::string output; //We first scan for properties that were either removed or changed for (property const& old_property : old_properties) { auto pnetworkingicate = [&](property const& p) { return old_property.same_name(p); }; auto it = std::find_if(new_properties.begin(), new_properties.end(), pnetworkingicate); if (it == new_properties.end()) { //We didn't find the property, so we need to indicate it was removed output.append("REMOVE:" + old_property.name + ':'); } else if (!it->same_value(old_property)) { //Found the property, but the value changed. output.append(it->name + ':' + std::to_string(it->value) + ':'); } } //Finally, we need to see which were added. for (property const& new_property : new_properties) { auto pnetworkingicate = [&](property const& p) { return new_property.same_name(p); }; auto it = std::find_if(old_properties.begin(), old_properties.end(), pnetworkingicate); if (it == old_properties.end()) { //We didn't find the property, so we need to indicate it was added output.append("ADD:" + new_property.name + ':' + new_property.name + ':' + std::to_string(new_property.value) + ':'); } //The previous loop detects changes, so we don't need to bother here. } if (output.size() > 0) output = output.substr(0, output.size() - 1); //Trim off the last colon return output; } 

Y podemos demostrar que está funcionando con una function main simple:

 int main() { std::string diff_string = get_diff_string("MASTER:50:SYSTEM:50:STEAM:100:UPLAY:100", "MASTER:30:SYSTEM:50:STEAM:100:ROCKETLEAGUE:80:CHROME:100"); std::cout << "Diff String was \"" << diff_string << '\"' << std::endl; } 

Que produce una salida (de acuerdo con IDEONE.com ):

 Diff String was "MASTER:30:REMOVE:UPLAY:ADD:CHROME:CHROME:100:ADD:ROCKETLEAGUE:ROCKETLEAGUE:80" 

Que, aunque los contenidos están en un order ligeramente diferente a su ejemplo, aún contiene toda la información correcta. Los contenidos están en order diferente porque std::set clasificó implícitamente los attributes por nombre al tokenizar las properties; si desea deshabilitar esa sorting, deberá usar una estructura de datos diferente que preserve el order de input. Lo elegí porque elimina los duplicates, lo que podría causar un comportamiento extraño de lo contrario.

En este caso particular, puedes hacerlo de la siguiente manera:

  • Divida las cadenas antiguas y nuevas por el delimitador y almacene los resultados en un vector.
  • Bucle sobre el vector con los datos antiguos. Busque cada palabra en el vector con nuevos datos: por ejemplo, find ("MASTER").
  • Si no se encuentra, agregue "REMOVE: MASTER" a sus resultados.
  • Si se encuentra, compare los numbers y agréguelos a los resultados si se han modificado.
  • La cadena agregada se puede encontrar haciendo un bucle sobre la nueva cadena y buscando las palabras en la cadena anterior.

Sugiero que enumere algunas características (en su caso, por ejemplo: UPLAY presente, ELIMINAR está presente, …)

para cada uno de ellos asignar un peso si las dos cadenas difieren para la característica dada.

Al final, los pesos de resumen para las características se presentan en una cadena y ausentes en la otra y obtienen un número. Este número debe representar lo que estás buscando.

Puede ajustar los pesos hasta que esté satisfecho con el resultado.