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

Usuwanie spacji w zdaniu

Object Storage Arubacloud
0 głosów
1,289 wizyt
pytanie zadane 29 października 2020 w C i C++ przez Billy Użytkownik (680 p.)

Witam wszystkich,

Chciałem napisać taki kod, który usuwa spacje z podanego zdania - i działa, ale jedynie dla 1 spacji - po wpisaniu i zatwierdzeniu jakiejkolwiek spacji więcej program się wysypuje i wyrzuca błąd w kosoli:

/home/keith/builds/mingw/gcc-9.2.0-mingw32-cross-native/mingw32/libstdc++-v3/include/bits/basic_string.h
:1067: std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::reference std::__cxx11::basic_string<_CharT,
 _Traits, _Alloc>::operator[](std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::size_type) [with _Cha
rT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>; std::__cxx11::basic_string<_
CharT, _Traits, _Alloc>::reference = char&; std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::size_ty
pe = unsigned int]: Assertion '__pos <= size()' failed.

A sam program kończy się "process returned 3".

#include <iostream>
#include <string>
using namespace std;

int main()
{
    string sms;
    int i=0, j=0, counter=0;
    getline(cin, sms);
    //dynamiczna alokacja zmiennej o takiej liczbie znaków jak orginalne wczytane zdanie 
    /* jest to typ char, bo string zaalokowany w ten sposób zawiera albo numer miejsca w
    pamięci(bez użycia gwiazdki), albo przypadkowe znaki(1 lub inne (zależy chyba od 
    długości), a ostatecznie po wpisaniu 2 spacji wysypuje się tak samo */
    char *sms2 = new char[sms.length()];     
    for(i, j; i<sms.length(); i++, j++)
    {
        if(sms[j]==' ')
        {
            j++;
            sms2[i] = sms[j];
        }
        else
            {
            sms2[i] = sms[j];
            }
    }
    cout<<sms2;

    return 0;
}

Przykład danych wejściowych:
lol wow
lol wow lol
Wyjście:
lolwow
(podany wyżej błąd) 

Czy miałby ktoś pomysł, jak naprawić ten błąd?

2
komentarz 29 października 2020 przez tkz Nałogowiec (42,000 p.)
komentarz 29 października 2020 przez Billy Użytkownik (680 p.)
Problem jest taki, że ja potrzebuję specyficznie tego programu, bo muszę litery po spacji zamienić na wielkie, żeby nie były takim niezrozumiałym ciągiem, tylko każde nowe słowo zaczynało się nową literą, więc funkcja remove niestety odpada
komentarz 29 października 2020 przez tkz Nałogowiec (42,000 p.)
A zobaczyłeś dokładnie co jest w tym linku? Na dole masz przykład, który usuwa spacje.
komentarz 29 października 2020 przez Billy Użytkownik (680 p.)
Tak, przeczytałem, ale tak jak wspomniałem w komentarzu wyżej - celem tego programu będzie również zamiana na wielkie litery znaków znajdujących się bezpośrednio po spacji, do czego bardzo łatwo byłoby wykorzystać obecną pętlę (dopisując do znajdującego się w niej warunku tylko zamianę na duże litery), tylko że niestety nie działa, a używając funkcji remove tego efektu nie osiągnę
1
komentarz 29 października 2020 przez tkz Nałogowiec (42,000 p.)
komentarz 29 października 2020 przez Billy Użytkownik (680 p.)

Dobra, przyznam się szczerze, że o tym nie pomyślałem, żeby najpierw dać duże litery, a dopiero potem usunąć spacje. Plusik za to dla ciebie cheeky

Aczkolwiek fajnie by było wiedzieć, czemu ten program który napisałem wcześniej się wysypuje. No ale dzięki wielkie, teraz wszystko działa laugh

komentarz 30 października 2020 przez Billy Użytkownik (680 p.)
edycja 30 października 2020 przez Billy

@tkz, Okej, wszystko pięknie działa, ale potrzebuję, żeby pierwsza litera w zdaniu nie byłą zamieniana na większą - no to głupi nie jestem, wystarczy usunąć warunek i==0 i powinno działać, co w tym trudnego, nie? Otóż nic bardziej mylnego. Po usunięciu warunku cały kod wysypuje się tak jak wcześniej, mimo tego, że nie powinno nieść to za sobą żadnych skutków ubocznych, ale jednak... 

EDIT: Jak zostawię pustego ifa i==0, a potem else if i drugi warunek, to normalnie wszystko działa... Poddaję się...

#include <iostream>
#include <string>
#include <algorithm>

//występowanie biblioteki cctype nic nie zmienia w działaniu programu, ale dałem na wszelki wypadek
#include <cctype>

using namespace std;

int main()
{
    string sms;
    while(getline(cin, sms))
    {
        for(int i=0; i<sms.length(); i++)
        {
            //ten wykomentowany kod działa idealnie, ale jak usunę i==0, całość się wysypuje
            //if(i==0 || sms[i-1] ==' ')
                //sms[i] = toupper(sms[i]);

//tutaj po prostu rozbiłem wcześniejszy warunek na dwa
//gdy jest z pierwszym ifem - wszystko działa
//gdy go wykomentuję i zamienię else if na if, to tak jak wyżej - wysypuje się
            if (i == 0)
                sms[i] = toupper(sms[i]);

            else if (sms[i - 1] == ' ')
                sms[i] = toupper(sms[i]);
        }
        sms.erase(remove(sms.begin(),sms.end(), ' '),sms.end());
        cout<<sms<<endl;
    }
return 0;
}

1 odpowiedź

+1 głos
odpowiedź 29 października 2020 przez tangarr Mędrzec (154,860 p.)
wybrane 4 listopada 2020 przez Billy
 
Najlepsza
Twoim problemem jest nieznajomość łańcuchów C.
Napis sms2 nie posiada znaku końca napisu.

Najlepiej by było gdybyś nie bawił się w ręczne zarządzanie pamięcią i używał napisów C++ (czyli typu string).

Dodaj sobie nową zmienną bool zawierającą informację o nowym słowie - rozkaz wymiany następnej napotkanej litery na dużą.
komentarz 29 października 2020 przez Billy Użytkownik (680 p.)
Ale jak wtedy stworzyć odpowiednio dużego stringa do przechowania tego napisu? Nie mamy podanego na wejściu długości napisu, więc musimy go zadeklarować dynamicznie (co chyba da się zrobić, podobnie jak na charze), ale tak jak wykomentowałem w kodzie, string przy wyświetleniu wyświetla jedynie jeden, przypadkowy znak, dlatego też skorzystałem z chara.

Dodatkowo, jeśli byłaby to wina braku końca napisu, to wyraz z jedną spacją też by się nie tworzył, więc chyba nie w tym rzecz. Jeśli ogarnę usuwanie spacji, to zmiana na duże litery to będzie kwestia dopisania jednego warunku.
komentarz 30 października 2020 przez tangarr Mędrzec (154,860 p.)

Klasa string reprezentuje napis dynamiczny.

string napis;
napis = "Ala";
napis = napis + " kota";
napis += " i psa";
cout << napis;

Rzeczywiście, to nie jest problem końca napisu.
Dopiero teraz zauważyłem, że asercja została wywołana w operatorze indeksowania napisu. Czego zupełnie nie rozumiem, ponieważ przekroczenie zakresu odbywa się w zmiennej sms2 (która nie jest stringiem C++).
Problemem była nieprawidłowa obsługa spacji. Oto wersja poprawiona:

#include <iostream>
#include <string>
using namespace std;
 
int main()
{
    string sms;
    getline(cin, sms);
    //dynamiczna alokacja zmiennej o takiej liczbie znaków jak orginalne wczytane zdanie 
    /* jest to typ char, bo string zaalokowany w ten sposób zawiera albo numer miejsca w
    pamięci(bez użycia gwiazdki), albo przypadkowe znaki(1 lub inne (zależy chyba od 
    długości), a ostatecznie po wpisaniu 2 spacji wysypuje się tak samo */
    cout << sms << endl;
    char *sms2 = new char[sms.length()+1];
    for(int i=0, j=0; i<sms.length(); j++)
    {
        if(sms[j]==' ')
        {
            j++;
        }
        else
        {
            sms2[i] = sms[j];
            i++;
        }
    }
    cout<<sms2;
 
    return 0;
}

Skoro już rozwiązałeś swój problem (w innym komentarzu) to teraz wersja C++11
 

#include <iostream>
using namespace std;
 
int main()
{
    string sms, sms2;
    getline(cin, sms);
    // dla optymalizacji można wykonać
    // sms2.reserve(sms.length());
    for (auto c : sms) {
        if (c == ' ')
            continue;
        sms2 += c;
    }
    cout<<sms2;
    return 0;
}

 

komentarz 31 października 2020 przez Billy Użytkownik (680 p.)
Niestety, ta poprawiona wersja też nie działa, nawet bardziej niż ta poprzednia, bo nie działa już dla pierwszej spacji.
Natomiast ten drugi kod już działa perfekcyjnie. Miałbym tylko jedno pytanie: co oznacza linijka (auto c : sms)? Bo pierwszy raz widzę taką składnię w pętli for. A dodatkowo nie bardzo rozumiem, czemu c oznacza szufladkę stringa (bo tak jest traktowana z tego co widzę).

P.S. A właśnie, bo widziałem, że zauważyłeś, że problem rozwiązałem w innym komentarzu - wiesz czemu musze tam użyć pustej instrukcji warunkowej if? Bo mi nic nie przychodzi do głowy.
1
komentarz 31 października 2020 przez tangarr Mędrzec (154,860 p.)

Zapis

 for (auto c : sms)

jest "nową" (wprowadzoną w C++11) wersją pętli for. https://en.cppreference.com/w/cpp/language/range-for

Taki zapis pozwala iterować po całym zakresie wskazanego kontenera. Zmienna c przechowuje wartość aktualnego elementu. Słowo kluczowe auto oznacza automatyczną dedukcję typu podczas kompilacji. Równie dobrze mógłbym napisać

for (char c : sms)

Aby uzyskać taki efekt przy pomocy klasycznej pętli for musiałbyś użyć konstrukcji

for (int i=0; i<sms.size(); i++) {
    char c = sms[i];
    // reszta kodu
}

Jeżeli jeszcze tego nie zauważyłeś zmienna c jest pojedynczym znakiem a nie napisem.

Nie potrafiłem zrozumieć co masz na myśli mówiąc o pustym ifie. Jeszcze raz przeczytałem twój komentarz skierowany do tkz (tym razem uważnie). I już wiem co miałeś na myśli. Pustą instrukcję/blok po instrukcji warunkowej.

Spójrz na poniższy przykład

for (int i=0; i<5; i++) {
    if (i == 0) {
        // nic nie robimy
     }
     else {
         cout << "tablica[" << i-1 << "]" << endl;
     }
}

Jest bardzo podobny do twojego kodu. Ten kod wyświetli na ekranie:

tablica[0]
tablica[1]
tablica[2]
tablica[3]

Jak będzie wyglądał kod po wykasowaniu instrukcji warunkowej?

for (int i=0; i<5; i++) {
    cout << "tablica[" << i-1 << "]" << endl;
}

Niby nic się nie zmieniło. Ale czy na pewno? Po wykonaniu na ekranie zobaczysz

tablica[-1]
tablica[0]
tablica[1]
tablica[2]
tablica[3]

To właśnie ten indeks [-1] powoduje wykrzaczenie się programu. Wychodzisz poza zakres tablicy.

komentarz 4 listopada 2020 przez Billy Użytkownik (680 p.)
Okeeeeej, teraz już rozumiem :P Czyli tego elsa wystarczyłoby zamienić na ifa i wstawić tam (i!=0). No to już wszystko jasne ;) A co do tego zapisu który mi przedstawiłeś to jeszcze bardzo chętnie poczytam :D Dzięki wielki :P Plusik dla ciebie <3

Podobne pytania

+1 głos
2 odpowiedzi 428 wizyt
pytanie zadane 3 października 2022 w JavaScript przez Piotrek2713 Mądrala (5,380 p.)
0 głosów
2 odpowiedzi 895 wizyt
pytanie zadane 18 października 2019 w C i C++ przez Karpik Użytkownik (680 p.)
0 głosów
2 odpowiedzi 8,694 wizyt
pytanie zadane 3 stycznia 2018 w Java przez Kabuuz Bywalec (2,820 p.)

92,575 zapytań

141,424 odpowiedzi

319,649 komentarzy

61,961 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.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...