Я делаю программу, которая читает файл, содержащий символы двух разных алфавитов (кириллицы и немецкого языка). Однако при выводе на терминал ö, ä и ü отображаются как ?.
До сих пор я пробовал:
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 - ...
Читаемый файл находится в Windows 1251, и я читаю его с помощью функции fgets().
Использовать инструмент или библиотеку для чтения файлов в любой кодировке и преобразования в UTF-8?
Файл, закодированный в Windows-1251, не может содержать глифы ö
, ä
и ü
, поскольку они не существуют в этой кодовой странице.
@Someprogrammerdude Это ускользает от того факта, что, например. strlen()
и друзья не будут поступать правильно в UTF-8 (особенно этот вопрос касается не-7-битных символов), так что это потребует еще большей сложности...
помните, что текстовый терминал старой школы может отображать только одну кодовую страницу за раз! И это то, для чего был и предназначен C.
Покажите пример файла, желательно с шестнадцатеричным дампом, и покажите, как можно отличить кириллические и немецкие символы одной и той же кодовой точки в разных кодировках? Или они на самом деле в Unicode? А также покажи свой код
Если у вас есть файл, в котором смешаны Windows-1251 и Windows-1252, то будет очень сложно определить, когда символ 0xFD равен ü
, а когда э
.
Какова ваша цель с файлами и текстами? Только чтобы сразу распечатать? Сделать обработку? Что-то другое? Пожалуйста, предоставьте нам как можно больше подробностей об исходной и основной проблеме, которую вам необходимо решить, чтобы нам было легче предложить вам подходящее решение.
Может быть, включить несколько байтов входного файла, чтобы мы могли увидеть, с чем на самом деле имеем дело? Возможно od -tx1 lernwortschatz.txt | head -n 8
, если вы используете Unix
Если вы не используете Unix (похоже, что вы используете Windows), вы можете изменить свой код с printf("%s", str
)` на for(int i=0; str[i]; i++) printf("%02x ", str[i]); printf("\n");
, чтобы отображать байты, а не содержимое строки. Вставьте часть результата в вопрос.
@phuciv Во-первых, добавлен код с примером. Во-вторых, я понятия не имею, что такое hexdump. Наконец, что вы подразумеваете под тем, как можно различать кириллические и немецкие символы одной и той же кодовой точки в разных кодировках?
Как поставить ergänzen - попълвам
в файл? «Я понятия не имею, что такое шестнадцатеричный дамп». Вам нужно погуглить, потому что вы не сможете выполнять свою работу, не зная своих основных инструментов.
@Someprogrammerdude добавил, что я собираюсь сделать с программой.
@PaulHankin добавил часть результата в байтах.
@phuclv добавил шестнадцатеричное представление
«Что я пытаюсь сделать с файлом: я хочу получить все его содержимое, а затем немедленно распечатать его без сохранения». Что ж, это должно работать нормально, если вы запустите свою программу и перенаправите ее в файл (например, myprogram.exe > foo.txt
). Однако ваш терминал не может интерпретировать байты, которые вы пытаетесь вывести, и поэтому вы получаете ?
.
Шестнадцатеричный дамп — это входной файл или результат копирования вывода с терминала?
В вашем шестнадцатеричном дампе нет ergänzen
. Покажите, пожалуйста, как выглядит файл с этим конкретным словом.
Итак, файл, который у вас есть, действительно имеет две разные кодировки однобайтовых символов в каждой строке. Это настоящий технический подвиг, который можно было бы осуществить с помощью любого обычного текстового редактора! :)
Давайте возьмем строку hören
68 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 с помощью чего-то вроде
... промыть и повторить.
Другой вариант — прочитать ваш ввод в кодовые точки 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);
}
В файле ОП нет широких символов.
В какой кодировке изначально файл? В этом вся разница. Как вы читаете файл?