Эта программа может использовать только библиотеки стандарта C.
Я пытаюсь прочитать CSV-файл в кодировке UTF-8 на языке C, используя fwscanf, но у меня возникают проблемы с процессом чтения. Файл содержит строки со строкой и значением с плавающей запятой, разделенными запятой. Вот минимальный пример, демонстрирующий проблему:
#include <stdio.h>
#include <wchar.h>
#include <locale.h>
#define MAX_STRING_LENGTH 31
int main() {
setlocale(LC_ALL, "en_US.UTF-8");
FILE *file = fopen("input.csv", "r, ccs=UTF-8");
if (file == NULL) {
fwprintf(stderr, L"Error opening file.\n");
return 1;
}
wchar_t string[MAX_STRING_LENGTH];
float frequency;
int row = 0;
while (!feof(file)) {
row++;
int result = fwscanf(file, L"%30[^,],%f,", string, &frequency);
if (result == 2) {
wprintf(L"Row %d: String = '%ls', Frequency = %.4f\n", row, string, frequency);
} else if (result == 1) {
wprintf(L"Row %d: String = '%ls', Frequency not read\n", row, string);
} else if (result == EOF) {
break;
} else {
wprintf(L"Error reading row %d\n", row);
wchar_t c;
// Skip the rest of the line
while ((c = fgetwc(file)) != L'\n' && c != WEOF);
}
}
fclose(file);
return 0;
}
Пример входного файла.csv:
hello,1.0000
world,0.5000
how,0.7500
are,0.2500
you,1.0000
?,0.5000
Ожидаемый результат:
Row 1: String = 'hello', Frequency = 1.0000
Row 2: String = 'world', Frequency = 0.5000
Row 3: String = 'how', Frequency = 0.7500
Row 4: String = 'are', Frequency = 0.2500
Row 5: String = 'you', Frequency = 1.0000
Row 6: String = '?', Frequency = 0.5000
Проблема, с которой я столкнулся, заключается в том, что fwscanf неправильно читает файл. Он либо считывает неверные значения, либо вообще не читает. Я пробовал использовать разные настройки локали и режимы открытия файлов, но проблема не устранена.
Прочтите это: Почему « while( !feof(file))» всегда неверно? Кроме того, смешивание [f][w]scanf() с [f]get[w]c() может затруднить кодирование. Просто читайте одну строку за раз, а затем анализируйте ее.
@chqrlie Здесь другая проблема с функцией: fwscaf генерирует неопределенные значения значений, поскольку они могут быть китайскими или просто значениями NULL.
@iPc: мы согласны.
Между , и ccs= не должно быть пробела.





Аргумент string не соответствует строке формата L"%30[^,],%f,". %[ ожидает указатель на массив char, который получит преобразование широких символов, считанных из потока, в их многобайтовое представление.
Вы хотите выполнить противоположную задачу: преобразовать входной поток байтов в кодировке UTF-8 в широкую строку, то есть: массив wchar_t. Вместо этого вам следует использовать fscanf("%30l[^,],%f,", string, &frequency).
Если вам не нужно использовать широкие строки в остальной части программы, преобразование из UTF-8 кажется ненужным, поскольку эта кодировка полностью совместима с синтаксисом CSV и всеми его вариантами.
Широко ориентированные функции ввода-вывода, такие как fwscanf(), не подходят для вашего случая использования. Они ожидают ввода в виде последовательности расширенных символов (где реализации имеют некоторую свободу определения того, что это означает), но ввод UTF-8 — это не так. Реализации могут различаться, но вполне вероятно, что ваши вызовы fwscanf пытаются прочитать файл, как если бы он был закодирован в UCS-2 (который для этой цели функционально эквивалентен UTF-16). Точно так же ваши вызовы wprintf, вероятно, не выдают выходные данные, закодированные в соответствии с конфигурацией вашего терминала.
В C есть понятие «многобайтовых символов», отдельное и отличное от «широких символов». Первые состоят из двух или более единиц char и наиболее естественно хранятся в массивах char, возможно, с вкраплениями однобайтовых символов. Последние состоят из одного wchar_t и наиболее естественно хранятся в массивах wchar_t, и в этом случае они не могут перемежаться однобайтовыми символами.
Ваш ввод UTF-8 лучше всего соответствует первому, а байт-ориентированные функции ввода-вывода лучше всего подходят для их чтения и записи. (И терминал или другое устройство отображения отвечает за интерпретацию кодовых последовательностей для представления соответствующих графических представлений.) В качестве примечания: в C есть литералы UTF-8, начиная с C11, и они соответствуют массивам char.
Итак, вы пытаетесь приложить ненужные дополнительные усилия. Используйте узкие функции ввода-вывода и обычные строки вместо широкоориентированных функций ввода-вывода и широких строк.
Кроме того,
подумайте о том, чтобы не использовать fscanf (и fwscanf), так как их обманчиво сложно использовать правильно. Среди возможных альтернатив — читать построчно с помощью fgets(), а затем анализировать каждую строку с помощью sscanf().
Кстати, в C есть литералы UTF-8, начиная с C11, и они соответствуют массивам char. Не совсем: константы и строки с префиксом u8 представляют собой экземпляры и массивы типа char8_t, которые могут отличаться от типа char. Однако строковые литералы в исходных файлах в кодировке UTF-8 поддерживаются всеми компиляторами C как массивы char, если они принимают 8-битный ввод.
@chqrlie, в C11, где они были представлены, и в C17, все еще текущей версии стандарта, литералы UTF-8 соответствуют массивам char. char8_t является новым в C2X (и будет типом элементов в литералах UTF-8 в этой версии), но, насколько мне известно, он еще не выпущен. В C2X char8_t имеет тот же тип, что и unsigned char, поэтому определенно отличается от char, даже если char не имеет знака.
Хорошая точка зрения. Это изменение делает эти литералы совершенно бесполезными, поскольку их больше нельзя передавать строковым функциям без приведения. Разрешить потенциальное подписание char было ужасной ошибкой, которая совершенно не соответствовала семантике strcmp() и getchar(). Слишком поздно для предварительного исправления с помощью нового типа char8_t.
while (!feof(file)): stackoverflow.com/questions/5431941/…