Kirjautuminen

Haku

Tehtävät

Koodit: C++: Merkkijonon kääntäminen

Kirjoittaja: SiperianSiika

Kirjoitettu: 31.12.2010 – 22.11.2011

Tagit: teksti, koodi näytille, vinkki

Koodi tutustuttaa lukijan erilaisiin tapoihin käyttää C++:n standardikirjaston algoritmeja merkkijonon ympäri kääntämiseen. Erityisesti tutustutaan kopiointialgoritmeihin ja erikoisempiin iterattoreihin, kuten reverse_iteratoriin ja insertoivaan iteraattoriin. Lisäksi tavataan templateilla toteutettu myös unicode-merkkijonoa std::wstring tukeva stringinkääntöfunktio ja tulostetaan konsoliin unicodea.

// Includataan käytettävät headerit kauniisti järjestyksessä
#include <algorithm>
#include <iostream>
#include <string>
#include <cassert>
#include <cstdio>

// Käännä char-stringi. Tapa 1.
// Luodaan insert-iteraattori, joka lisää merkkejä output-stringin loppuun.
// Kopioidaan input-stringi käännettynä insert-iteraattoriin reverse_copyllä.
std::string reverseString_1(const std::string &input) {
	std::string output;

	std::insert_iterator<std::string> out_iter(output, output.end());
	std::reverse_copy(input.begin(), input.end(), out_iter);

	return output;
}

// Käännä char-stringi. Tapa 2.
// Luodaan insert-iteraattori, joka lisää merkkejä output-stringin loppuun.
// Kopioidaan input-stringi insert-iteraattoriin, mutta annetaan copy-
// funktiolle iteraattori joka käy input-stringin läpi lopusta alkuun.
std::string reverseString_2(const std::string &input) {
	std::string output;

	std::insert_iterator<std::string> out_iter(output, output.end());
	std::copy(input.rbegin(), input.rend(), out_iter);

	return output;
}

// Käännä char-stringi. Tapa 3.
// Käydään input-stringi läpi lopusta alkuun ja lisätään merkit
// yksitellen output-stringin loppuun. Periaatteessa sama kuin tapa 2.
std::string reverseString_3(const std::string &input) {
	std::string output;

	std::string::const_reverse_iterator r_it = input.rbegin();
	for(; r_it != input.rend(); ++r_it) {
		output.push_back(*r_it);
	}

	return output;
}

// Käännä char-stringi. Tapa 4.
// Alustetaan output-stringi oikean pituisella määrällä merkkejä.
// Iteroidaan stringin merkkien yli kopioiden input-stringin
// merkit käänteisessä järjestyksessä output-stringiin.
std::string reverseString_4(const std::string &input) {
	std::string output(input.length(), '\0');

	for(size_t i = 0; i < input.length(); ++i) {
		output[i] = input[input.length() - i - 1];
	}

	return output;
}

// Käännä char-stringi. Tapa 5.
// Kopioidaan input-stringi output-stringiin.
// Käännetään output-stringi.
std::string reverseString_5(const std::string &input) {
	std::string output = input;
	std::reverse(output.begin(), output.end());
	return output;
}


// std::string on toteutukseltaan std::basic_string<char>, kun taas
// unicode-merkkejä tukeva std::wstring on std::basic_string<wchar_t>.
// Muunkinlaisia stringejä voisi periaatteessa määritellä.

// Tehdään funktiotemplaatti, joka toimii näille kaikille. Kutsutaan
// käytettävää merkkityyppiä nimellä char_t. Tällöin stringityyppi on
// std::basic_string<char_t>.

template<typename char_t>
std::basic_string<char_t> reverseString(const std::basic_string<char_t> &input) {
	// Käyttömukavuuden vuoksi annetaan käytettävälle stringityypille
	// lempinimi string_t.
	typedef std::basic_string<char_t> string_t;

	// Käännetään merkkijono edellä esitellyllä tavalla 2.
	string_t output;
	std::insert_iterator<string_t> out_iter(output, output.end());
	std::copy(input.rbegin(), input.rend(), out_iter);
	return output;
}


int main(int argc, char *argv[]) {
	// Testataan edellisten funktioiden toimivuus.

	// Makro assert keskeyttää ohjelman suorituksen, mikäli parametrin
	// ehto on epätosi. Tässä on kyseessä helppo ns. yksikkötesti, joiden
	// käyttäminen helpottaa ohjelmointia, sillä virheet havaitaan etukäteen
	// ja paikannetaan nopeasti.
	//   Huomaa, että käännettäessä ohjelmasta julkaisuversiota,
	// assert-käskyt usein eliminoidaan hidastamasta ohjelmaa (esim. -DNDEBUG).

	std::string input = "Saippuakauppias";
	std::string result = "saippuakauppiaS";

	assert(reverseString_1(input) == result);
	assert(reverseString_2(input) == result);
	assert(reverseString_3(input) == result);
	assert(reverseString_4(input) == result);
	assert(reverseString_5(input) == result);

	// Testaa yleisemmän funktion toimivuus erityyppisillä parametreilla.
	// L"merkkijono" tarkoittaa wchar_t-stringiä, kun taas
	// "merkkijono" tarkoittaa char-stringiä.
	std::wstring winput = L"Säippyäkäyppiäs";
	std::wstring wresult = L"säippyäkäyppiäS";
	assert(reverseString(winput) == wresult);
	assert(reverseString(input) == result);

	// Aseta locale ja tulosta testitulos. Skandien tulisi näkyä.
	setlocale(LC_ALL, "");
	std::wcout << L"Säippyäkäyppiäs ok!" << std::endl;
}

Kommentit

Metabolix [06.01.2011 00:59:52]

#

Listalta puuttuu muuten pari helpointa.

Kopiointi appendilla toimii aavistuksen helpommin kuin std::copy-funktiolla:

std::string reverseString_6(const std::string &input) {
    std::string output;
    output.append(input.rbegin(), input.rend());
    return output;
}

Myös vastaava muodostin on olemassa, eli yhden rivin funktiokin onnistuu (jolloin täytyy miettiä, onko funktion tekemisessä järkeä ensinkään):

std::string reverseString_7(const std::string &input) {
    return std::string(input.rbegin(), input.rend());
}

// Tai muualla koodissa:
std::string input = "abcde";
std::string output(input.rbegin(), input.rend());

SiperianSiika [06.01.2011 01:34:10]

#

Kas vain! Ovela kettu olet!

jlaire [06.11.2015 16:40:32]

#

Parametrin ottaminen const-referenssinä ja kopioiminen lokaaliin muuttujaan on itse asiassa huono idea, varsinkin modernissa C++:ssa. Jos funktio tarvitsee kopion, kannattaa ottaa parametri arvona ja käyttää sitä suoraan.

std::string reverseString_8(std::string input) {
    std::reverse(input.begin(), input.end());
    return input;
}

Tällä tavalla kutsujalla on mahdollisuus hyödyntää move-operaatioita ja välttää turha kopio.

void foo() {
    // väliaikaisarvo
    auto a = reverseString_8(std::string("..."));

    // std::move castaa a:n rvalue referenssiksi
    std::string b("...");
    auto c = reverseString_8(std::move(b));
}

Kirjoita kommentti

Muista lukea kirjoitusohjeet.
Tietoa sivustosta