Примеры моего кода на C++
1. Консольное приложение - построение файла различий между двумя произвольными
текстовыми файлами (исходным и полученным после редактирования),
применение файла различий (обратное получение исходного файла).
исходный файл-source file
файл после редактирования-target file
файл изменений-changeset file
файл difr.h
//---------------------------------------------------------------------------
// SCCS
// Обработка изменений в текстовых файлах
// (c) Vladimir Mishenin
// email: bobm@mail.ru
#ifndef difrH
#define difrH
#include <stdio.h>
#include <vector>
#include <string>
#include <list>
//---------------------------------------------------------------------------
namespace Difference {
//макс длина строки
static const int MAXSIZESTR = 4096;
//длина контекста
static const int EXSIZECTX = 1;
//---------------------------------------------------------------------------
//эл-т для хранения в контейнере
struct StrItem {
std::string sval;
};
//эл-т для хранения в контейнере
struct StrExtItem {
StrExtItem(void) { shash = 0.; }
void CalcHash(void);
std::string sval;
double shash;
};
//---------------------------------------------------------------------------
//базовый шаблонный класс контейнера с ф-ями ввода/вывода
template <typename item>
class IOcont {
public:
std::vector<item> Items;
bool ReadItems(const char* fn);
bool WriteItems(const char* fn);
virtual ~IOcont(void) { Items.clear(); }
};
//прочитать строки из файла fn в контейнер
template <typename item>
bool IOcont<item>::ReadItems(const char* fn)
{
FILE* fl;
if((fl = fopen(fn, "rt")) != NULL) {
char buf[MAXSIZESTR];
int len;
item itm;
while(fgets(buf, MAXSIZESTR, fl)) {
len = (int)strlen(buf);
if(len > 0) {
while(buf[len-1] == '\n' || buf[len-1] == '\r') {
buf[len-1] = 0;
if(--len < 1) break;
}
}
itm.sval = buf;
Items.push_back(itm);
}
fclose(fl);
return true;
}
return false;
}
//записать строки из контейнера в файл fn
template <typename item>
bool IOcont<item>::WriteItems(const char* fn)
{
FILE* fl;
if((fl = fopen(fn, "wt")) != NULL) {
for(std::vector<item>::iterator it = Items.begin();
it!= Items.end(); ++it) {
fputs(it->sval.c_str(), fl);
fputs("\n", fl);
}
fclose(fl);
return true;
}
return false;
}
//---------------------------------------------------------------------------
//производные классы контейнеров
//класс хранения текста
class DataCont : public IOcont<StrExtItem> {
public:
void CalcHashAll(void);
int FindContext(const DataCont &ctx, int &idxbeg, bool beg = false) const;
};
//класс описатель действия
struct Action {
Action(void) { beg = false; act = 0; }
bool IsOk(void);
void Clear(void);
char act; //I-insert D-delete R-replace
DataCont after; //блок AFTER
bool beg; //контекст after дб строго от начала
DataCont dt1; //блок INSERT DELETE REPLACE
DataCont dt2; //блок ON (для REPLACE)
};
//последовательность действий
typedef std::list<Action> Actions;
//класс хранения комманд
class CmdCont : public IOcont<StrItem> {
public:
void Actions2Cmd(const Actions &a);
void Cmd2Actions(Actions &a);
private:
void CopyVec(const DataCont &vec);
StrItem cmd;
};
//---------------------------------------------------------------------------
//класс-оболочка задачи
class Difr {
DataCont inp;
DataCont out;
CmdCont cmd;
const char* errstr;
bool CreateCmdProc(Actions &a);
bool ApplyCmdProc(Actions &a);
void WriteAction(int iix, int iox, int iframex, int oframex, Actions &a);
void WriteContext(int oframe, Action &act);
public:
Difr(void);
virtual ~Difr(void);
bool CreateCmd(const char* inpfn, const char* outfn, const char* cmdfn);
bool ApplyCmd(const char* inpfn, const char* outfn, const char* cmdfn);
const char* GetLastError(void);
};
}; //namespace Difference
#endif
файл difr.cpp
//---------------------------------------------------------------------------
// SCCS
// Обработка изменений в текстовых файлах
// (c) Vladimir Mishenin
// email: bobm@mail.ru
#include "stdafx.h"
#include "difr.h"
//---------------------------------------------------------------------------
namespace Difference {
//---------------------------------------------------------------------------
//посчитать shash для строки sval
void StrExtItem::CalcHash(void)
{
const double e = 1.1111111;
int l = (int)sval.size();
shash = 0.;
for(int i = 0; i < l; ++i)
shash += sval[i] * (i+1) * e;
}
//---------------------------------------------------------------------------
//посчитать shash для всех строк контейнера
void DataCont::CalcHashAll(void)
{
for(std::vector<StrExtItem>::iterator it = Items.begin();
it != Items.end(); ++it) it->CalcHash();
}
//найти в Items контекст ctx, в idxbeg вернуть индекс начала контекста
//возврат: 0-не найден 1-найден >1-неоднозначность,несколько
//beg=true-контекст дб строго от начала
int DataCont::FindContext(const DataCont &ctx, int &idxbeg, bool beg) const
{
int ret = 0;
int sz1 = (int)Items.size();
int sz2 = (int)ctx.Items.size();
int i2 = 0;
idxbeg = 0;
if(sz2 < 1) return 1;
for(int i1 = 0; i1 < sz1; ++i1) {
if(Items[i1].shash == ctx.Items[i2].shash) {
int ti1 = i1;
for(;;) {
if(++i2 >= sz2) {
idxbeg = ti1;
if(++ret > 1) return ret;
if(beg) return ret;
else break;
}
if(++i1 >= sz1) break;
if(Items[i1].shash != ctx.Items[i2].shash) {
if(beg) return ret;
break;
}
}
i1 = ti1;
i2 = 0;
}
else if(beg) return ret;
}
return ret;
}
//---------------------------------------------------------------------------
//проверить корректность действия
bool Action::IsOk(void)
{
if(dt1.Items.empty()) return false;
if(act != 'I' && act != 'D' && act != 'R') return false;
if(act == 'R' && dt2.Items.empty()) return false;
return true;
}
//очистить действие
void Action::Clear(void)
{
after.Items.clear();
dt1.Items.clear();
dt2.Items.clear();
beg = false;
act = 0;
}
//---------------------------------------------------------------------------
//записать последовательность действий a в свой контейнер комманд Items
void CmdCont::Actions2Cmd(const Actions &a)
{
for(Actions::const_iterator it = a.begin(); it != a.end(); ++it) {
cmd.sval = "AFTER";
Items.push_back(cmd);
if(it->beg) {
cmd.sval = "BEGIN";
Items.push_back(cmd);
}
if(it->after.Items.size() > 0)
CopyVec(it->after);
switch(it->act) {
case 'D':
cmd.sval = "DELETE";
Items.push_back(cmd);
CopyVec(it->dt1);
break;
case 'I':
cmd.sval = "INSERT";
Items.push_back(cmd);
CopyVec(it->dt1);
break;
case 'R':
default:
cmd.sval = "REPLACE";
Items.push_back(cmd);
CopyVec(it->dt1);
cmd.sval = "ON";
Items.push_back(cmd);
CopyVec(it->dt2);
break;
}
}
}
//копировать строки из вектора StrExtItem в свой контейнер комманд Items
void CmdCont::CopyVec(const DataCont &vec)
{
for(std::vector<StrExtItem>::const_iterator it = vec.Items.begin();
it != vec.Items.end(); ++it) {
cmd.sval = it->sval;
Items.push_back(cmd);
}
}
//---------------------------------------------------------------------------
//записать свой контейнер комманд Items в последовательность действий a
void CmdCont::Cmd2Actions(Actions &a)
{
Action act;
StrExtItem itm;
enum mode {waitcmd, after, dt1, dt2} md = waitcmd;
bool actrdn = false; //действие считано
for(std::vector<StrItem>::const_iterator it = Items.begin();
it != Items.end(); ++it) {
switch(md) {
default:
case waitcmd:
if(it->sval == "AFTER") {
if(actrdn)
if(act.IsOk()) a.push_back(act);
actrdn = true;
md = after;
act.Clear();
}
break;
case after:
if(it->sval == "INSERT") { act.act = 'I'; md = dt1; }
else if(it->sval == "DELETE") { act.act = 'D'; md = dt1; }
else if(it->sval == "REPLACE") { act.act = 'R'; md = dt1; }
else if(it->sval == "BEGIN") { act.beg = true; }
else {
itm.sval = it->sval;
act.after.Items.push_back(itm);
}
break;
case dt1:
if(it->sval == "AFTER") {md = waitcmd; --it; }
else if(act.act == 'R' && it->sval == "ON") {md = dt2; }
else {
itm.sval = it->sval;
act.dt1.Items.push_back(itm);
}
break;
case dt2:
if(it->sval == "AFTER") {md = waitcmd; --it; }
else {
itm.sval = it->sval;
act.dt2.Items.push_back(itm);
}
break;
} //switch
} //for
if(actrdn)
if(act.IsOk()) a.push_back(act);
}
//---------------------------------------------------------------------------
//класс-оболочка задачи
//сообщ об ошибках
const char* const NOERR = "";
const char* const NOTREAD = "Error reading file";
const char* const NOTWRITE = "Error writing file";
const char* const NOTFOUNDCTX = "Required context not found";
const char* const NOTAPPLAMB = "Change set applying is ambiguous";
Difr::Difr(void)
{
errstr = NOERR;
}
Difr::~Difr(void)
{
}
const char* Difr::GetLastError(void)
{
return errstr;
}
//---------------------------------------------------------------------------
//читать inp и out создать cmd
bool Difr::CreateCmd(const char* inpfn, const char* outfn, const char* cmdfn)
{
inp.Items.clear();
out.Items.clear();
cmd.Items.clear();
if(!inp.ReadItems(inpfn) || !out.ReadItems(outfn)) {
errstr = NOTREAD;
return false;
}
inp.CalcHashAll();
out.CalcHashAll();
Actions acts; //последовательность действий
if(!CreateCmdProc(acts)) return false;
cmd.Actions2Cmd(acts);
if(!cmd.WriteItems(cmdfn)) {
errstr = NOTWRITE;
return false;
}
errstr = NOERR;
return true;
}
//пройти по inp и out, созд действия, записать в контейнер a
bool Difr::CreateCmdProc(Actions &a)
{
int szi = (int)inp.Items.size();
int szo = (int)out.Items.size();
int ii = 0, io = 0;
int iframe, oframe;
int found = 1;
for( ; ii <= szi-1 && io <= szo-1; ++ii, ++io) { //проход по одинаковым
if(inp.Items[ii].shash != out.Items[io].shash) { //разные
iframe = ii;
oframe = io;
found = 0;
int szctx = EXSIZECTX; //размер доп контекста
for(;;) { //проход по inp
for(;;) { //проход по out
if(io >= szo) break;
if(inp.Items[ii].shash == out.Items[io].shash) {
//найдено, проверить контекст вниз
++found;
int i = ii;
int o = io;
bool dif = false;
for(int j = szctx; j > 0; --j) {
++i;
++o;
if(i >= szi && o >= szo) break;
if(i >= szi || o >= szo) { dif = true; break; }
if(inp.Items[i].shash != out.Items[o].shash) { dif = true; break; }
++found;
}
if(dif) found = 0;
if(found) break;
}
++io;
} //проход по out
if(found) {
//запись действия
WriteAction(ii, io, iframe, oframe, a);
break;
}
io = oframe;
if(++ii >= szi) {
//запись действия
WriteAction(ii, io, iframe, oframe, a);
break;
}
} //проход по inp
} //разные
} //проход по одинаковым
//хвосты
if(ii < szi) {
//по inp
if(found) iframe = ii;
ii = szi;
io = oframe = szo;
WriteAction(ii, io, iframe, oframe, a);
}
else if(io < szo) {
//по out
if(found) oframe = io;
io = szo;
ii = iframe = szi;
WriteAction(ii, io, iframe, oframe, a);
}
return true;
}
//записать действие в контейнер a
void Difr::WriteAction(int iix, int iox, int iframex, int oframex, Actions &a)
{
Action act;
if(iix > iframex && iox <= oframex) {
//delete
act.act = 'D';
WriteContext(oframex, act);
act.dt1.Items.insert(act.dt1.Items.begin(), &inp.Items[iframex], &inp.Items[iix-1]+1);
}
else if(iox > oframex && iix <= iframex) {
//insert
act.act = 'I';
WriteContext(oframex, act);
act.dt1.Items.insert(act.dt1.Items.begin(), &out.Items[oframex], &out.Items[iox-1]+1);
}
else {
//replace
act.act = 'R';
WriteContext(oframex, act);
act.dt1.Items.insert(act.dt1.Items.begin(), &inp.Items[iframex], &inp.Items[iix-1]+1);
act.dt2.Items.insert(act.dt2.Items.begin(), &out.Items[oframex], &out.Items[iox-1]+1);
}
if(act.IsOk()) a.push_back(act);
}
//записать контекст действия act (в after)
//oframe-индекс после контекста
void Difr::WriteContext(int oframe, Action &act)
{
int ctx = 0;
DataCont tmp;
int sizectx = 1; //размер контекста
int i;
if(oframe > 0) {
for(;;) {
//если контекст не уникальный-увеличивать вверх до начала
if(oframe-sizectx >= 0) ctx = sizectx;
else {
ctx = oframe;
act.beg = true;
}
tmp.Items.insert(tmp.Items.begin(), &out.Items[oframe-ctx], &out.Items[oframe]);
if(act.beg) break;
if(out.FindContext(tmp, i) < 2) break;
++sizectx;
tmp.Items.clear();
}
act.after.Items = tmp.Items;
}
else act.beg = true;
}
//---------------------------------------------------------------------------
//читать inp и cmd создать out
bool Difr::ApplyCmd(const char* inpfn, const char* outfn, const char* cmdfn)
{
inp.Items.clear();
out.Items.clear();
cmd.Items.clear();
if(!inp.ReadItems(inpfn) || !cmd.ReadItems(cmdfn)) {
errstr = NOTREAD;
return false;
}
inp.CalcHashAll();
out = inp;
Actions acts;
cmd.Cmd2Actions(acts);
if(!ApplyCmdProc(acts)) return false;
if(!out.WriteItems(outfn)) {
errstr = NOTWRITE;
return false;
}
errstr = NOERR;
return true;
}
//применить действия из контейнера a к контейнеру строк out
bool Difr::ApplyCmdProc(Actions &a)
{
int cts;
int ibeg; //инд начала контекста
int idx; //инд операции
DataCont tmp;
std::vector<StrExtItem>::iterator itrve;
for(Actions::iterator it = a.begin(); it != a.end(); ++it) {
switch(it->act) {
case 'I':
it->after.CalcHashAll();
it->dt1.CalcHashAll();
cts = out.FindContext(it->after, ibeg, it->beg);
if(cts < 1) { errstr = NOTFOUNDCTX; return false; }
if(cts > 1) { errstr = NOTAPPLAMB; return false; }
idx = ibeg + (int)it->after.Items.size();
itrve = out.Items.begin();
advance(itrve, idx);
out.Items.insert(itrve, it->dt1.Items.begin(), it->dt1.Items.end());
//out.Items.insert(&out.Items[idx], it->dt1.Items.begin(), it->dt1.Items.end());
break;
case 'D':
tmp = it->dt1;
tmp.Items.insert(tmp.Items.begin(), it->after.Items.begin(), it->after.Items.end());
tmp.CalcHashAll();
cts = out.FindContext(tmp, ibeg, it->beg);
if(cts < 1) { errstr = NOTFOUNDCTX; return false; }
if(cts > 1) { errstr = NOTAPPLAMB; return false; }
idx = ibeg + (int)it->after.Items.size();
itrve = out.Items.begin();
advance(itrve, idx);
out.Items.erase(itrve, itrve + it->dt1.Items.size());
//out.Items.erase(&out.Items[idx], &out.Items[idx] + it->dt1.Items.size());
break;
case 'R':
tmp = it->dt1;
tmp.Items.insert(tmp.Items.begin(), it->after.Items.begin(), it->after.Items.end());
tmp.CalcHashAll();
it->dt2.CalcHashAll();
cts = out.FindContext(tmp, ibeg, it->beg);
if(cts < 1) { errstr = NOTFOUNDCTX; return false; }
if(cts > 1) { errstr = NOTAPPLAMB; return false; }
idx = ibeg + (int)it->after.Items.size();
itrve = out.Items.begin();
advance(itrve, idx);
out.Items.erase(itrve, itrve + it->dt1.Items.size());
//out.Items.erase(&out.Items[idx], &out.Items[idx] + it->dt1.Items.size());
itrve = out.Items.begin();
advance(itrve, idx);
out.Items.insert(itrve, it->dt2.Items.begin(), it->dt2.Items.end());
//out.Items.insert(&out.Items[idx], it->dt2.Items.begin(), it->dt2.Items.end());
break;
default:
break;
}
}
return true;
}
}; //namespace Difference
файл sccs.cpp
//---------------------------------------------------------------------------
// SCCS
// Обработка изменений в текстовых файлах
// (c) Vladimir Mishenin
// email: bobm@mail.ru
#include "stdafx.h"
#include "difr.h"
static void usage(void)
{
std::cout << "Incorrect arguments." << std::endl;
std::cout << "Usage:" << std::endl;
std::cout << "sccs.exe source_file target_file changeset_file /apply" << std::endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
if(argc != 4 && argc != 5) {
usage();
return 1;
}
bool apply = false;
if(argc == 5) {
if(std::string(argv[4]) != std::string("/apply")) {
usage();
return 1;
}
apply = true;
}
Difference::Difr task;
if(apply) {
if(!task.ApplyCmd(argv[1], argv[2], argv[3])) {
std::cout << task.GetLastError() << std::endl;
return 1;
}
}
else {
if(!task.CreateCmd(argv[1], argv[2], argv[3])) {
std::cout << task.GetLastError() << std::endl;
return 1;
}
}
std::cout << "Ok." << std::endl;
return 0;
}
(c) Vladimir Mishenin