• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Plik tekstowy i oddzielanie słów

Cloud VPS
0 głosów
896 wizyt
pytanie zadane 24 stycznia 2020 w C i C++ przez Agnes Użytkownik (990 p.)
Mam plik tekstowy taki, że w każdej linijce są po dwa słowa oddzielone spacją. Jak najlepiej pobrać każde z tych słów osobno? Jest jakiś szybszy sposób niż przechodzenie po wszystkich znakach i kończenie, gdy trafi się na biały znak? Koniecznie bez użycia vectora.
komentarz 25 stycznia 2020 przez mokrowski Mędrzec (158,840 p.)
Jeśli (tak naprawdę) wymagasz std::vector a nie możesz go mieć, to mi pachnie problemem XY: https://en.wikipedia.org/wiki/XY_problem

Proszę podaj całość polecenia bo być może twoja sugestia rozwiązania jest błędna.
komentarz 25 stycznia 2020 przez Agnes Użytkownik (990 p.)
edycja 25 stycznia 2020 przez Agnes

Napisac program, który tłumaczy tekst z jezyka polskiego na angielski i z powrotem. Tłumaczenie polega na zastepowaniu słów jezyka wyjsciowego słowami jezyka docelowego. Przy implementacji nalezy skorzystac z drzewa binarnego. Drzewo binarne zbudowane dla jezyka polskiego zbudowane jest w ten sposób, ze wyrazy, które stoja wczesniej w porzadku alfabetycznym sa po lewej stronie wezła, a te, które stoja pózniej – po prawej stronie. Te same elementy, które naleza do drzewa dla jezyka polskiego, sa takze elementami drzewa dla jezyka angielskiego zbudowanego według porzadku alfabetycznego wyrazów angielskich.

Przykładowy plik słownika:

ja I
miec have
kot cat

Moja struktura opisująca pojedynczy węzeł dla obu "poddrzew":

struct node
{
	string word;

	node* left;
	node* right;
	node* anLeft;
	node* anRight;
};

anLeft i anRight to wskaźniki dotyczące angielskich wersji słów.

I próbuję teraz jakoś zapisać słowa w odpowiednich miejscach.

Odnośnie ograniczeń, nie mogę też alokować dynamicznych tablic.

3 odpowiedzi

0 głosów
odpowiedź 24 stycznia 2020 przez Inn Gaduła (4,300 p.)
#include <iostream>
#include <fstream>

using namespace std;

int main()
{
    fstream plik;
    plik.open("plik.txt", ios::in | ios::out);

    string slowo;

    while(plik >> slowo)
    {
        cout << slowo << endl;
    }

    plik.close();

    return 0;
}

Możesz w ten sposób pobrać zawartość pliku, zapis w tablicy już sobie ogarniesz.

komentarz 25 stycznia 2020 przez Agnes Użytkownik (990 p.)
A dałoby się każde z tych słów zapisać do dynamicznie utworzonej tablicy stringów? Da się sprawić, żeby jej rozmiar był zawsze równy ilości słów w pliku? Wiem, że vector byłby chyba najlepszą opcją, ale nie mogę go używać :/
komentarz 25 stycznia 2020 przez Inn Gaduła (4,300 p.)
Podam taki kod w odpowiedzi.
0 głosów
odpowiedź 25 stycznia 2020 przez Inn Gaduła (4,300 p.)
#include <iostream>
#include <fstream>

int countWords(std::fstream& file)
{
    std::string word;
    int counter = 0;

    while (file >> word) counter++; 

    file.clear();
    file.seekg(0, std::ios::beg);

    return counter;
}

void wordsToArray(std::fstream& file, std::string array[])
{
    std::string word;
    int i = 0;

    while (file >> word)
    {
        array[i] = word;  
        i++;
    }
}

int main() 
{
    std::fstream file;
    file.open("plik.txt", std::ios::in | std::ios::out);

    int wordCount = countWords(file);
    std::string* words = new std::string[wordCount];

    wordsToArray(file, words);

    for (int i = 0; i < wordCount; i++)
    {
        std::cout << words[i] << "\n";
    }

    delete[] words;
    file.close();

    return 0;
}

Bez dynamicznej tablicy lub vectora będzie trudno to wykonać, zawsze możesz ustawić tablice na 1000 miejsc ale nie jest to dobra technika.

komentarz 25 stycznia 2020 przez Agnes Użytkownik (990 p.)
A jak w najlepszy sposób uporządkować te słowa w sposób opisany w zadaniu? Mój pomysł jest taki, żeby skorzystać z tego, że każde polskie słowo jest na pozycji o parzystym numerze względem początku pliku, ale to jakoś zawodzi.
0 głosów
odpowiedź 25 stycznia 2020 przez mokrowski Mędrzec (158,840 p.)

To zadanie oczywiście da się wykonać bez alokacji tablicy dynamicznej czy wektora. Jedyne wywołania new, będą występowały dla struktury node. Będzie także trzeba pamiętać o prawidłowym dodaniu słowa do drzewa. Stąd trzeba wczytać parę słów i następnie "związać je" w 2 drzewach. Strukturę obydwu drzew należy prawidłowo zniszczyć przed wyjściem z programu.

Kod ideowy (choć działa):

#include <fstream>
#include <iostream>
#include <tuple>
#include <utility>

struct node {
    std::string word;
    node * left;
    node * right;
    node * translate;
};

// Dodaje węzeł do drzewa
void insert_node_to_tree(node * word_node, node * word_tree) {
    // FIXME: Wykonać poprawną implementację dodawania do słownika
    // Ta jest _TOTALNIE_ nieprawidłowa. Dodaje ciągle do lewej gałęzi.
    node * current_node = word_tree;
    while(current_node->left != nullptr) {
        current_node = current_node->left;
    }
    current_node->left = word_node;
}

void delete_tree(node * tree) {
    // FIXME: Zaimplementować poprawne usunięcie drzewa
    delete tree;
}

// Wyszukiwanie węzła w drzewie.
const node * find_node(const node * word_node, const node * word_tree) {
    // FIXME: Zaimplementować poprawnie.
    return nullptr;
    (void) word_node;
    (void) word_tree;
}


std::pair<node *, node*> read_words(std::ifstream& file) {
    // Tworzenie 2 pierwszych węzłów które będą korzeniami drzew
    node * pl_en_tree = new(std::nothrow) node();
    node * en_pl_tree = new(std::nothrow) node();
    // Wczytanie pierwszych 2 słów
    file >> pl_en_tree->word >> en_pl_tree->word;
    // Kontrola czy alokacja i stan strumienia umożliwia kontynuację
    if(!file || (pl_en_tree == nullptr) || (en_pl_tree == nullptr)) {
        delete pl_en_tree;
        delete en_pl_tree;
        return {};
    }
    // Budowanie słownika
    std::string pl_word;
    std::string en_word;
    while(file >> pl_word >> en_word) {
        node * pl_node = new node{pl_word, nullptr, nullptr, nullptr};
        node * en_node = new node{en_word, nullptr, nullptr, nullptr};
        // Wiązanie słów do translacji
        pl_node->translate = en_node;
        en_node->translate = pl_node;
        insert_node_to_tree(pl_node, pl_en_tree);
        insert_node_to_tree(en_node, en_pl_tree);
    }
    return {pl_en_tree, en_pl_tree};
}

int main() {
    std::ifstream file("dictionary.txt");
    if(!file) {
        std::cerr << "Open file error!\n";
        return -1;
    }
    node * pl_en_tree;
    node * en_pl_tree;
    std::tie(pl_en_tree, en_pl_tree) = read_words(file);
    if((pl_en_tree == nullptr) || (en_pl_tree == nullptr)) {
        delete pl_en_tree;
        delete pl_en_tree;
        std::cerr << "Tree construction error!\n";
        return -1;
    }
    delete_tree(pl_en_tree);
    delete_tree(en_pl_tree);
}

W FIXME widzisz części które trzeba implementować.

komentarz 26 stycznia 2020 przez mokrowski Mędrzec (158,840 p.)
edycja 26 stycznia 2020 przez mokrowski

Ok, trzeba to będzie obejść. @mokrowski, ten kod nie działa, bo sprawdzenie tego w taki sposób:

1. Zaimplementuj to co widzisz w FIXME i zapoznaj się z algorytmem. Gwarantuję że działa. Poza tym zwróć uwagę. Pierwotne pytanie oscylowało w rejonach "chcę mieć dynamiczny wektor bez wektora". Masz przykład że swobodnie można bez tego.

2. std::tie(...) jest użyte z powodu tego że nie chciałem pisać rozbijania std::pair na first i second bo zachowawczo założyłem że C++17 może być zbyt... postępowe :) . std::tie(...) dostępne jest od C++11 a dopiero/już w C++17 doszło:

auto [x, y] = call();

Znając praktykę uczelni, bywa że stosujecie jakieś antyczne środowiska. Podjąłem decyzję "nie wymagać C++17" no ale brak C++11 to już jest.... muzeum (z szacunkiem dla muzeum) :) Tak na marginesie warto zerknąć jakie są różnice i możliwości obydwu składni. Wbrew pozorom wzajemnie się uzupełniają.

3. Kod można jeszcze w bardzo dużym stopniu tak usprawnić jak uczynić bardziej czytelnym. Skupiłem się wyłącznie na zilustrowaniu sposobu realizacji zadania. Jak dokonasz własnej implementacji, jeśli poprosisz, umieszczę swoją propozycję finalną.

I czy jedyną słuszną alternatywą dla nie możliwego zwracania dwóch parametrów w funkcji read_words() jest napisanie dwóch funkcji, jedna zwracająca i zajmująca się tylko tym poddrzewm pl_en_tree i druga dla en_pl_tree?

4. Oczywiście że nie. Możesz zwrócić strukturę z N elementami, możesz przyjąć jako argumenty typu wskaźnik/referencja dane które "wypełnisz". Zawsze jednak należy preferować drogę tradycyjną czyli zwrócenie przez return. Zaraz zapytasz kiedy nie? Np. gdy masz wyłączone wyjątki (lub ich nie chcesz), chcesz wykonać pracę i zwrócić informację o błędzie. Wtedy funkcja może wyglądać tak:

// Jako true/false, zwraca informację czy wystąpił błąd.
// W argumentach arg1 i arg2, zwraca wynik
bool foo(MyClass& arg1, MyClass& arg2) {
    // Operacje na argumentach i wyniku działania
    bool result = false;
    /*
    ....
    */
    return result;
}

 

komentarz 26 stycznia 2020 przez Agnes Użytkownik (990 p.)
edycja 26 stycznia 2020 przez Agnes

A co jest w tym nie tak:

#include <fstream>
#include <iostream>

using namespace std;



struct node 
{
	string word;
	node* left;
	node* right;
	node* translate;
};
node* insert(node* w_tree, string word)		
{
	fstream file;
	file.open("dictionary1.txt");

	if (file.is_open() == false)
	{
		cout << "błąd";
	}
	string word1 = word, word2, word3;
	file >> word1 >> word2 >> word3;


	if (w_tree == nullptr)
	{
		node* new_tree = new node;
		new_tree->word = word;
		new_tree->translate->word = word2;
		new_tree->right = nullptr;
		new_tree->left = nullptr;

		return new_tree;
	}
	if (word1 > word3)
	{
		w_tree->left = insert(w_tree->left, word3);
	}
	if (word1 < word3)
	{
		w_tree->right = insert(w_tree->right, word3);
	}

	return w_tree;
}
int main()
{
	fstream file;
	file.open("dictionary1.txt");

	if (file.is_open() == false)
	{
		cout << "błąd";
	}
	string word;

	file >> word;
	
	insert(nullptr, word);
		
	return 0;
}

Biorę oczywiście Twój kod pod uwagę, ale na razie skupiam się na tym, żeby napisać poprawną funkcję wstawiającą węzły do drzewa.

komentarz 26 stycznia 2020 przez mokrowski Mędrzec (158,840 p.)

Hmm.. chyba nie do końca rozumiesz ideę.

1. Co to jest?

string word1 = word, word2, word3;

Chcesz inicjować 4 zmienne typu wordX? No to tak:

string word1, word2, word3;
// Choć do dobrej praktyki należy raczej tak:
string word1;
string word2;
string word3;

2. Dalej masz coś takiego...

        w_tree->translate->word = word2;

Powiedz czy wskaźnik translate wskazuje na jakiś obiekt?

Te błędy najważniejsze.

komentarz 26 stycznia 2020 przez Agnes Użytkownik (990 p.)

Chyba udało mi się poprawić punkt drugi:

	if (w_tree == nullptr)
	{
		node* new_tree = new node;
		new_tree->right = nullptr;
		new_tree->left = nullptr;
		new_tree->word = word;
		new_tree->translate = new node;
		new_tree->translate->word = word2;


		return new_tree;
	}

A odnośnie punktu pierwszego wydawało mi się, że to bez różnicy.

komentarz 26 stycznia 2020 przez Agnes Użytkownik (990 p.)

Więc na razie całość wygląda tak:

#include <fstream>
#include <iostream>

using namespace std;

struct node 
{
	string word;
	node* left;
	node* right;
	node* translate;
};
node* insert(node* w_tree, string word)		
{
	fstream file;
	file.open("dictionary1.txt");

	if (file.is_open() == false)
	{
		cout << "błąd";
	}
	string word1 = word;
	string word2;
	string word3;
	file >> word1 >> word2 >> word3;


	if (w_tree == nullptr)
	{
		node* new_tree = new node;
		new_tree->right = nullptr;
		new_tree->left = nullptr;
		new_tree->word = word;
		new_tree->translate = new node;
		new_tree->translate->word = word2;


		return new_tree;
	}
	if (word1 > word3)
	{
		w_tree->left = insert(w_tree->left, word3);
	}
	if (word1 < word3)
	{
		w_tree->right = insert(w_tree->right, word3);
	}

	return w_tree;
}
int main()
{
	fstream dir;
	dir.open("dictionary1.txt");

	if (dir.is_open() == false)
	{
		cout << "błąd";
	}
	string word1;
	string word2;

	dir >> word1;

	node* pl_en = new node;
	node* en_pl = new node;
	
	pl_en = insert(nullptr, word1);		//pl
	en_pl = insert(nullptr, word2);		//en

		
	return 0;
}

Z tym, że dla każdego węzła, który nie jest pierwszy ani ostatni jest tym sposobem zainicjalizowany tylko jeden wskaźnik chyba.

Podobne pytania

0 głosów
1 odpowiedź 663 wizyt
pytanie zadane 11 grudnia 2022 w C i C++ przez polandonion Dyskutant (7,630 p.)
+1 głos
1 odpowiedź 498 wizyt
0 głosów
1 odpowiedź 392 wizyt

93,459 zapytań

142,453 odpowiedzi

322,722 komentarzy

62,837 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj

Kursy INF.02 i INF.03
...