Łańcuch - wyświetlenie znaku o najniższym kodzie oraz jego kod

0

Witajcie

Mam do zrobienia zadanie i jednym z podpunktów jest wyświetlenie znaku o najniższym kodzie oraz jego kod. Udało mi się wyświetlić znak o najniższym kodzie, ale pokazuje ciągle ten sam kod. Gdzie popełniam błąd?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{

    int i, cyfry=0;
    char str[100];
    puts("\n Podaj dlugosc znakow (<=99)");
    gets(str);
    puts("\n Wczytany lancuch:");
    puts(str);

    for(i=0; str[i]!='\0'; i++)
        {
            if(str[i]>'0' && str[i]<='9')cyfry++;
        }

        printf("\n Liczba znakow ktore sa cyframi str[%i]:",cyfry);

    int ilosc, poz, n;
    n=strlen(str);
    ilosc=0, poz=0;

    for(i=0; i<n-1; i++)
        {
            if(str[i]=='*' && str[i+1]=='/')
            {
                ilosc++;
                if(ilosc==1)poz=i+1;
                i++;
            }
        }

        printf("\n Sekwencja znakow */ wystepuje %i razy",ilosc);
        printf("\n */ pojawia sie na %i pozycji po raz pierwszy",poz);


    char min;
    min=str[0];

    for(i=1; i<n; i++)
        {
            if(str[i]<min)min=str[i];
        }

        printf("\n Znak o najnizszym kodzie %c (kod%d)",min);



    return 0;
}

1
printf("\n Znak o najnizszym kodzie %c (kod%d)",min);

IMO zgubiłeś jeden parametr – dla %d. Powinno być tak:

printf("\n Znak o najnizszym kodzie %c (kod%d)", min, min);

Chyba że ta funkcja sama się domyśli, że ma dwa razy użyć tego samego parametru. ;)


Przy okazji:

for(i=0; str[i]!='\0'; i++)
{
  if(str[i]>'0' && str[i]<='9')cyfry++;
}

printf("\n Liczba znakow ktore sa cyframi str[%i]:",cyfry);

0 to też cyfra, więc powinieneś ją też brać pod uwagę:

if(str[i] >= '0' && str[i] <= '9') cyfry++;
0

Bardzo dziękuję za pomoc.

Pozdrawiam

0

Okazało się, że muszę jeszcze obliczyć ile w danym łańcuchu jest wszystkich liter (małych i dużych). Żeby nie kopiować wszystkiego to wkleję tylko wycinek:

    int i, znak=0;
    char str[100];
    puts("\n Podaj dlugosc znakow (<=99)");
    gets(str);
    puts("\n Wczytany lancuch:");
    puts(str);

    for(i=0; str[i]!='\0'; i++)
        {
            if((str[i]>='a' && str[i]<='z') || (znak >= 'A' && znak <='Z'))znak++;
        }

        printf("\n Liczba znakow ktore sa literami str[%i]:",znak);

Udało mi się zrobić tylko częściowo, bo zlicza jedynie małe litery. Wydaje mi się, że zrobiłem jakiś głupi błąd, ale nic mi nie przychodzi do głowy co zmienić.

Pozdrawiam

1

Tu jest błąd:

if((str[i] >= 'a' && str[i] <= 'z') || (znak >= 'A' && znak <= 'Z')) znak++;

Lewa para warunków sprawdza str[i], a prawa znak. W obu użyj str[i]:

if((str[i] >= 'a' && str[i] <= 'z') || (str[i] >= 'A' && str[i] <= 'Z')) znak++;
0

To znowu ja. Tym razem z pytaniem o wytłumaczenie pewnej kwestii. Jednym z podpunktów zadania było obliczyć ilość wystąpienia w danym łańcuchu sekwencji znaków „if”, oraz pozycję ostatniego wystąpienia tej sekwencji. Udało mi się zmodyfikować podpunkt z poprzedniego posta i wyszło coś takiego:

    int ilosc, poz, n;
    n=strlen(str);
    ilosc=0, poz=0;

    for(i=0; i<n; i++)
        {
            if(str[i]=='i' && str[i+1]=='f')
            {
                ilosc++;
                if(ilosc==1);
                {
                    poz=i;
                }
            }
        }

        printf("\n Sekwencja znakow if wystepuje %i razy",ilosc);
        printf("\n Ostatnia pozycja sekwencji znakow if to nr %i",poz);

Niby działa dobrze, ale nie rozumiem, dlaczego gdy było to napisane tak:

       {
            if(str[i]=='i' && str[i+1]=='f')
            {
                ilosc++;
                if(ilosc==1)poz=i;
                
            }
        }

to program źle działał. Mógłby mi ktoś to wytłumaczyć, dlaczego drugi zapis jest niepoprawny i dlaczego w tym miejscu musi być średnik:

if(ilosc==1);
2

To trochę bardziej zagmatwane, niż się wydaje.

Lamash napisał(a):

Mógłby mi ktoś to wytłumaczyć, dlaczego drugi zapis jest niepoprawny i dlaczego w tym miejscu musi być średnik: […]

Nie, tego średnika tam nie może być. Póki on jest to terminuje ten warunek, przez co kod znajdujący się po nim zawsze jest wykonywany. Ten kod:

if(ilosc == 1);
{
  poz = i;
}

jest równoznaczny z tym:

if(ilosc == 1);
poz = i;

a po usunięciu zbędnych instrukcji, z tym:

poz = i;

Równie dobrze mógłby wyglądać tak:

{
  {
    {
      {
        poz = i;
      }
    }
  }
}

bo nadmiarowe klamry nie szkodzą – kompilator je zignoruje, bo niczego sensownego nie grupują. A dlaczego bez średnika działał źle? Zobacz na wymagania:

Jednym z podpunktów zadania było obliczyć ilość wystąpienia w danym łańcuchu sekwencji znaków „if”, oraz pozycję ostatniego wystąpienia tej sekwencji.

No właśnie, ostatniego wystąpienia, a Twój warunek (bez średnika) powodował, że zmienna poz była modyfikowana wyłącznie po znalezieniu pierwszego wystąpienia, więc przechowywała pozycję pierwszego wystąpienia. Prawidłowy zapis to ten bez średnika i bez żadnego warunku:

ilosc = poz = 0;
 
for(i = 0; i < n; i++)
{
  if(str[i] == 'i' && str[i + 1] == 'f')
  {
    ilosc++; // znalazłem kolejne wystąpienie
    poz=i;   // zapamiętam więc jego pozycję
  }
}

Przy okazji:

for(i = 0; i < n; i++)
{
  if(str[i] == 'i' && str[i + 1] == 'f')

Igrasz z null-em. ;)

0

Dziękuję za wyczerpującą odpowiedź, mógłbyś tylko rozwinąć tę kwestię?

Przy okazji:

for(i = 0; i < n; i++)
{
  if(str[i] == 'i' && str[i + 1] == 'f')

Igrasz z null-em. ;)

Dodatkowo tym razem zmagam się z zadaniem gdzie muszę obliczyć ile w danym łańcuchu jest znaków niealfanumerycznych, zastąpić te znaki spacją i wyświetlić zmodyfikowany łańcuch. Pierwsze co przyszło mi do głowy to spojrzenie na tablice ASCII i wyszło mi coś takiego:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{

    int i, znak=0;
    char str[100];
    puts("\n Podaj dlugosc znakow (<=99)");
    gets(str);
    puts("\n Wczytany lancuch:");
    puts(str);

    for(i=0; str[i]!='\0'; i++)
        {
            if((str[i] >= '!' && str[i] <= '/') || (str[i] >= ':' && str[i] <= '@') || (str[i] >= ':' && str[i] <= '@') ||(str[i] >= '[' && str[i] <= '`') || (str[i] >= '{' && str[i] <= '~'))znak++;
        }

        printf("\n Liczba znakow niealfanumerycznych str[%i]:",znak);


    return 0;
}

Działa poprawnie, ale zastosowałem chyba najbardziej idiotyczny sposób na rozwiązanie tego i teraz nie mam pojęcia jakbym miał to zastąpić spacją.

Jeszcze raz dzięki za pomoc.

1
Lamash napisał(a):

Dziękuję za wyczerpującą odpowiedź, mógłbyś tylko rozwinąć tę kwestię?

Iterator pętli śmiga od 0 do n-1, natomiast warunek sprawdza pary znaków od [0,1] do [n-1,n]. Jak myślisz, co znajduje się w pamięci pod indeksem n? Igrasz nawet nie tyle z nullem, co ze śmieciami w pamięci, bo tablicy str w ogóle nie inicjalizujesz. ;)

W kodzie znajdującym się w pierwszym poście tego wątku, masz dwie pętle do dwóch różnych algorytmów, i ta pierwsza posiada prawidłowe indeksowanie iteratora:

for(i = 0; i < n-1; i++)
{
  if(str[i] == '*' && str[i+1] == '/')

Zasada indeksowania pętli w takim przypadku jest bardzo prosta. Jeśli n to liczba znaków w przeszukiwanym ciągu, a m to liczba znaków szukanego podciągu, to iterator pętli powinien indeksować od 0 do n-(m-1). Przykład:

int n = 8;
int m = 3;
int i;

for(i = 0; i < n-(m-1); i++)
//  i = 0; i < 8-(3-1)
//  i = 0; i < 8-2
//  i = 0; i < 6

Działa poprawnie, ale zastosowałem chyba najbardziej idiotyczny sposób na rozwiązanie tego i teraz nie mam pojęcia jakbym miał to zastąpić spacją.

Sprawdzasz znaki ręcznie, choć w ctype.h masz już gotową funkcję – isalnum – która sprawdza, czy znak jest alfanumeryczny. Ty potrzebujesz sprawdzić, czy znak nie jest alfanumeryczny, więc możesz skorzystać z tej funkcji i jej rezultat zanegować:

#include <ctype.h>

int i, count = 0;

for(i = 0; str[i] != '\0'; i++)
{
  if(!isalnum(str[i]))
  {
    count++;
    str[i] = ' ';
  }
}

Nic trudnego. :]

0

Dzięki za pomoc, ta funkcja do znaków alfanumerycznych dużo mi pomogła w kilku kolejnych zadaniach. Teraz muszę obliczyć ile razy w danym łańcuchu występują znaki(każdy liczony osobno):

'\'
';'
'+'
'spacja'

Przy zastosowaniu instrukcji switch, a także posortować łańcuch wg. rosnących kodów. Nie miałem na zajęciach nic związanego z wykorzystaniem tej instrukcji więc zacząłem szukać czegoś pomocnego na internecie. Trochę się pogubiłem bo mam to zrobić przy użyciu instrukcji switch, a na różnych stronach jest napisane, że instrukcji switch nie można używać z łańcuchami, i można użyć ```
strcmp


Co do sortowania wydaje mi się, że takie coś powinno zadziałać, ale kompletnie nie wiem co mam zrobić z tym switchem wcześniej:
```c
   char ch;
    int j;

    for(i=1; i<n; i++)
        for(j=0;j<n-i;j++)
        if(str[j]>str[j+1])
        {
            ch=str[j];
            str[j]=str[j+1];
            str[j+1]=ch;
        }


    printf("\n Lancuch posortowany wg. rosnacych kodow %s",str);
1
Lamash napisał(a):

a na różnych stronach jest napisane, że instrukcji switch nie można używać z łańcuchami, i można użyć strcmp.

Moment – argumentem instrukcji switch nie może być ciąg znaków, jednak Ty potrzebujesz policzyć konkretne, jednobajtowe znaki. Czyli nie możesz zrobić tak:

switch(str)

ale możesz tak:

switch(str[i])

Co do sortowania wydaje mi się, że takie coś powinno zadziałać, ale kompletnie nie wiem co mam zrobić z tym switchem wcześniej:

Jeśli w jednej pętli będziesz zliczał znaki i sortował cały ciąg, to otrzymasz błędne wyniki. Musisz to rozdzielić na dwie osobne pętle, obojętne która wcześniej a która później. Samo rozróżnienie znaków i zsumowanie wymaganych może wyglądać tak:

int back, semi, plus, space;
back = semi = plus = space = 0;

switch(str[i])
{
  case '\\': { ++back;  break; }
  case ';':  { ++semi;  break; }
  case '+':  { ++plus;  break; }
  case ' ':  { ++space; break; }
}
0

Jeszcze nie rozumiem tego w 100%, ale jeśli switch polega na tym, że ten kod:

    {++back;  break;}

wykonuje się jeśli

switch(str[i])

=

case '\\':

To czy taki zapis jest poprawny?

 switch(str[i])
    {
    case '\\': {
                ++back;
                printf("\n Znak back wystepuje %i razy", back);
                break;
               }
2

Nie potrzebujesz tych klamer w case'ach.

switch (str[i])
{
    case '\\':
        ++back;
        printf("\n Znak back wystepuje %i razy", back);
        break;
}

Co można też zapisać jako:

switch (str[i])
{
    case '\\':
        printf("\n Znak back wystepuje %i razy", ++back);
        break;
}
2

Składniowo jest poprawny, jednak merytorycznie nie. Zwróć uwagę, że dane na ekranie wyświetlasz w trakcie zliczania znaków, a raczej nie o to chodzi w Twoim zadaniu.

Kod zgrupowany w podanym case wykona się za każdym razem, gdy str[i] zawiera znak \. Jeśli str zawiera wiele backslashów, to ten printf wiele razy wyświetli tekst w konsoli, za każdym razem podając liczbę o jeden większą.

To ile razy wystąpił dany znak musisz wyświetlić po pętli zliczającej – po to zadeklarowałem cztery zmienne, aby liczba tych znaków była znana w dalszej części programu. Czyli w ten sposób:

int back, semi, plus, space;
back = semi = plus = space = 0;
 
switch(str[i])
{
  case '\\': ++back;  break;
  case ';':  ++semi;  break;
  case '+':  ++plus;  break;
  case ' ':  ++space; break;
}

printf("back: %ix\nsemi: %ix\nplus: %ix\nspace: %ix\n", back, semi, plus, space);

Ewentualnie możesz sobie rozbić tego printf na cztery osobne wywołania, aby było przejrzyście:

printf("back:  %ix\n", back);
printf("semi:  %ix\n", semi);
printf("plus:  %ix\n", plus);
printf("space: %ix\n", space);
0

Ponownie dzięki za odpowiedź jednak przy odpalaniu programu wyskakuje błąd i program się crashuje. Odpaliłem debuggera i wskazuje on na linijkę 18(Program received signal SIGSEGV, Segmentation fault.), jest to

 switch(str[i])

lub

back = semi = plus = space = 0;

Pierwszy raz mam taką sytuacje i nie wiem co jest źle w tym zapisie.

2

Nie wychodzisz gdzieś poza tablicę?
Ani pierwszy kod ani drugi nie zawiera błędu.

0

Początek jaki napisałem sam to jedynie:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{

    int i;
    char str[100];
    puts("\n Podaj dlugosc znakow (<=99)");
    gets(str);
    puts("\n Wczytany lancuch:");
    puts(str);
1

Nie używaj gets - jest to funkcja niebezpieczna bo nie sprawdza wielkości bufora i może doprowadzić do jego przepełnienia.
Lepiej będzie jeśli użyjesz fgets:

fgets(str, sizeof(str) / sizeof(char), stdin);

sizeof(str) / sizeof(char) obliczy ilość miejsc w buforze.

Możesz wkleić obecny kod?

0

Obecnie wygląda to tak:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{

    int i;
    char str[100];
    puts("\n Podaj dlugosc znakow (<=99)");
    gets(str);
    puts("\n Wczytany lancuch:");
    puts(str);


    int back, semi, plus, space;
    back = semi = plus = space = 0;

    switch(str[i])
    {
    case '\\': { ++back;  break; }
    case ';':  { ++semi;  break; }
    case '+':  { ++plus;  break; }
    case ' ':  { ++space; break; }
    }

    printf("back:  %ix\n", back);
    printf("semi:  %ix\n", semi);
    printf("plus:  %ix\n", plus);
    printf("space: %ix\n", space);



    return 0;
}

2

Używasz niezainicjalizowanego i. Ten switch-case miał być w pętli która by sprawdziła litera po literze jaki znak występuje.

0

Rzeczywiście, dopisałem teraz:

int n;
    n=strlen(str);
    for(i=0; i<n; i++)

I wszystko jest ok. Dzięki za pomoc.

1

No i te klamry we wszystkich case są nadmiarowe, z racji specyficznej dla C konstrukcji instrukcji wyboru. Mogą zostać bo nie szkodzą, jednak nie są konieczne.

0

Niestety mam kolejny problem. Muszę znaleźć w nowym łańcuchu literę o najniższym kodzie, wyświetlić tą literę oraz jej kod ASCII, a także przepisać do nowego łańcucha te znaki, których kody ASCII są parzyste.

Na samo znalezienie najniższego kodu napisałem coś takiego:

    char min;
    int n;
    n=strlen(str);
    min=str[0];

    for(i=1; i<(n-1); i++)
        {
             if(str[i]<min)min=str[i];
        }

    printf("\n Znak o najnizszym kodzie %c (kod%d)",min,min);

Ale, że muszę znaleźć tylko literę o najniższym kodzie to pomyślałem, że mógłbym to jakoś połączyć z w.w zapisem:

if((str[i] == 'a' && str[i] == 'z') || (str[i] == 'A' && str[i] == 'Z'))

Co do przepisania znaków których kody są parzyste to pomyślałem nad:

if(str[i]%2==0)

Jednak, czy dalej już wystarczy napisać printf?

2

Ten if nie ma sensu. Litera nie może być jednocześnie a i z albo A i Z.

Raczej tego nie połączysz bo chodzi o to aby znaleźć literę o najniższej wartości w ciągu i wcale nie jest powiedziane że będzie to a.

Co do parzystości to ten kod sprawdzający ok, ale jakie jest polecenie? Jeżeli masz przepisać je gdzieś to printf oczywiście nie wystarczy.

1
Lamash napisał(a):

Ale, że muszę znaleźć tylko literę o najniższym kodzie to pomyślałem, że mógłbym to jakoś połączyć z w.w zapisem:

if((str[i] == 'a' && str[i] == 'z') || (str[i] == 'A' && str[i] == 'Z'))

A funkcji isalpha nie wolałbyś użyć? ;)

Przy czym @atmal ma rację – ten warunek nigdy nie będzie spełniony.

0

Spróbowałem użyć tej funkcji isalpha jednak coś tu źle zapisałem(dałem```
#include <ctype.h>

  ```c
  for(i=1; i<(n-1); i++)
        {
            if(isalpha(str[i])<min)min=isalpha(str[i]);
        }

I źle pokazuje. Generalnie na zajęciach nie mamy zbyt wiele mówione o używaniu funkcji chyba, że są one jakoś niezbędne, a też nie chce wychodzić przed "szereg" na zajęciach.

2
if(isalpha(str[i])<min)

Cóż to? Nigdzie nie jest napisane, że isalpha zwraca kod znaku z argumentu, jeśli jest literą. Po prostu zwraca liczbę różną od 0. Dla przykładu, isalpha('A') zwraca 1024, a nie 65, więc nie możesz rezultatu porównywać ze zmienną min.

Za bardzo chciałeś skrócić kod i przekombinowałeś – naukę zacznij od najprostszych konstrukcji:

if(isalpha(str[i]) && str[i] < min)
  min = str[i];

Lewa część sprawdza czy znak jest literą, a prawa czy jest mniejszy od bieżącego minimum. Nie potrzebujesz drugi raz używać isalpha, bo pierwsze wywołanie jasno informuje o tym, czy znak jest literą, czy nią nie jest.

Generalnie na zajęciach nie mamy zbyt wiele mówione o używaniu funkcji chyba, że są one jakoś niezbędne […]

Nie możesz wszystkiego pisać ręcznie – po to jest biblioteka standardowa, aby z niej korzystać i nie tracić czasu na wymyślanie koła na nowo przy każdej możliwej okazji.

[…] a też nie chce wychodzić przed "szereg" na zajęciach.

Myślisz, że lepsze zrozumienie tematu od reszty ”szeregu” jest wadą?

0

Dzięki za wyjaśnienie kwestii z isalpha.

Myślisz, że lepsze zrozumienie tematu od reszty ”szeregu” jest wadą?>

Ogólnie to nigdy bym tak nie pomyślał, ale prowadząca moją grupę laboratoryjną jest naprawdę specyficzną kobietą :/

Teraz już chyba ostatnim co mi zostało to obliczyć ile razy i na których pozycjach występuje znak # i przepisać do nowego łańcucha znaki będące cyframi systemu szesnastkowego. Co do 2 części zadania z HEXem to myślę, że uda mi się zrobić samemu. W 1 części obliczyłem ile razy występuje '#':

    int ilosc, poz, n;
    n=strlen(str);
    ilosc=0, poz=0;

    for(i=0; i<n; i++)
        {
            if(str[i]=='#')
            {
                ilosc++;
                poz=i;
            }
        }

        printf("\n Sekwencja znaku # wystepuje %i razy",ilosc);

I działa chyba poprawnie. Co do obliczenia na jakich pozycjach występuje znak '#' to napisałem coś takiego:

    char znajdz;
    znajdz=#
    i=0;

    while(str[i]!='0')
        {
            if(str[i] == znajdz)
            {
                printf("Znak # wystepuje na pozycjach %d\n", i);
            }
            i++;
        }

Jednak nie mam nawet okazji sprawdzić, czy to działa bo mam błąd w tej linijce:

znajdz=#
1
Lamash napisał(a):

Jednak nie mam nawet okazji sprawdzić, czy to działa bo mam błąd w tej linijce:

znajdz=#

No bo próbujesz do zmiennej wpisać coś, co znakiem nie jest i przy okazji coś, co nie ma sensu i czego kompilator nie rozumie. Wpisanie znaku # do zmiennej powinno wyglądać tak:

znajdz = '#';

Jak widać, zapomniałeś znaku objąć apostrofami. Kolejny błąd masz w tej linijce:

while(str[i] != '0')

Warunkiem działania pętli jest znak inny niż 0. Wiem, że chciałeś zrobić pętlę, która będzie działać aż do napotkania znaku null, tyle że powinieneś ten warunek napisać tak:

while(str[i] != '\0')

Jeśli chodzi o pierwszą część, czyli zliczanie wystąpień znaku # to niepotrzebnie przypisujesz wartość iteratora do zmiennej poz, bo tego poz i tak nigdzie dalej nie używasz. Zresztą ciągle nadpisujesz jej wartość, więc i tak nie ma ona sensu. Wystarczy tyle:

for(i = 0; i < n; i++)
  if(str[i] == '#')
    ilosc++;

Co do drugiej części – Twój kod jest prawidłowy. Tylko poprawnie ustaw wartość zmiennej znajdz.

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