weryfikacja choinki z pliku

0

Cześć

Od razu zaznaczam - jestem totalnym świeżakiem i stąd moja wielka prośba o pomoc, bo tylko z tym zadaniem mam problem.
Wiem, że wydaje się, że podobny wątek był, ale w tamtym de facto zabrnęło to donikąd, a poza tym ja chcę zrobić to inaczej, bo tamto wyjdzie zbyt zagmatwane.
Mianowicie mam zadanie, aby stworzyć metodę boolean, która zweryfikuje czy choinka wczytana z pliku ma odpowiednią ilość spacji, gwiazdek i czy cała struktura jest poprawnie zbudowana. A mianowicie stworzyłem takie coś:

List<String> lines = Files.readAllLines(Paths.get("plik.txt"));
int height = lines.size();
for (int i = 0; i < lines.size(); i++) {
     System.out.println(i);
     for (int j = 0; j < lines.get(i).length(); j++) {
          if (j < (height - i) || j > (height + i)) {
                    if (!lines.get(j).equals(" ")) {
                        System.out.print(j);
                        System.out.print("choinka się nie zgadza");
                    }
                } else if (j == height) {
                    if (!lines.get(j).equals("*")) {
                        System.out.print("choinka się nie zgadza1");
                    }
                }
            }
        }

Ja wiem, że tutaj nie powinno wyświetlać za każdym razem "choinka się nie zgadza" jak się nie zgadzają znaki (teoretycznie chciałbym, aby tak działało), a powinien być zwrócenie warunku boolean, ale to tak zrobiłem póki to robię, abym wiedział, gdzie jest błąd.
Ale właśnie mi to nie działa. Mianowicie są dwa problemy:
Poza zasięgiem indeksu i weryfikacja czy znak się zgadza (metoda equals).
Wyskakuje mi takie coś (program się odpala, podaję wynik tego kodu + info z programu):

"0
0choinka się nie zgadza1choinka się nie zgadza2choinka się nie zgadzaException in thread "main" java.lang.IndexOutOfBoundsException: Index 3 out of bounds for length 3
at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70)
at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:266)
at java.base/java.util.Objects.checkIndex(Objects.java:359)
at java.base/java.util.ArrayList.get(ArrayList.java:427)
at Zadanie6.main(Zadanie6.java:39)"

I gdzie ja w tym algorytmie jest błąd?
Uprzedzam, że chciałem zmienić na iterowanie po Stringu ( metoda charAt()), ale nie ma możliwości porównania (equals()), a jak nawet tak zrobiłem pętle z wyświetlaniem w ten sposób znaków to choinka się wyświetli.
Z indeksem to używając tej samej pętli, nic nie zmieniając w konstrukcji pętli, zmieniając tylko warunki " if (!lines.get(j).equals(" "))" na wyświetlenie znaku to nie wyskakuje żaden błąd.
Pytanie, gdzie ja tutaj zrobiłem błąd? Na prawdę piszę do Was już jako ostateczność, bo próbowałem wszystkiego, przerobiłem już sporo zadań, to mam na teście, i siedzę nad tym od 4 godzin i dalej nie wiem jak rozwiązać to zadanie, aby było klarownie, czysto i łatwo. Moglibyście mi powiedzieć, gdzie zrobiłem błąd i na co zamienić? Byłbym bardzo wdzięczny

2

A co masz w pliku plik.txt ?
W tej lini masz błąd if (!lines.get(j).equals(" "))
Zrób sobie zmienną pomocniczą String line = lines.get(i) żeby nie gubić się w indeksach.

BTW metoda charAt zwraca "małego" chara (prymitywa) i wtedy porównuje się za pomocą == a nie equals.

Czyli ostatecznie ta linia powinna wyglądać line.charAt(j) != ' '

0

Dziękuję Ci bardzo! Na prawdę jestem bardzo wdzięczny, bo to zadanie z "testu" i nie mogłem dojść, gdzie jest błąd. Teraz wszystko ładnie działa, dzięki za pomoc i wyjaśnienie! :)
Przy okazji mógłbym spytać czemu w takim razie nie działa if (!lines.get(j).equals(" ")) ? Tzn co dokładnie tutaj nie działa? Bo pierw się zastanawiałem czy w ten sposób nie można wyciągnąć znaku, ale jak zrobiłem choinkę używając w ten sposób indeksowania po linii to zadziałało (dla testu). Więc stwierdziłem, że w taki sposób indeksowanie działa.

1
kilroy napisał(a):

Przy okazji mógłbym spytać czemu w takim razie nie działa if (!lines.get(j).equals(" ")) ?

Bo mieszasz i z j. Indeksujesz linie za pomocą i, a potem chcesz jednak zaindeksować za poocą j. Pewnie j jest wieksze niż lines.length i wylatujesz poza zakres

0
for (int i = 0; i < lines.size(); i++) {
            String line = lines.get(i);
            for (int j = 0; j < lines.get(i).length(); j++) {

Tak tylko dla wyjaśnienia o co mi chodziło - w tym kodzie chodziło mi o indeksowanie po j i myślałem, że to jest indeksowanie po linii, żeby wyciągać w ten sposób znak. Ale no coś na pewno w tym nie gra, bo tak jak piszesz - wylatuje poza zakres. Tylko, że nie wiem w takim razie czy ja to dobrze rozumiem.
Ale jestem mega wdzięczny za pomoc, bo męczyłem się z 4 godziny i nie mogłem dojść jak to zrobić

0

Tak to ma być:

for (int i = 0; i < lines.size(); i++) {
   for (int j = 0; j < lines.get(i).length(); j++) {
   // tutaj j jest indeksem znaku z linii lines.get(i) i tylko tej
      char c = lines.get(i).charAt(j); // <- takie coś nie spowoduje błędu bo j iteruje po linii lines.get(i)                
   }
}

To co ty napisałeś w tym poście ostatnim to:

for (int i = 0; i < lines.size(); i++) { <- iteruj po wszystkich elementach listy 'lines'
            String line = lines.get(i); // <- do zmiennej 'line' przypisz linię z indeksem 'i', w ten sposób iterując od początku do końca przejdziesz po wszystkich liniach
            for (int j = 0; j < lines.get(i).length(); j++) { // <- lines.get(i) - tym bierzesz linię z indeksem 'i'. 
//To samo co zrobiłeś linię wyżej więc jak widać nie bardzo to ma sens. zawołanie na tym `.length` 
//zwróci ci długość tej lini. Czyli indeks 'j' będzie przechodził po wszystkich znakach z tej linii. 
0

@szweszwe: Dzięki wielkie za odpisanie, ale źle mnie zrozumiałeś z mojej winy, bo zły wstawiłem kod, a co innego miałem na myśli pisząc.
Chodzi mi o fragment:

for (int i = 0; i < lines.size(); i++) {
     for (int j = 0; j < lines.get(i).length(); j++) {
          if (j < (height - i) || j > (height + i)) {
                   ** if (!lines.get(j).equals(" ")) **{

I nie rozumiem do końca czemu to nie działa, bo myślałem, że używając lines.get(j) w ostatniej linijce będę mógł wyciągnąć znak spod j i porównać go do " ". Ale właśnie wyskakuje błąd.

Zrobiłem tak, jak napisał Kamil i wszytko działa prawidłowo. Tak jak poniżej:

for (int i = 0; i < lines.size(); i++) {
            String line = lines.get(i);
            for (int j = 0; j < lines.get(i).length(); j++) {
                if (j < (height - i) || j > (height + i)) {
                    if (line.charAt(j) != ' ') {
1

No ja wiem, że nie rozumiesz i staram się to wytłumaczyć, ale bezskutecznie.

I nie rozumiem do końca czemu to nie działa, bo myślałem, że używając lines.get(j) w ostatniej linijce będę mógł wyciągnąć znak spod j i porównać go do " ". Ale właśnie wyskakuje błąd.

Lines jest LISTĄ Stringów.
lines = ["string0", "string1", "string2", "string3", "string4"]
lines.get(0) zwraca "string0"
lines.get(2) zwraca "string2"
Jeżeli iterujesz i = 0; i < lines.size(); i++ to iterujesz od 0 do lines.size() czyli rozmiaru tej listy = 5.
Teraz załóżmy, że robisz lines.get(0) - bierzesz stringa "string0"
jak zawołasz "String0".length() to zwróci ci to długość tego stringa = 7.
Iterując j po tym stringu masz indeksy od 0 do 6 bo w "string0" masz 7 literek.
jak zawołasz "string0".charAt(0) to dostajesz char 's'
dla "string0".charAt(1) masz 't'
"string0".charAt(2) masz 'r'.

I teraz co się dzieje:
zewnętrzną pętla iterując przy pomocy i przechodzi po "string0" aż do "string4" - po całych stringach. Obiektach z listy. Nie znakach.
Wewnętrzną j po znakach bo: lines.get(i) - zwraca aktualny string pod indeksem i a .length() - długość tego stringa
a to co robisz wewnątrz to lines.get(j) - czyli "weź obiekt pod indeksem j z listy lines" - a lines jest listą stringów! Rozumiesz? Iterując po znakach odwołujesz się do obiektu z listy stringów pod tym indeksem!
Co ty chcesz zrobić to zrobić tak: lines.get(i).charAt(j) - czyli z lines weź string i - a potem z tego stringa char o indeksie j - a że akurat iterujesz od 0 do lines.get(i).size() to masz pewność że to są indeksy znaków stringa 'i'.
To nie działa magicznie tak, że jak sobie zawołasz lines.get(i) to zwracasz stringa a jak lines.get(j) to zwracasz znak. No bo jak to może tak działać? Dlatego dostajesz exception że się odwołujesz do czegoś czego nie ma.

To co Kamil chciał przekazać to, zamiast kombinacji lines.get(i).charAt(j), to żebyś sobie przypisał string do zmiennej. Zrobiłeś to źle, ale przez przypadek zadziałało.
Miałeś zrobić tak:

for (int i = 0; i < lines.size(); i++) {
            String line = lines.get(i); <- tutaj sobie przypisujesz aktualny string
            for (int j = 0; j < line.length(); j++) { <- a tutaj zamiast lines.get(i) użyć line (zauważ LINE bez S, jak psikuta)
                if (j < (height - i) || j > (height + i)) { <- to to jakieś bzdety
                    if (line.charAt(j) != ' ') { <- tutaj jest okej, bo tym razem nie zrobiłeś `lines` tylko `line`

Przypisując to do zmiennej, nie musisz używać w jednej linii 2 indeksów które ci się mylą.

0

@szweszwe: Dzięki wielkie! Teraz załapałem, że popełniłem głupi błąd, jak zwykle u mnie nie zauważyłem. Akurat w tym przypadku wiedziałem jak działają listy, iterowanie po listach za pomocą funkcji itp., ale jestem na prawdę bardzo wdzięczny za takie rozpisanie, bardzo doceniam to, bo to jednak Was czas i wyjaśnianie żółtodziobowi, ale dzięki temu ja się uczę. Na prawdę bardzo doceniam i bardzo dziękuję za taką postawę!
Teraz zauważyłem, jak wytłumaczyłeś co ja robiłem, że ja po prostu zwyczajnie próbowałem iterować jeszcze raz po całej liście zamiast po Stringu. Jakby teoretycznie powinienem dać lines.get(i).get(j) oczywiście jakby była taka możliwość. A przez to, że .get() odnosiło się znowu do listy lines to nie wyskakiwał mi błąd z .get(), bo przez tą metodę nie wyciągnę znaku.
Teraz już widzę, że rzeczywiście namieszałem przez to w kodzie. Jeszcze raz dzięki wielkie!
A w takiej sytuacji to jak to wygląda w praktyce - tworzy się właśnie nowy String dla lepszej czytelności czy jednak za wszelką cenę liczy się długość kodu?

1

No ja bym to pewnie zrobił tak:
Wczytał bym sobie pierwszą linijkę z góry i sprawdził ile ma spacji do gwiazdki i ile od gwiazdki do końca (substring?). Jeżeli te wartości będą różne to już wiesz, że choinka jest nieprawidłowa. Na koniec zapisujesz sobie też ilość gwiazdek pomiędzy spacjami.
Jeżeli to będzie ok, to bym trzymał sobie w jakiejś mapce sumę spacji i gwiazdek dla tej pozycji - lub inaczej, bo w sumie na raz musisz trzymać dla jednej linii.

Potem sprawdzasz kolejny wiersz i robisz to samo co wcześniej dodatkowo porównując czy ilość spacji jest mniejsza o 2 oraz czy liczba gwiazdek jest większa o dwie w stosunku do poprzedniej.

No i tak iterujesz sobie aż do końca ;)

1

A w takiej sytuacji to jak to wygląda w praktyce - tworzy się właśnie nowy String dla lepszej czytelności czy jednak za wszelką cenę liczy się długość kodu?

Ja bym powiedział, że jak chcesz. Jak ci łatwiej z dodatkową zmienną to czemu nie. Jak widać może się okazać pomocna i uprościć dla kogoś kod. Z drugiej strony lines.get(i).charAt(j) to nie jest jakiś zapis żeby ktoś się tutaj zaczął głowić o co chodzi. Ale dla mnie nie ma tutaj lepszego rozwiązania. Sam bym to zrobił jak najkrócej więc bez tej zmiennej line.

0

@.andy: No właśnie w tym poście co wspominałem coś takiego ktoś proponował i zacząłem myśleć o tym, ale nie znam aż tylu działań na Stringach i nie wiedziałem do końca jak to ugryźć, aby to wszystko wrzucić, aby działało z pozycji metody statycznej. Z resztą widziałem, że autor sam miał duży problem, aby to "zautomatyzować". Proponowali mu, aby to podzielić na metody, ale wydawało mi się to już w ogóle skomplikowane jak na poziom jaki przerobiłem działy i wydawało się dużo metod i kodów. Więc użyłem algorytmu do tworzenia choinki, który jest szybki, krótki i "prosty" i weryfikuje czy choinka ma odpowiednią strukturę/budowę - w ten sposób jest pewne też, że zgadza się liczba spacji i gwiazdek, bo algorytm jest oparty o wysokość choinki.

@szweszwe Rozumiem, dzięki :) i jeszcze raz dziękuję za pomoc!

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