Błąd w kodowaniu przy operacjach na tekście

0

Mam pytanie o zadanie:

• Zadeklaruj bufor tekstowy pomocniczy ( char[] ) o rozmiarze 200.
• Wczytaj do niego z klawiatury zdanie zawierające kilka wyrazów.
• Zadeklaruj łańcuch znaków (dynamiczna alokacja) o rozmiarze dokładnie takim, aby wczytane zdanie zmieściło się w nim.
• Przekopiuj tekst z bufora pomocniczego do bufora dynamicznego.
• Wydrukuj bufor dynamiczny na ekranie.
• Dokonaj operacji negacji bitowej na każdym znaku (za wyjątkiem znaku końca '\0').
• Następnie dokonaj operacji przesunięcia bitowego w prawo o 1, dla każdego znaku (za wyjątkiem znaku końca '\0').
• Wydrukuj bufor dynamiczny na ekranie.

to mój kod:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
    char buffer[200];
    printf("Podaj zdanie:\n");
    scanf("%[^\n]s", buffer);
    int n = -1;
    while(buffer[++n]);
    char *text;
    text = (char*)malloc(sizeof(char) * n + 1);
    strcpy(text, buffer);
    puts(text);
    putchar('\n');
    for(int i = 0; i<n; i++)
    {
        text[i] = ~(text[i]);
    }
    for(int i = 0; i<n; i++)
    {
        text[i] = text[i] >> 1;
    }
    puts(text);

    return 0;
}

chodzi mi o ta negację bitowa i przesunięcie bitowe. Dobrze mam to zapisane? przykładowe co dostaję w odpowiedzi to:

Podaj zdanie:
pies i kot
pies i kot
����������

pytajniki i puste pole, tak ma być?

2

Działanie jest chyba dobre, ale ten program ma problem z wydajnością i bezpieczeństwem. Poprawna wersja:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main ()
{       char    temp[201], *buf, c, *p;
        temp[200] = '\n';
        puts ("Podaj zdanie:");
        fgets (temp, 200, stdin);
        *(strchr (temp, '\n')) = 0;
        buf = malloc (strlen (temp) + 1);
        if (buf == NULL)
        {       puts ("Nieudana rezerwacja pamieci.");
                return 1;
        }
        strcpy (buf, temp);
        puts (buf);
        for (p = buf; c = *p; ++p)
                *p = (~c) >> 1;
        puts (buf);
        free (buf); return 0;
}
2

@maksym572285,

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
    char buffer[200];
    printf("Podaj zdanie: ");
    scanf("%[^\n]",buffer); // bez s
    int size=strlen(buffer);
    char *text=(char*)malloc(size+1); // sizeof(char) zawsze 1, nie ma co dzielić na 2 wierszy
    strcpy(text,buffer);
    printf("%s\n",text); // nie ma co robić tego w 2 wiersze
    for(int i=0;i<size;++i) text[i]^=-1; // xor -1 da ten sam efekt
    for(int i=0;i<size;++i) text[i]>>=1; // istnieje operacja skrócona
    printf("%s\n",text);
    free(text); // przydzieliłeś nie zapomnij zwolnić
    return 0;
}

Aczkolwiek całkiem możliwe że zamiast tych 2-ch pętli potrzebujesz:
for(int i=0;i<size;++i) text[i]=((unsigned)(unsigned char)~text[i])>>1; aby pozbyć się śmieci w starszym bicie.
https://ideone.com/kpwiau

1

Dla tej operacji to co się drukuje zależy od tego jaki jest ustawiony rodzaj kodowania znaków w systemie.
W Windows zwykle ustawione jest kodowanie jedno-bajtowe zależne od języka systemu. Na nim ta operacje wypisze jakieś krzaczki (rożne znaki).
Na POSIXach (Linux/Unix/BSD(MacOS)) zwykle używane jest kodowanie UTF-8. Twoja operacja wprowadza znaki o negatywnej wartości, które w UTF-8 oznaczą wielobajtową sekwencję dla znaku.
Mało tego istnieje możliwość, że taka sekwencja będzie nieprawidłowa z punktu widzenia UTF-8.
Te pytajniki mnie nie zaskakują.

Teraz przeskok do unsigned char zmienia zachowania oepratora >> bo na wiodący bit będzie wskakiwać zero, a nie kopia wiodącego bitu argumentu.

0

Pytajniki oznaczają, że terminal nie rozpoznał znaku. Wyjście programu można przekierować do np. hexel, wtedy widać jakie bajty są wysyłane do terminala.

PS C:\dev> .\b.exe
Podaj zdanie: ala
ala
���
PS C:\dev> .\b.exe | hexyl
ala
┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
│00000000│ ef bb bf 50 6f 64 61 6a ┊ 20 7a 64 61 6e 69 65 3a │×××Podaj┊ zdanie:│
│00000010│ 20 61 6c 61 0d 0a 3f 3f ┊ 3f 0d 0a                │ ala__??┊?__     │
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘
PS C:\dev> cmd
Microsoft Windows [Version 10.0.19045.2486]
(c) Microsoft Corporation. All rights reserved.

C:\dev>b.exe | hexyl
ala
┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
│00000000│ 50 6f 64 61 6a 20 7a 64 ┊ 61 6e 69 65 3a 20 61 6c │Podaj zd┊anie: al│
│00000010│ 61 0d 0a cf c9 cf 0d 0a ┊                         │a__×××__┊        │
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘

Z innych programów konwertujących polecam też xxd z opcją -g1 lub -b (wyświetla binarnie).


PS. dla np. litery a, która bitowo wygląda tak 01100001, po zanegowaniu powinno dać 10011110. Przesunięcie bitowe o jeden w prawo da w wyniku 11001111 a to jest jeden ze znaków unicodu, który akurat jest niepoprawny. Gdyby przesunięcie bitowe zrobić tak:

  text[i] = (unsigned char)text[i] >> 1;

to zamiast pytajnika byłoby litera O (01001111)

0

Kurcze admin dałbyś minutę.

Miałem zagwozdkę z sensem tego zadania, przez chwile myślałem, że może coś nie kumam

Mamy char które chcemy shiftować może ono jest signed może unsigned. Ale zostanie promowane do inta zanim cokolwiek się stanie. Int zawsze tu będzie signed.
Więc zawsze mamy ub bo shiftujemy signeda.
!!!! EDIT ->>> byłoby UB jakby było w lewo a jest w prawo w zadaniu. Więc wszystko jest spoko. Czyli nie trzeba kombinować. Chociaż przy minusie nawet w prawo jest to dosyć mętna sprawa.

Autorze wątasa prosze bo sam musiałem doczytać ;-)
https://port70.net/~nsz/c/c99/n1256.html#6.5.7

Chyba najrozsądniej byłoby przekopiować pamięć do wyzerowanego unsigned int, zrobić shit a potem przekopiować nasze 8 bitów zpowrotem

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