Объединение двух кодовых страниц в одной программе на языке C

Я делаю программу, которая читает файл, содержащий символы двух разных алфавитов (кириллицы и немецкого языка). Однако при выводе на терминал ö, ä и ü отображаются как ?.

До сих пор я пробовал:

  • используя system("chcp 1251")
  • изменение кодировки файла, из которого читает моя программа

Есть ли способ, чтобы программа прочитала символы обоих алфавитов? Есть ли какая-то «смешанная кодовая страница», которую я пропустил?

Код:

    void readG(){
        system("cls");
    
        // open the file in read mode
        fptr = fopen("C:\\Users\\pl\\projects\\sources\\lernwortschatz.txt", "r");
    
        // print title
        printf("LERNWORTSCHATZ\n");
        printf("A1\n");
        printf("-------------------------------------------------\n");
    
        // read and print the file's contents
        while(fgets(str, 10000, fptr))
        {
            printf("%s", str);
        }
    
        // close the file
        fclose(fptr);
    }

Пример: Что ожидается: ergänzen - попълвам

Что получается: erg?nzen - попълвам

Что я пытаюсь сделать с файлом: Я хочу получить все его содержимое, а затем сразу распечатать без сохранения.

Часть результата в байтах: 30 20 2д 20 4д 65 69 6е 65 20 57 3ф 72 74 65 72 20 69 6д 20 4б 75 72 73 0а 2д 2д 2д 2д 2д 2д 2д 2д 2д 2д 2д 2д 2д 2д 2д 2д 2д 2д 2д 2д 2д 2д 2д 2д 2д 2д 2д 2д 2д 2д 2д 2д 2д 0а 61 6е 73 65 68 65 6е 20 2д 20 фффффффе2 фффффффе8 фффффффе6 0а 64 61 73 20 42 69 6с 64 2с 2д 65 72 20 2д 20 ффффффеа фффффффе0 ffffff0 ffffff2 фффффффе8 офигеть ффффффеа фффффффе0

Вот часть файла, который читает программа: ansehen - виж das Bild,-er - картинка хёрен - слушам noch einmal - еще один пункт анкройзен - зачерквам/попълвам

Вот эта часть файла в шестнадцатеричном представлении:

00000000: 2d2d 2d2d 2d2d 2d2d 2d2d 2d0d 0a61 6e73  -----------..ans
00000010: 6568 656e 202d 20e2 e8e6 0d0a 6461 7320  ehen - .....das
00000020: 4269 6c64 2c2d 6572 202d 20ea e0f0 f2e8  Bild,-er - .....
00000030: edea e00d 0a68 3f72 656e 202d 20f1 ebf3  .....h?ren - ...
00000040: f8e0 ec0d 0a6e 6f63 6820 6569 6e6d 616c  .....noch einmal
00000050: 202d 20ee f9e5 20e5 e4e8 ed20 effa f20d   - ... .... ....
00000060: 0a61 6e6b 7265 757a 656e 202d 20e7 e0f7  .ankreuzen - ...

В какой кодировке изначально файл? В этом вся разница. Как вы читаете файл?

AKX 25.07.2024 09:32

Читаемый файл находится в Windows 1251, и я читаю его с помощью функции fgets().

efendi 25.07.2024 09:34

Использовать инструмент или библиотеку для чтения файлов в любой кодировке и преобразования в UTF-8?

Some programmer dude 25.07.2024 09:35

Файл, закодированный в Windows-1251, не может содержать глифы ö, ä и ü, поскольку они не существуют в этой кодовой странице.

AKX 25.07.2024 09:36

@Someprogrammerdude Это ускользает от того факта, что, например. strlen() и друзья не будут поступать правильно в UTF-8 (особенно этот вопрос касается не-7-битных символов), так что это потребует еще большей сложности...

AKX 25.07.2024 09:37

помните, что текстовый терминал старой школы может отображать только одну кодовую страницу за раз! И это то, для чего был и предназначен C.

jwenting 25.07.2024 09:40

Покажите пример файла, желательно с шестнадцатеричным дампом, и покажите, как можно отличить кириллические и немецкие символы одной и той же кодовой точки в разных кодировках? Или они на самом деле в Unicode? А также покажи свой код

phuclv 25.07.2024 09:40

Если у вас есть файл, в котором смешаны Windows-1251 и Windows-1252, то будет очень сложно определить, когда символ 0xFD равен ü, а когда э.

AKX 25.07.2024 09:40

Какова ваша цель с файлами и текстами? Только чтобы сразу распечатать? Сделать обработку? Что-то другое? Пожалуйста, предоставьте нам как можно больше подробностей об исходной и основной проблеме, которую вам необходимо решить, чтобы нам было легче предложить вам подходящее решение.

Some programmer dude 25.07.2024 09:55

Может быть, включить несколько байтов входного файла, чтобы мы могли увидеть, с чем на самом деле имеем дело? Возможно od -tx1 lernwortschatz.txt | head -n 8, если вы используете Unix

Paul Hankin 25.07.2024 10:03

Если вы не используете Unix (похоже, что вы используете Windows), вы можете изменить свой код с printf("%s", str)` на for(int i=0; str[i]; i++) printf("%02x ", str[i]); printf("\n");, чтобы отображать байты, а не содержимое строки. Вставьте часть результата в вопрос.

Paul Hankin 25.07.2024 10:13

@phuciv Во-первых, добавлен код с примером. Во-вторых, я понятия не имею, что такое hexdump. Наконец, что вы подразумеваете под тем, как можно различать кириллические и немецкие символы одной и той же кодовой точки в разных кодировках?

efendi 25.07.2024 10:17

Как поставить ergänzen - попълвам в файл? «Я понятия не имею, что такое шестнадцатеричный дамп». Вам нужно погуглить, потому что вы не сможете выполнять свою работу, не зная своих основных инструментов.

n. m. could be an AI 25.07.2024 10:20

@Someprogrammerdude добавил, что я собираюсь сделать с программой.

efendi 25.07.2024 13:36

@PaulHankin добавил часть результата в байтах.

efendi 25.07.2024 13:36

@phuclv добавил шестнадцатеричное представление

efendi 25.07.2024 13:36

«Что я пытаюсь сделать с файлом: я хочу получить все его содержимое, а затем немедленно распечатать его без сохранения». Что ж, это должно работать нормально, если вы запустите свою программу и перенаправите ее в файл (например, myprogram.exe > foo.txt). Однако ваш терминал не может интерпретировать байты, которые вы пытаетесь вывести, и поэтому вы получаете ?.

AKX 25.07.2024 14:01

Шестнадцатеричный дамп — это входной файл или результат копирования вывода с терминала?

Paul Hankin 25.07.2024 15:39

В вашем шестнадцатеричном дампе нет ergänzen. Покажите, пожалуйста, как выглядит файл с этим конкретным словом.

n. m. could be an AI 29.07.2024 18:18
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
19
125
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Итак, файл, который у вас есть, действительно имеет две разные кодировки однобайтовых символов в каждой строке. Это настоящий технический подвиг, который можно было бы осуществить с помощью любого обычного текстового редактора! :)

Давайте возьмем строку hören68 3f 72 65 6e 20 2d 20 f1 eb f3 f8 e0 ec в качестве примера, но я собираюсь немного изменить ее, потому что шестнадцатеричный дамп, который вы показываете, уже поврежден; байт 3F — это знак вопроса, а не то, что было бы ö в ISO-8859-1 (F6).

Я собираюсь использовать Python, чтобы проиллюстрировать проблемы, с которыми вы столкнетесь, поскольку он хорошо справляется с различными кодировками.

>>> x = '68 f6 72 65 6e 20 2d 20 f1 eb f3 f8 e0 ec'
>>> b = bytes.fromhex(x)
b'h\xf6ren - \xf1\xeb\xf3\xf8\xe0\xec'

Если мы просто декодируем шестнадцатеричную кодировку этих байтов в строку байтов, мы сможем увидеть ее презентацию Python repr, где все печатаемые 7-битные байты ASCII отображаются как сами по себе, а все остальное отображается как escape-последовательность. Не обманывайтесь, это не текст, читабельный для человека, это просто последовательность байтов, которая частично выглядит читабельной.

Хорошо, давайте попробуем декодировать это в текст как ISO-8859-1 (также известный как Latin-1) (что близко к кодовой странице CP1252).

>>> b.decode("latin-1")
'hören - ñëóøàì'

Мы видим, что ö для hören декодируется хорошо, но кириллица не читается моджибаке.

Тогда поступим по-другому:

>>> b.decode("cp1251")
'hцren - слушам'

Немецкий получается немного неудачным, потому что байт \xf6 интерпретируется как ц в CP1251, а русский проверяется (во всяком случае, согласно Google Translate).

Итак, если бы мы использовали Python, мы бы декодировали это, разделив его и декодировав каждую половину:

>>> de_bytes, _, ru_bytes = b.partition(b" - ")
>>> (de_bytes.decode("latin-1"), ru_bytes.decode("cp1251"))
('hören', 'слушам')

(и это действительно отлично распечатывается на терминале моего Mac, а также в Режиме Python UTF-8 в Windows).

Теперь вернемся к стране C: проблема в том, что fgets() и его друзьям плевать на кодировки — они все просто байты (хотя fgets() знает, что байт 0x0a (10 в десятичном формате) — это символ новой строки в кодировке ASCII, и перестает там читать).

Когда вы читаете эти байты, вы получаете именно эти байты, и ваше приложение должно их интерпретировать. Когда вы выводите эти байты с помощью printf() на обычном терминале Windows, он будет использовать текущую кодовую страницу вывода консоли для перевода байтов в глифы.

Технически вы можете правильно вывести эти файлы на свой терминал Windows с помощью чего-то вроде

  1. прочитать строку
  2. переключиться на кодовую страницу 1252 (SetConsoleOutputCP(1252);)
  3. выписывайте каждый латинский байт, пока не найдете пробел-тире-пробел
  4. переключиться на кодовую страницу 1251 (SetConsoleOutputCP(1251);)
  5. выписывайте каждый байт кириллицы, пока не выйдете из этой строки

... промыть и повторить.

Другой вариант — прочитать ваш ввод в кодовые точки Unicode, например. UTF-8 или UTF-16. Вам все равно придется интерпретировать каждую половину строк по-разному, а UTF-8, в частности, представляет собой кодировку переменной ширины, поэтому вы больше не можете доверять strlen(), чтобы дать вам фактическую длину строки для человеческого глаза, но при по крайней мере, ваша игровая площадка будет достаточно ровной, чтобы вы могли использовать некоторые ответы в Правильно печатайте символы utf8 в консоли Windows.

Сначала проверьте свою кодовую страницу на консоли, набрав chcp. Он покажет вашу активную кодовую страницу. Например. Active code page: 437

Введите chcp 65001 на консоли. (65001 – Юникод UTF-8) Теперь это совместимость с UTF-8.

В своем коде вы можете использовать wchar.h вместо fgetws и fputws.

Также следует отметить, что BYTE_COUNT не является числом широких символов. Это количество байтов в файле.

  • Например. пъ состоит из 2 широких символов, но также имеет 4 байта
  • это широкий символ, но он имеет 3 байта
#include <stdio.h>
#include <stdlib.h>
#include <wchar.h>

#define BYTE_COUNT 100

void readG(){
  FILE *fptr = fopen("file.txt", "r");

  printf("LERNWORTSCHATZ\n");
  printf("A1\n");
  printf("-------------------------------------------------\n");

  wchar_t str[BYTE_COUNT ];
  if (fgetws(str, BYTE_COUNT , fptr) != NULL) {
    fputws(str, stdout);
  }

  fclose(fptr);
}

В файле ОП нет широких символов.

AKX 25.07.2024 15:50

Другие вопросы по теме