Jak wczytać z pliku dwuwymiarową tablicę o różnych rozmiarach?

0

Cześć !

Nie mam kompletnie pomysłu jak wczytać z pliku dwuwymiarową tablicę o różnych rozmiarach ( tzn. raz 4x10, 10x50, 100x100 )
Pierwsze dwie liczby to rozmiar x i y tablicy, reszta to liczby które chce wczytać ( przyjmuję, że są z zakresu 0 - inf )

Przykładowy plik wygląda tak

15 10
19 27 27 27 27 27 27 27 27 5 5 5 5 16 16 
19 27 27 27 27 27 27 27 27 5 5 5 5 16 16 
19 27 27 27 27 27 27 27 27 5 5 5 5 16 16 
19 27 27 27 27 27 27 27 27 5 5 5 5 16 16 
16 16 16 16 16 16 16 16 16 16 16 16 16 16 16 
16 16 16 16 16 16 16 16 16 16 16 16 16 16 16 
13 13 13 13 13 13 5 5 5 5 1 1 1 16 16 
13 13 13 13 13 13 5 5 5 5 1 1 1 16 16 
13 13 13 13 13 13 5 5 5 5 1 1 1 16 16 
13 13 13 13 13 13 5 5 5 5 1 1 1 16 16 

lub

3 3
12 12 12 
12 12 12 
12 12 12 

do tej pory udało mi się wczytać rozmiar trochę łopatologiczną metodą ale jednak działającą.
funkcja odpowiedzialna za wczytanie:

void cMapa::WczytajMape(std::string nazwa)
{
	std::fstream plik;

	std::string schowek;
	std::string x;
	std::string y;
	int a = 0;

	plik.open(nazwa, std::ios::in);
	{
		std::getline(plik, schowek);
		for (size_t i = 0; i < schowek.size(); i++)
		{
			if (schowek[i] == 32)
				a++;

			if (a == 0)
				x.push_back(schowek[i]);
			else
				y.push_back(schowek[i]);
		}

		rozmiar.x = std::stoi(x);
		rozmiar.y = std::stoi(y);

		for (size_t i = 0; i < rozmiar.y; i++)
		{
			for (size_t i = 0; i < rozmiar.y; i++)
			{

			}
		}

	}
	plik.close();
}

i to pola w klasie

class cMapa
{
public:
	cMapa();
	~cMapa() = default;

protected:
	sf::Vector2u rozmiar;
	std::vector<std::vector<sf::RectangleShape>> pola;	

public:
	void WczytajMape(std::string nazwa);
};

kiedyś używałem zczytywania do znaku ale teraz nie wiem czemu nie chce zadziałać:

std::getline(plik, schowek, " ");

a i przy okazji mam jeszcze jedno pytanie, czy da się jakoś ustawić żeby i było równe 0 przy takiej metodzie iterowania przez stringa ?
w ascii 51 to wlasnie spacja :/
czemu 51.png

1
void WczytajMape(std::string nazwa) {
     std::ifstream file(nazwa);
     if (file.good()) {
          WczytajMape(file);
     }
}

void WczytajMape(std::istream &input) {
     int width, height;

     if (input >> width >> height >> std::ws) {
           pola.clear();
           pola.reserve(height);
           for (int i = 0; i <height; ++i) {
                 std::string line;
                 if (std::getline(input, line)) {
                 // geline nie jest konieczne bo masz nadmiarowe dane o wymiarach planszy
                      std::istringstream lineStream(line);
                      std::vector<sf::RectangleShape> row;
                      row.reserve(width);
                      std::copy_n(std::input_iterator<int>{lineStream}, width, 
                                  std::back_inserter(row));
                      pola.push_back(std::move(row));
                 }
           }
     }
}
0

// co sie dzieje w nawiasach ifa i pod nim
     if (input >> width >> height) { 
           pola.clear();
           pola.reserve(height);


           for (int i = 0; i <height; ++i) {
                 std::string line;

// co getline zwraca true/false ? istringstream ? czemu tak? 
                 if (std::getline(input, line)) {
                      std::istringstream lineStream(line);
                      std::vector<sf::RectangleShape> row;
                      
// czemu odwracamy?
                      row.reserve(width);
                  
// a tutaj to nawet nie wiem o co zapytać :(
                      std::copy_n(std::input_iterator<int>{lineStream}, width, 
                                  std::back_inserter(row));
                      pola.push_back(std::move(row));
                 }
           }
     }
}

Mógłbyś się odnieść do tych komentarzy i opisać ten fragment kodu ? Jak on działa ?

0

// co sie dzieje w nawiasach ifa i pod nim

http://www.cplusplus.com/reference/istream/basic_istream/operator%3E%3E/
http://www.cplusplus.com/reference/ios/basic_ios/operator_bool/

// co getline zwraca true/false ? istringstream ? czemu tak?

http://www.cplusplus.com/reference/string/string/getline/
i to co wyżej

// czemu odwracamy?

To nie jest odwracanie ale rezerwacja pamięci: http://www.cplusplus.com/reference/vector/vector/reserve/
Optymalizacja, którą możesz wywalić.

// a tutaj to nawet nie wiem o co zapytać :(

Faktycznie, może za duża armata dla ciebie:
https://en.cppreference.com/w/cpp/iterator
https://en.cppreference.com/w/cpp/iterator/back_inserter
https://en.cppreference.com/w/cpp/algorithm/copy_n

Wersja uroszczona:

int x;
for (int j = 0; j < width; ++j) {
    if (lineStream >> x) {
          row.push_back(x); // może jest potrzebne static_cast<sf::RectangleShape>(x)
    }
}
pola.push_back(std::move(row));
3
kameleo327 napisał(a):

Cześć !

Nie mam kompletnie pomysłu jak wczytać z pliku dwuwymiarową tablicę o różnych rozmiarach ( tzn. raz 4x10, 10x50, 100x100 )
Pierwsze dwie liczby to rozmiar x i y tablicy, reszta to liczby które chce wczytać ( przyjmuję, że są z zakresu 0 - inf )

Jeżeli dwuwymiarowość byś emulował a faktycznie trzymał dane w jednym, ciągłym wektorze to sprawę można załatwić bardzo prosto:

std::vector< sf::RectangleShape > cells;
std::ifstream file( "mapa.txt" );
int height, width;
if ( file >> width >> height )
{
  std::transform( std::istream_iterator< int >( file ), std::istream_iterator< int >(), std::back_inserter( cells ), 
    []( int value )
    {
      return sf::RectangleShape( value, value ); // przykładowo, nie wiem jak korzystasz z tych wartości przy tworzeniu obiektu
    }
  );
}

A jeśli koniecznie chcesz mieć wektor wektorów to można to rozwiązać w miarę podobnie:

struct MapRow
{
  std::string data;
  operator std::vector< sf::RectangleShape >() const
  {
    std::vector< sf::RectangleShape > row;
    std::stringstream ss( this->data );
    int value;
    while( ss >> value )
    {
      row.emplace_back( value, value ); // podobnie jak w kodzie wyżej, nie wnikam w sposób tworzenia
    }
    return row;
  }
};

std::istream& operator>>( std::istream& stream, MapRow& row )
{
  std::getline( stream, row.data );
  return stream;
}

void Func()
{
  std::vector< std::vector< sf::RectangleShape > > cells;
  std::ifstream file( "mapa.txt" );
  int width, height;
  if ( file >> width >> height ) {
    std::copy( std::istream_iterator< MapRow >( file ), std::istream_iterator< MapRow >(), std::back_inserter( cells ) );
  }
}
0

odnośnie trzymania takiej tablicy vel macierzy. W boost jest o ile pamiętam boost::matrix

0

Dziękuję za zainteresowanie, analizowałem długo Twój kod, sporo nowych rzeczy się nauczyłem.
Nie rozumiem jednak wciąż po co struktura, po co ten operator, nie rozumiem do końca tego kodu.

Na razie rozumiem tyle:

struct MapRow //tworzymy strukturę rząd tylko dlaczego akurat strukturę a nie trzymamy tego w oddzielnej funkcji np
{
  std::string data;
  operator std::vector< sf::RectangleShape >() const 
// Przeciążamy operator typu std::vector< sf::RectangleShape >, nie spotkałem sie dotąd z przeciążaniem czegoś innego niż +,-,= itd. jak to ma działać?
 
 {
    std::vector< sf::RectangleShape > row;
    std::stringstream ss( this->data );
    int value;
    while( ss >> value ) 
// tak dlugo jak value bedzie zczytywalo wartosc ze streamu tak dlugo bedzie trwac petla - rozumiem ze poza wartosciami z pliku, value bedzie zerem tak?
 
   {
      row.emplace_back( value, value ); // podobnie jak w kodzie wyżej, nie wnikam w sposób tworzenia 
// czemu nie push_back?
  
  }
    return row; 
  }
};
 
std::istream& operator>>( std::istream& stream, MapRow& row ) 
// przeciazamy operator >> ktory ma nam zczytywaćlinijke z pliku

{
  std::getline( stream, row.data );
  return stream;
}
 
void Func()
{
  std::vector< std::vector< sf::RectangleShape > > cells;
  std::ifstream file( "mapa.txt" );
  int width, height;
  if ( file >> width >> height ) { 
// ten if dziala tylko raz ? zczytujemy ze strumienia pliku dwie pierwsze liczby a potem strumien rowny jest 0 i nie wykonujemy zawartsci ifa?
   
 std::copy( std::istream_iterator< MapRow >( file ), std::istream_iterator< MapRow >(), std::back_inserter( cells ) );
 // std::istream_iterator nie moge wczytac, nie znajduje deklaracji wgl, dodalem biblioteke iostream, sstream, fstream i nic

  }
}

Czy to co rozumiem, rozumiem tak jak powinienem ?
Jeżeli chodzi o resztę, proszę o pomoc :(

1

Zrozumienie STL'a wymaga trochę wprawy, jeśli potrzebujesz coś prostszego to może wystarczy poniższe podejście:

#include <iostream>
#include <vector>
#include <iterator>

template<typename T>
class MyMatrix
{
	std::vector<T> m_buff; // dane trzymamy liniowo
	int m_cols = 0; // potrzebne do wyłuskania wartości
	int m_rows = 0; // można uzyć do jakiś walidacji
	
public:
	explicit MyMatrix(std::istream& is)
	{
		is >> m_cols >> m_rows;
		std::vector<T>(std::istream_iterator<int>(is), std::istream_iterator<int>()).swap(m_buff); // tworzymy tymczasowy vector i wypełniamy go danymi ze strumienia, następnie podmieniamy go z tym z naszej klasy
	}
	
	int* operator[](int row) // operator przesłaniający liniową naturę rozwiązania
	{
		return &m_buff[row * m_cols];
	}
};


int main() {
	MyMatrix<int> m(std::cin); // tutaj możesz przekazać strumień pliku wejściowego
	// uzycie m[row][col]; // jak zwykłą tablice dwuwymiarową

	return 0;
}
1

Drobna poprawka dla uogólnienia rozwiązania:

  std::vector<T>(std::istream_iterator<T>(is), std::istream_iterator<T>()).swap(m_buff);
0
void cMapa::WczytajMape()
{
        std::vector<std::vector<int>> macierz;
	
	std::ifstream plik("Mapy/mapa.txt");
	std::stringstream ss;

	int x, y;

	if (plik >> x >> y)
	{
		int wartosc;
		for (int i = 0; i < y; i++)
		{
			std::vector<int> wiersz;
			for (int j = 0; j < x; j++)
			{
				plik >> wartosc;
				wiersz.push_back(wartosc);
			}

			macierz.push_back(wiersz);
		}
	}
}

Czy nie można tak jak ja ? Bez używania operatorów, tworzenia klas, struktur, funkcji z STL etc. Użyłem nowo poznanego string stream i działa. Tylko teraz mi trochę głupio, że mój kod jest tak prosty i krótki w porównaniu do Waszych...

0

Oczywiście, że można. Jeśli kod działa zgodnie z założeniami to jest ok. Różnica jest taka, że implementacja jest mało elastyczna i oporna na rozszerzenia.
PS. Nigdzie nie używasz stringstream

0
std::vector<std::vector<int>> macierz;
std::ifstream plik("Mapy/mapa.txt");

unsigned int x, y;
if (plik >> x >> y)
{
    std::vector<std::vector<int>>(y, std::vector<int>(x)).swap(macierz);
    for (auto& vec : macierz)
        std::copy_n(std::istream_iterator<int>(plik), vec.size(), vec.begin());
}

1 użytkowników online, w tym zalogowanych: 0, gości: 1