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

Konwertowanie string'a na tablicę int'ów

VPS Starter Arubacloud
0 głosów
1,168 wizyt
pytanie zadane 30 sierpnia 2018 w C i C++ przez maciek061 Gaduła (4,490 p.)

Witam. Po dłuższej przerwie od programowania, postanowiłem że stworzę program wykonujący operacje arytmetyczne na bardzo dużych liczbach całkowitych. 

Zacząłem od dodawania. Plan był taki, że funkcja dostaje dwa stringi i zamienia je na tablice intów. Przykład:

238742138745421423545435436  |  42938421398434214213421346346

238742138  745421423  545435436  |  42  938421398  434214213  421346346

A potem odpowiednio je dodać (jeśli wynik jest większy od 1000000000 to jedynkę dodać to poprzedniej komórki. 

Więc spróbowałem napisać tę zamianę na int'y. Ciężka to była przeprawa, na razie mam taki kod: 

#include <iostream>
#include <cstdlib>
#include <string>

using namespace std;

void dodawanie(string a, string b);

int main()
{

    string a,b;
    cin>>a>>b;
    dodawanie(a,b);
    return 0;
}

void dodawanie(string a, string b)
{
    int dlugoscA = a.length(); int dlA = dlugoscA%9;
    int dlugoscB = b.length(); int dlB = dlugoscB%9;
    int maksdl = a.length();
    if(b.length()>maksdl) maksdl = b.length();

    int komorek = (maksdl/9)+1;
    int liczba[komorek], arg1[komorek], arg2[komorek]; for(int i=0; i<komorek; i++) {liczba[i]=0; arg1[i]=0; arg2[0];}

    int licznikcyfr_A=dlA;
    int licznikcyfr_B=dlB;




    for(int i=0; i<komorek; i++)
    {


        if(i==0)
        {
            string resztaA="0";
            for(int j=0; j<dlA; j++)
            {
                resztaA+=a[j];
            }

            string resztaB="0";
            for(int j=0; j<dlB; j++)
            {
                resztaB+=b[j];
            }

            arg1[0]=atoi(resztaA.c_str());
            arg2[0]=atoi(resztaB.c_str());
        }
        else
        {
            string rA="0";
            for(int j=(licznikcyfr_A+1); j<=(licznikcyfr_A+9); j++)
            {
                rA+=a[j];
            }

            string rB="0";
            for(int j=(licznikcyfr_B+1); j<=(licznikcyfr_B+9); j++)
            {
                rB+=b[j];
            }

            arg1[i]=atoi(rA.c_str());
            arg2[i]=atoi(rB.c_str());

            licznikcyfr_A+=9; licznikcyfr_B+=9;

        }



    }

    for(int i=0; i<komorek; i++)
    {
        cout<<arg1[i];
    } cout<<" ";
    for(int i=0; i<komorek; i++)
    {
        cout<<arg2[i];
    } cout<<endl;



}

(Na końcu test, czy dane zostały poprawnie zamienione) I tu pojawia się problem, ponieważ program jakby pomija niektóre cyfry, albo zastępuje je zerami. 

INPUT: 453187949023468921364897132 8746821376489213648921649824

OUTPUT: 053187949023468921364897132 846821376489213648921649824

Domyślam się, w czym mniej więcej tkwi problem, ale odpowiednie poprawki w kodzie wykraczają poza możliwości mojego mózgu...

Z góry dzięki za pomoc. 

1 odpowiedź

+1 głos
odpowiedź 30 sierpnia 2018 przez RafalS VIP (122,820 p.)
wybrane 31 sierpnia 2018 przez maciek061
 
Najlepsza

O mamunciu. Ten kod jest brzydki :P Bardzo.

W tej linijce, która jest o 3 razy za długa - zielonego pojecia nie mam czemu tak zrobiłeś:

for(int i=0; i<komorek; i++) {liczba[i]=0; arg1[i]=0; arg2[0];}

ale to na wynik wpływu nie miało.

Wystarczyło zrobić kilka printów po tych pętlach i widać co się dzieje:

i == 0: resztaA = 0resztaB = 08
cutting string a from 1 to 9 //w drugim obiegu petli (i==1) pomijasz jeden znak
cutting string b from 2 to 10
constructed: ra = 0531879490  rb = 0468213764
cutting string a from 10 to 18
cutting string b from 11 to 19
constructed: ra = 0234689213  rb = 0892136489
cutting string a from 19 to 27
cutting string b from 20 to 28
constructed: ra = 064897132   rb = 021649824
453187949023468921364897132 8746821376489213648921649824
053187949023468921364897132 846821376489213648921649824

Dodajesz te zera z przodu, żeby zrobić miejsce na przesuniecie przy dodawaniu? Zrobiłbym to później. W tym momencie, jeśli długość Ci się ładnie podzieli bez reszty to dostajesz atoi("0") czyli niepotrzebne zero w zerowym elemencie tablicy intów.

Poprawianie tego w obecnym stanie może Cię co najwyżej zniechęcić do programowania :D

Popraw strukture programu a debugowanie będzie o niebo łatwiejsze.

No więc tak:

  • for (int j = (licznikcyfr_B)...
    ....
    licznikcyfr_B += 9;

    czemu po prostu nie inkrementujesz od razu licznika? Wtedy nie masz szans pominąć znaków tak jak zrobiłeś to teraz.

  • pętla z if i == 0 jest głupia. Wywal przypadek z i == 0 przed pętle a pętle zacznij od 1. Po co za każdym razem sprawdzać czy aby na pewno nie jest i == 0, gdy wiemy ze bedzie zerem tylko na poczatku

  • sformatuj ten kod jakoś... błagam, nie rób tego sobie i nam, tego sie nie da czytać:

    	int liczba[komorek], arg1[komorek], arg2[komorek]; for (int i = 0; i<komorek; i++) { liczba[i] = 0; arg1[i] = 0; arg2[i] = 0; }

    czemu to jest jedna linijka :P?

    int dlugoscA = a.length(); int dlA = dlugoscA % 9;

    po co zmienna dlugoscA? a.length() jest czytelniejsze. dlA to fatalna nazwa, mi nie mówi nic.

  • nie pisz wszystkiego - jak np kopiowania kawałka stringa ręcznie pętlami for, są do tego fajne metody, chociażby string::substr

 

komentarz 30 sierpnia 2018 przez maciek061 Gaduła (4,490 p.)

Dzięki za to poświęcenie indecision

Napiszę to inaczej od nowa i odezwę się w razie problemów

komentarz 30 sierpnia 2018 przez RafalS VIP (122,820 p.)

Napisałem sobie to dla zabawy. Jeśli jesteś ciekawy to łap:

#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <iterator>

std::vector<int> string_to_int_vector(std::string str, int chars_per_int);

int main()
{
	constexpr int chars_per_int = 9;
	std::string numbers[] = { "453187949023468921364897132", "8746821376489213648921649824" };
	for (auto str : numbers) {
		std::vector<int> result = string_to_int_vector(str, chars_per_int);
		//fajny one-liner do wypisywania kontenerów :P
		std::copy(result.begin(), result.end(), std::ostream_iterator<int>(std::cout, " "));
		std::cout << std::endl;
	}
	return 0;
}

std::vector<int> string_to_int_vector(std::string str, int chars_per_int) {
	int remainder = str.size() % chars_per_int;
	int current_index = 0;
	std::vector<int> result;
	if (remainder != 0) {
		result.push_back(std::stoi(str.substr(current_index, remainder)));
		current_index += remainder;
	}
	while (current_index < str.size()) {
		result.push_back(std::stoi(str.substr(current_index, chars_per_int)));
		current_index += chars_per_int;
	}
	return result;
}

 

komentarz 30 sierpnia 2018 przez maciek061 Gaduła (4,490 p.)

Przy próbie kompilacji Twojego kodu kompilator wywala mnóstwo błędów. 

Ja w tym czasie napisałem coś takiego

#include <iostream>
#include <cstdlib>
#include <string>

using namespace std;

string dodawanie(string a, string b);

int main()
{

    string a,b;
    cin>>a>>b;
    cout<<dodawanie(a,b);
    return 0;
}
string dodawanie(string a, string b)
{
    while(a.length()>b.length())
        b="0"+b;
    while(b.length()>a.length())
        a="0"+a;
    /// Oba napisy sa tak samo dlugie

    string wynik[a.length()];

    cout<<wynik<<endl;

    int liczba=0, bufor=0;

    for(int i=a.length()-1; i>=0; i--)
    {
        wynik[i]='0';
        liczba=((a[i]-48) + (b[i]-48));
        liczba+=bufor; /// Z poprzedniej cyfry
        bufor = liczba/10;
        wynik[i] = liczba%10;

    }
    return wynik;
}

I jest problem z napisem wynik, jakoś nie można się po nim poruszać. Co robię źle?

komentarz 31 sierpnia 2018 przez RafalS VIP (122,820 p.)
edycja 31 sierpnia 2018 przez RafalS

 mnóstwo błędów.

Pewnie dlatego, że używasz prehistorycznej wersji języka. Ja kompiluje w standardzie C++17. W tym kodzie jest troszkę C++11, jeśli kompilujesz starszym to nic dziwnego, że są błędy. Polecam przejść na co najmniej C++11. Za rok z hakiem wychodzi kolejna wersja, a niektórzy dalej z 7-letniego C++11 nie korzystają :P

string wynik[a.length()];

tutaj tworzysz tablicę a.length() stringów. Prawdopodobnie chciałeś stworzyć jeden string o dlugosci a.length():

string wynik(a.length(), '0');

Czego w sumie też nie potrzebujesz robić. Możesz tak jak wcześniej rozbudowywać string, czyli zamiast:

wynik[i] = liczba%10;

użyj:

wynik += std::to_string(liczba%10);

Takie podejście załatwi Ci też problemy gdy wynik wychodzi 2 cyfrowy. Przy okazji zapomniałeś o konwersji inta na string. Jeśli dodasz do stringa np 43 to zostanie wstawiony char o tym numerze, czyli akurat znak '+'.

komentarz 31 sierpnia 2018 przez maciek061 Gaduła (4,490 p.)
Pobierałem Code::Blocksa z oficjalnej strony jakieś 2 tygodnie temu, nie powinien kompilować w najnowszym standardzie?
komentarz 31 sierpnia 2018 przez RafalS VIP (122,820 p.)
komentarz 31 sierpnia 2018 przez maciek061 Gaduła (4,490 p.)

Nie chciało mi się zmieniać mojej "prehistorycznej" wersji C++, więc zrobiłem to trochę inaczej, ale grunt że działa.

string dodawanie(string a, string b)
{
    while(a.length()>b.length())
        b="0"+b;
    while(b.length()>a.length())
        a="0"+a;
    /// Oba napisy sa tak samo dlugie

    string wynik(a.length(), '0');

    int liczba=0, bufor=0;

    for(int i=a.length()-1; i>=0; i--)
    {
        liczba=( (a[i]-48) + (b[i]-48) );
        liczba+=bufor; /// Z poprzedniej cyfry
        bufor = liczba/10;
        wynik[i] = (liczba%10)+48;
        liczba=0;
    }
    if(bufor!=0) 
    { // Gdy nie mieści się w ostatniej cyfrze
        char buf=bufor+48;
        wynik=buf+wynik;
    }

    return wynik;
}

 

Podobne pytania

0 głosów
1 odpowiedź 159 wizyt
0 głosów
2 odpowiedzi 241 wizyt
pytanie zadane 28 maja 2016 w PHP przez GaCeL Dyskutant (7,500 p.)
0 głosów
1 odpowiedź 240 wizyt
pytanie zadane 15 lutego 2021 w C i C++ przez Rainbow99 Początkujący (430 p.)

93,023 zapytań

141,986 odpowiedzi

321,290 komentarzy

62,370 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

Wprowadzenie do ITsec, tom 2

Można już zamawiać tom 2 książki "Wprowadzenie do bezpieczeństwa IT" - będzie to około 650 stron wiedzy o ITsec (17 rozdziałów, 14 autorów, kolorowy druk).

Planowana premiera: 30.09.2024, zaś planowana wysyłka nastąpi w drugim tygodniu października 2024.

Warto preorderować, tym bardziej, iż mamy dla Was kod: pasja (użyjcie go w koszyku), dzięki któremu uzyskamy dodatkowe 15% zniżki! Dziękujemy zaprzyjaźnionej ekipie Sekuraka za kod dla naszej Społeczności!

...