1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
|
#include <iostream>
#include <string>
#include <cctype>
#include <vector>
#include <cassert>
#include <fstream>
// remove leading and trailing white space
std::string trim( std::string str )
{
std::size_t pos = 0 ;
while( pos < str.size() && std::isspace( static_cast<unsigned char>(str[pos]) ) ) ++pos ;
while( !str.empty() && std::isspace( static_cast<unsigned char>( str.back() ) ) ) str.pop_back() ;
return str.substr(pos) ;
}
// convert to all lower case
std::string to_lower( const std::string& str )
{
std::string result ;
for( unsigned char c : str ) result += std::tolower(c) ;
return result ;
}
// return true if substr is a prefix of str; after ignoring white space and case
bool starts_with( const std::string& str, const std::string& substr )
{
return to_lower( trim(str) ).find( to_lower( trim(substr) ) ) == 0 ;
}
// read lines from an input stream into a vector
std::vector<std::string> get_lines( std::istream& stm )
{
std::vector<std::string> result ;
std::string line ;
while( std::getline( stm, line ) ) result.push_back(line) ;
return result ;
}
// write lines from the vector to an output stream
std::ostream& put_lines( std::ostream& stm, const std::vector<std::string>& lines )
{
for( const std::string& str : lines ) stm << str << '\n' ;
return stm ;
}
// return the position of the first line with the given prefix. return lines.size() if not found
std::size_t find_prefix( const std::vector<std::string>& lines, const std::string& prefix )
{
for( std::size_t pos = 0 ; pos < lines.size() ; ++pos )
if( starts_with( lines[pos], prefix ) ) return pos ;
return lines.size() ; // not found
}
// move lines in positions [from,to] to the position immediately before dest
std::vector<std::string> move_lines_forward( std::vector<std::string> lines, std::size_t from, std::size_t to, std::size_t dest )
{
assert( from <= to && to <= dest ) ; // sanity check
std::vector<std::string> result ;
// move lines [0,from) to result
for( std::size_t i = 0 ; i < from ; ++i ) result.push_back( std::move( lines[i] ) ) ;
// move lines [to+1,dest) to result
for( std::size_t i = to+1 ; i < dest ; ++i ) result.push_back( std::move( lines[i] ) ) ;
// move lines in positions [from,to] to result
for( std::size_t i = from ; i <= to ; ++i ) result.push_back( std::move( lines[i] ) ) ;
// move lines [dest,end) to result
for( std::size_t i = dest ; i < lines.size() ; ++i ) result.push_back( std::move( lines[i] ) ) ;
assert( result.size() == lines.size() ) ; // sanity check
return result ;
}
int main()
{
const std::string test_file_name = "test_clientconfig.xml" ;
{
// for testing: create a test file and dump its contents
std::ofstream(test_file_name) <<
R"(<?xml version="1.0" encoding="UTF-8"?>
<config>
<settings>
<differentsettings>true</differentsettings>
<etc>true</etc>
<etc>false</etc>
</settings>
<services>
<variousserviceclasses>
<thoseclasssettings>
</variousserviceclasses>
<variousserviceclasses>
<thoseclasssettings>
</variousserviceclasses>
</services>
<initpart>
<initstuff>
</initpart>
</config>
)" ;
std::cout << "test file contains:\n------------------\n"
<< std::ifstream(test_file_name).rdbuf() << "-----------\n\n" ;
}
if( std::ifstream file{test_file_name} )
{
auto lines = get_lines(file) ; // read the lines into a vector
const auto nlines = lines.size() ;
// we want to move section <settings> to just before </config>
const std::size_t from = find_prefix( lines, "<settings>" ) ; // locate the first line of the section
const std::size_t to = find_prefix( lines, "</settings>" ) ; // locate the last line of the section
const std::size_t dest = find_prefix( lines, "</config>" ) ; // locate the dest ination line
if( from <= to && to <= dest && dest < nlines ) // sanity check
{
// create a new vector with the lines moved
const auto moved_vec = move_lines_forward( std::move(lines), from, to, dest );
if( moved_vec.size() == nlines ) // if things appear to be ok
{
file.close() ; // close the input file
// and modify the original file (overwrite it)
std::ofstream file_out(test_file_name) ;
put_lines( file_out, moved_vec ) ;
}
}
}
// for testing: dump the contents of the file at the end
std::cout << "\n\nafter the section move, the modified file contains:\n-------------------------\n"
<< std::ifstream(test_file_name).rdbuf() << "-----------\n" ;
}
|