Problem z wczytaniem polskich znaków z pliku tekstowego (Język C)

0

Witam wszystkich mam problem z wczytaniem polskich znaków z pliku tekstowego na ekran.

Program wygląda w ten sposób:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <locale.h>
#include <wchar.h>
#pragma warning(disable: 4996)
 
int main()
{
    setlocale(LC_CTYPE, "Polish");
    FILE* plik, * plik2;
    char znak, szukany, szukany_napis[2];
    int i = 0, i1, j = 0, enter = 0, enter1, raz = 0, x = 0, x1, Czy_Enter = 0, pomoc=0;
 
    plik = fopen("tekst_2.txt", "r");
    if (plik == NULL)
    {
        printf("Niestety nie udało się otworzyć tekstu 2.");
        return 1;
    }
 
    printf("=============== Tekst 2 =================\n\n");
 
    while ((znak = fgetc(plik)) != EOF)
    {
        printf("%c", znak);
    }
 
    printf("\n\n=========================================\n\n");
    printf("Wybierz jeden z powyższych znaków Tekstu 2 aby znaleźć pierwsze jego wystąpienie w Tekście 1:\n");<span style="background-color:#ff0000">
</span>
    do
    {
        if (raz > 0 && j==0)
        {
            for (int i = 0; i < pomoc; i++)
            {
                getchar();
            }
        }
                                               
        char* wejscie = (char*)malloc(256 * sizeof(char)); //alokujemy tablice z pewną ilością znaków (+znak końca linii)
        fgets(wejscie, 256, stdin);
        int pomoc = strlen(wejscie)-1; //odjac 1 aby nie liczyc znaku konca linii
 
        szukany = wejscie[0];
        free(wejscie); //zwalniamy zaalokowaną pamięć
 
        szukany_napis[0] = szukany;
 
        rewind(plik);
        while ((znak = fgetc(plik)) != EOF)
        {
            if ((strchr(szukany_napis, znak) != NULL) && j < 1 && pomoc==1)
            {
                j++;
            }
        }
        if (j == 0)
        {
            printf("Niestety tego znaku nie ma w powyższym tekście lub podałeś ciąg znaków. Podaj pojedynczy znak ponownie:\n");
        }
 
        raz++;
 
    } while (j == 0);
 
    fclose(plik);
 
    //=================================================================================================================================
 
    j = 0;
 
    plik2 = fopen("tekst_1.txt", "r");
    if (plik2 == NULL)
    {
        printf("Niestety nie udało się otworzyć tekstu 1.");
        return 1;
    }
 
    printf("\n\n============ Tekst 1 z zaznaczonym pierwszym wystąpieniem znaku \"%c\" ================\n\n", szukany);
 
    szukany_napis[0] = szukany;
    szukany_napis[1] = '\0';
 
    printf("  %d   ", enter);
    //rewind(plik2);
    while ((znak = fgetc(plik2)) != EOF)
    {
        if (Czy_Enter == 1)
        {
            printf("%3d   ", enter);
            Czy_Enter = 0;
        }
        x++;
        if ((strchr(szukany_napis, znak) != NULL) && j < 1)
        {
            j++;
            i1 = i;
            enter1 = enter;
            x1 = x;
            printf("[ => %c <= ]", znak);
            continue;
        }
        if (znak == '\n')
        {
            enter++;
            Czy_Enter = 1;
            x=0;
        }
   
        printf("%c", znak);
        i++;
    }
    if (j != 1) printf("\n\nBRAK ZAZNACZENIA - BRAK ZNAKU!");
    printf("\n\n================================== Informacje ===================================\n\n");
    if (j == 1) printf("Znaleziono znak \"%c\" w tekście 1.\nWiersz: %d\nPozycja w wierszu: %d\nPozycja w całym tekście: %d\n\n", szukany, enter1, x1-1, i1 - enter1);
    else printf("Niestety nie znaleziono znaku \"%c\" w tekście 1.\n\n", szukany);
 
    fclose(plik2);
 
    return 0;
}

Próbowałem już wielu rzeczy i nadal jest ten sam efekt, a mianowicie taki, że wyświetlanie polskich znaków poprzez np. "printf("Wybierz jeden z powyższych znaków Tekstu 2 aby znaleźć pierwsze jego wystąpienie w Tekście 1:\n");" działa bez zarzutów ale w momencie, w którym chcę wyświetlić zawartość notatnika, w którym występują polskie znaki to pojawią się krzaczki. Próbowałem również zapisywać treść notatnika z kodowaniem UTF-8, niestety nadal bez rezultatów.

Poniżej zawartości plików tekst_2.txt oraz tekst_1.txt

(tekst_2.txt)

abcde 4356
fg h123
ijQ OPVB
k? !$
x %^ mp
ółżń

(tekst_1.txt)

Na pokładzie samolotu Greene zawiera umowę z CIA, w ramach której w zamian za dostęp do ropy Stany Zjednoczone nie sprzeciwią się przewrotowi w Boliwii. W Austrii Greene uczestniczy w przedstawieniu Toski Pucciniego w operze w Bregencji. Bond podąża jego tropem, w operze zabija członka organizacji i kradnie mu nadajnik do porozumiewania się z terrorystami. W rzeczywistości przedstawienie Toski jest przykrywką dla spotkania członków organizacji o nazwie Quantum. Terroryści omawiają najważniejsze sprawy dotyczące ich globalnych operacji. Bond przerywa spotkanie i robi zdjęcie uciekającym członkom Quantum, które wysyła do M. Wśród spiskowców jest Guy Haines – zaufany doradca premiera Wielkiej Brytanii. W trakcie ucieczki z opery Bond rzekomo zabija agenta służb specjalnych, ochraniającego Hainesa. Z tego powodu M nakazuje sprowadzić agenta 007 z powrotem do siedziby MI6[1][4].

3
  1. Najlepiej ograniczyć kod do czystego problemu. Ideałem jest kod z plikami na github
  2. https://pl.wikipedia.org/wiki/Kodowanie_polskich_znak%C3%B3w (w jakim kodowaniu Twój plik ?)
  3. Jaki system operacyjny , kompilator itp. ?

To tak zeby zaczac

6
setlocale(LC_CTYPE, "Polish");

nie ma takiego locale na żadnym znanym mi systemie.
Dodaj obsługę błędów i sam się przekonaj.

0
Marius.Maximus napisał(a):
  1. Najlepiej ograniczyć kod do czystego problemu. Ideałem jest kod z plikami na github
  2. https://pl.wikipedia.org/wiki/Kodowanie_polskich_znak%C3%B3w (w jakim kodowaniu Twój plik ?)
  3. Jaki system operacyjny , kompilator itp. ?

To tak zeby zaczac

Plik jest w kodowaniu UTF-8, system to Windows 11, a pracuję w Visual Studio 2022

MarekR22 napisał(a):
setlocale(LC_CTYPE, "Polish");

nie ma takiego locale na żadnym znanym mi systemie.
Dodoaj obsługę błędów i sam się przekonaj.

Próbowałem stosować wielu locale'i także np. "pl_PL.UTF-8" jednak wtedy dzieją się cuda, ponieważ program wtedy działa w ten sposób, że po natrafieniu na pierwszy polski znak z printf'a nie wyświetla żadnych znaków dalej tzw. wyświetla na przykład ("g" zamiast "gżegżółka").

0

Sprobuj setlocale(LC_CTYPE, ".UTF8"); i jednocześnie zapisać plik źródłowy .c jako UTF8 (plik z danymi też).

0
Azarien napisał(a):

Sprobuj setlocale(LC_CTYPE, ".UTF8"); i jednocześnie zapisać plik źródłowy .c jako UTF8 (plik z danymi też).

Naprawdę nie mam pojęcia w czym jest problem bo spróbowałem właśnie tak zrobić i nadal zamiast linijki "ółńż" wyświetla się "├│┼é┼ä┼╝"

1
Quba19 napisał(a):
MarekR22 napisał(a):
setlocale(LC_CTYPE, "Polish");

nie ma takiego locale na żadnym znanym mi systemie.
Dodoaj obsługę błędów i sam się przekonaj.

Próbowałem stosować wielu locale'i także np. "pl_PL.UTF-8" jednak wtedy dzieją się cuda, ponieważ program wtedy działa w ten sposób, że po natrafieniu na pierwszy polski znak z printf'a nie wyświetla żadnych znaków dalej tzw. wyświetla na przykład ("g" zamiast "gżegżółka").

Zamiast "próbować" dodaj obsługe błędów tak jak ci pisałem. Wtedy będziesz miał przynajmniej pewność, że jeden krok zadziałał poprawnie i trzeba dłubać przy kolejnym.
Nawet prawidłowa nazwa locale nie gwarantuje, że zadziała, bo dane locale musi być zainstalowane na danym systemie.

2
Quba19 napisał(a):
Azarien napisał(a):

Sprobuj setlocale(LC_CTYPE, ".UTF8"); i jednocześnie zapisać plik źródłowy .c jako UTF8 (plik z danymi też).

Naprawdę nie mam pojęcia w czym jest problem bo spróbowałem właśnie tak zrobić i nadal zamiast linijki "ółńż" wyświetla się "├│┼é┼ä┼╝"

To mi wygląda na problem, że konsola używa innego kodowania niż UTF-8.
Albo po prostu twój program używa kodowania niezgodnego z konsolą.
Wiem jak to ogarnąć w C++ (że program używa jednego kodowania, plik innego a kosola jescze innego). Szukałem jak to zrobić w C i nie znalazłem.

Brzydkim obejściem jest zmiana strony kodowej konsoli:

chcp 65001
twojprogra.exe
0

Mi AI od binga podałą to #include "windows.h" i setConsoleOutputCP(65001);
Ale osobiście udałem się na stronę IBM, żeby odczytać specyfikację UTF8.
Nie wiem jak się ci ludzie w microsofcie ugadali, jaka reprezentacja zer i jedynek odpowiada za np. "ą".
Domyślam się, że są to 2 bajty.

1

Ok ChatGpt nakierował mnie na taki kod:

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


int main()
{
    setlocale(LC_ALL, ""); // to ma sens chcesz używać kodowania takie, jak ma ustawione użytkownik
    FILE *file = fopen("filename.txt", "r, ccs=UTF-8"); // w konicu sugestia jak wymusić kodowanie inne niż globalne na danym pliku
    ....
    fclose(f);
}

Nie testowałem, ale na pierwszy rzut oka ma to ręce i nogi.

0
MarekR22 napisał(a):

Ok ChatGpt nakierował mnie na taki kod:

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


int main()
{
    setlocale(LC_ALL, ""); // to ma sens chcesz używać kodowania takie, jak ma ustawione użytkownik
    FILE *file = fopen("filename.txt", "r, ccs=UTF-8"); // w konicu sugestia jak wymusić kodowanie inne niż globalne na danym pliku
    ....
    fclose(f);
}

Nie testowałem, ale na pierwszy rzut oka ma to ręce i nogi.

Gdy próbuję w ten sposób program nie uruchamia się i dostaję tylko komunikat (załącznik poniżej):
error.png

1

Dokumentacja potwierdza metodę: https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/fopen-wfopen?view=msvc-170#unicode-support

Ale u mnie też nie działa:

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

void copyTextFiles(FILE* in, FILE* out)
{
    char buff[1024];
    while(fscanf(in, "%1023[^\n]%*[\n]", buff) == 1) {
        fprintf(out, "%s\n", buff);
    }
}

int main(int argc, char* argv[])
{
    char * l = setlocale(LC_ALL, ""); // to ma sens chcesz używać kodowania takie, jak ma ustawione użytkownik
    if (!l) {
        perror("setlocale");
        return 1;
    }
    printf("Locale selected: %s\n", l);
    char mode[256];
    if (argc > 1) {
        sprintf(mode, "rt+, ccs=%s", argv[1]);
    } else {
        sprintf(mode, "rt+");
    }
    printf("mode = %s\n", mode);
    FILE *file = fopen("encode.c", mode); // w konicu sugestia jak wymusić kodowanie inne niż globalne na danym pliku
    if (!file) {
        perror("fopen encode.c");
        return 2;
    }
    printf("File was opened\n");
    copyTextFiles(file, stdout);
    fclose(file);
}
>cl /utf-8 encode.c /W4
Microsoft (R) C/C++ Optimizing Compiler Version 19.34.31933 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

encode.c
encode.c(8): warning C4996: 'fscanf': This function or variable may be unsafe. Consider using fscanf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
encode.c(23): warning C4996: 'sprintf': This function or variable may be unsafe. Consider using sprintf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
encode.c(25): warning C4996: 'sprintf': This function or variable may be unsafe. Consider using sprintf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
encode.c(28): warning C4996: 'fopen': This function or variable may be unsafe. Consider using fopen_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
Microsoft (R) Incremental Linker Version 14.34.31933.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:encode.exe
encode.obj

Jak próbuje użyć kodowania to leci crash.
Jak bez kodowania to wypisuje wszystko z krzaczkami :/.

0
johnny_Be_good napisał(a):

Mi AI od binga podałą to #include "windows.h" i setConsoleOutputCP(65001);

To jest hack którego jest pełno w internecie, ale to jest psucie ustawień użytkownika.

Tutaj oganiam temat dla C++ i działa bardzo dobrze: https://stackoverflow.com/a/67819605/1387438
Jak widać jak konsola nie obsługje znaku to wyświetla mi pytajniki zamiast krzaczków. CHba że dany znak można urpościć to wtedy porzuca ogonki i akcenty.

0

Prośba abyś dał zipa swoich testów ( plik który testujesz ). a nie przeklejkę jak zrobiłeś.

0

Jakim edytorem zapisałeś plik (to ważne)? Wpisz do tego edyora wszystkie polskie znaki (ąćęłńóśźżĄĆĘŁŃÓŚŹŻ), odczytaj je w programie i wyświetl jako kody znaków.

/* Wyświetlenie tekstu z "plik.txt" jako kody znaków. */
#include <stdio.h>

int main ()
{
int znak, licznik;
FILE *uchwyt;
if (uchwyt = fopen ("plik.txt", "r"))
        {
        licznik = 0;
        while ((znak = fgetc (uchwyt)) != EOF)
                printf ("%d. znak ma kod %d.\n", ++licznik, znak);
        fclose (uchwyt);
        }
else
        puts ("Nie ma pliku!");
return 0;
}

Teraz już wiesz jak są odczytywane poszczególne znaki i możesz je prawidłowo obsłużyć. Jeśli używasz jednego edytora, to nie musisz nawet wiedzieć, jak się ten schemat kodowania nazywa.

1

Z ciekaowści napisałęm pytanie na SO czemu leci ten crash:
https://stackoverflow.com/questions/76081950/crash-in-c-program-compiled-with-msvc-when-trying-to-read-a-file-with-diffrent-e

Repo z MCVE:
https://github.com/MarekR22/mixEncodingsInC.git

UPDATE
Troszkę eksperymentów, i taki kod działa:

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

void copyTextFiles(FILE* in, FILE* out)
{
    char buff[1024];
    while(fscanf(in, "%1023[^\n]%*[\n]", buff) == 1) {
        fprintf(out, "%s\n", buff);
    }
}

int main(int argc, char* argv[])
{
    char * l = setlocale(LC_ALL, ".utf-8");
    if (!l) {
        perror("setlocale");
        return 1;
    }
    printf("Locale selected: %s\n", l);
    FILE *file = fopen("example_utf-8.txt", "rt+");
    if (!file) {
        perror("example_utf-8.txt");
        return 2;
    }

    printf("File was opened\n");
    copyTextFiles(file, stdout);
    fclose(file);
    printf("Done\n");
}

Mogę zmienić stronę kodową i fallback znaków działa zgodnie z oczekiwaniem.

0

Ja potrzebowałem konwertowac podczas zapisu do pliku, odczytu z pliku, zapisu do kontrolki. Ale zrobiłem. Z 20 linijek kodu do każdej z tych 3 operacji (poza ifami)

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