Ориентация главы
Первая из трёх глав о файлах — магистраль дескрипторов: xrtOpen (права комбинацией флагов) → WriteFull/ReadFull (семантика «прочитать/записать полностью») → xrtSeek/Tell (позиционирование по трём базисам) → xrtClose. Семейство с суффиксом Full забирает в библиотеку заготовку повторов на случай короткого чтения/записи (та же семантика, что у абстракции IO главы 41 — здесь её прямая форма на файловых дескрипторах); ReadAtFull читает по абсолютному смещению, не тревожа курсор — специализированная поза для подсматривания в заголовок файла. Средняя глава (глава 46) — отображения, блокировки и атомарная запись; следующая (глава 47) — каталоги и обход: три главы складываются в полную карту файловых операций.
45.1引入
Файловый API стандартной библиотеки C — слой исторических отложений: строки режимов fopen (кто помнит наизусть, чем "rb+" отличается от "w+b"), короткая семантика read/write (возвращаемое значение требует сверять в цикле самому), кроссплатформенные различия поведения (капкан конвертации переводов строк в текстовом режиме). Каждый C-программист писал цикл while для fread и гадал о моменте fflush — это не ваша вина, это вина возраста интерфейса; задача главы — положить этот исторический багаж на стол раз и навсегда.
Файловый слой XRT впитывает ямы этих пятидесяти лет: права — комбинация флагов (чтение/запись/создание/усечение/добавление — пять флагов побитовым ИЛИ; читается с одного взгляда, и больше никогда не нужно выяснять, кто есть кто из "rb+" и "w+b"), семантика Full встроена (прочитать или записать полностью либо отказ — заготовка циклов исчезает), различия платформ сглажены (едина бинарная семантика, без конвертации переводов строк, ошибки идут структурным отчётом главы 4, а не загадками errno). xrtOpen возвращает дескриптор xfile — стыковка с абстракцией IO главы 41 (строка адаптера превращает его в xreader/xwriter).
45.2概念
Жизненный цикл дескриптора (сперва выучите магистраль)
Примеры комбинаций флагов: XFILE_READ | XFILE_WRITE (открыть чтение-запись без создания), XFILE_WRITE | XFILE_CREATE | XFILE_TRUNCATE (перестроить и записать — стандартная поза временных файлов). Создание и усечение разделены явно — аварии вида «открыл существующий файл и случайно опустошил его» при комбинациях флагов неслучайно произойти не могут.
Семантика Full в файловой версии (конец заготовок)
Две конкретные формы семантики Full главы 41 на файлах: WriteFull при полном диске — на середине записи кончилось место, отказ целиком, а слот ошибки хранит структурные подробности (объект ошибки главы 4 несёт диагностику); ReadFull при раннем EOF — из 100 запрошенных байтов пришло 80 и затем EOF, отказ («файл усечён» — настоящий сигнал сбоя, а не «сойдёт и 80»).
ReadAtFull: боковое чтение, не тревожа курсор (только для подсматривания)
Потоково читая большой файл, вы хотите заглянуть в его заголовок (проверка magic number) — обычный Read сдвинет курсор, а написание «прочитал и Seek обратно» и витиевато, и ошибочно. ReadAtFull(句柄, 绝对偏移, 缓冲, 长度) (дескриптор, абсолютное смещение, буфер, длина) выполняет боковое чтение, не двигая курсор (образец проверяет Tell-ом, что курсор по-прежнему на 3) — стандартная поза подсматривающей проверки и запросов к индексу.
Стык с абстракцией IO (напрямую или через адаптер)
xrtReaderFromFile адаптирует xfile в единый Reader главы 41 (у записи то же семейство) — файл входит в канал «один код сериализации в трёх средах» (память/файл/сеть). Выбор между прямым подключением (API этой главы) и адаптером (интерфейс IO): разовая операция с одним файлом короче напрямую; когда одна логика обязана стыковаться со многими пунктами назначения (память/файл/сеть) — через адаптер; сам выбор продолжает суждение о «трёх хозяевах» главы 41.
45.3示例
Полная программа: магистраль дескрипторов насквозь
Из образца репозитория examples/file/basic/main.c — создание, запись, позиционирование, боковое чтение, закрытие, удаление:
#include <stdio.h> #include <xrt.h> */ /* 展示二进制文件最常用的一次创建、完整写入和完整读取。 */ int main(void) { static const char sText[] = "xrt file"; char arrText[sizeof(sText)]; uint64 iPosition; xfile File = xrtOpen("xrt-file-example.tmp", XFILE_READ | XFILE_WRITE | XFILE_CREATE | XFILE_TRUNCATE); if ( File == NULL ) { return 1; } if ( !xrtWriteFull(File, sText, sizeof(sText), NULL) || !xrtSeek(File, 3, XSEEK_START, NULL) || !xrtReadAtFull(File, 0, arrText, sizeof(arrText), NULL) || !xrtTell(File, &iPosition) || (iPosition != 3u) || !xrtSeek(File, 0, XSEEK_START, NULL) || !xrtReadFull(File, arrText, sizeof(arrText), NULL) ) { (void)xrtClose(File); return 1; } printf("%s\n", arrText); if ( !xrtClose(File) ) { return 1; } return xrtFileDelete("xrt-file-example.tmp") ? 0 : 1; }
Что только что произошло. (1) xrtOpen(路径, 写|创建|截断) (путь, запись|создание|усечение) — комбинация флагов объявляет «перестроить и записать», без строк режимов. (2) WriteFull пишет весь отрезок (8 байтов записаны ровно — встроенная гарантия отказа при короткой записи). (3) После Seek к началу ReadFull читает назад — цикл запись-чтение замкнулся. (4) Боковое чтение ReadAtFull проверяет: Tell курсора по-прежнему 3 — подглядывание не тревожит потоковую позицию, и это число есть доказательство. (5) xrtClose проверяет возвращаемое значение — сбои сброса буферов (последняя партия при полном диске) вскрываются здесь, а не крушением в продакшене. (6) xrtFileDelete удаляет временный файл по пути — дескриптор уже закрыт; удаление по пути — независимая операция; двухшаговая зачистка завершает дело.
Полная программа: три среды через IO-адаптер
Из examples/io/file/main.c — одна логика сериализации над памятью и файлом:
*/ #include <stdio.h> #include <xrt.h> int main(void) { static const char sPath[] = "xrt-io-example.bin"; unsigned char arrData[7]; xwriter* pWriter = xrtWriterOpen(sPath); /* 拥有式:Destroy 关文件 */ xreader* pReader; int iResult = 1; /* 写 7 字节 "xrt io\n" 后立即关闭(Destroy 含落盘检查)。 */ if ( (pWriter == NULL) || !xrtWriterWriteFull(pWriter, "xrt io\n", sizeof(arrData), NULL) || !xrtWriterDestroy(pWriter) ) { return 1; } /* 重新打开读取:ReadFull 保证读满 7 字节,否则失败。 */ pReader = xrtReaderOpen(sPath); if ( (pReader != NULL) && xrtReaderReadFull(pReader, arrData, sizeof(arrData), NULL) ) { fwrite(arrData, 1u, sizeof(arrData), stdout); iResult = 0; } xrtReaderDestroy(pReader); /* 范例产物随用随删,不污染工作目录。 */ (void)xrtFileDelete(sPath); return iResult; }
Что только что произошло. (1) Файловый адаптер оборачивает xfile в xwriter — код сериализации (написанный в главе 40) ложится на файл без единой правки. (2) Та же логика, что с адаптером памяти: продукты совпадают байт в байт — файловая сторона проверки обещания «трёх сред». (3) Возврат адаптера расслоён чисто: Destroy адаптера и Close файла каждый сам по себе — в согласии с дисциплиной заимствованной адаптации главы 41.
45.4契约
- Комбинации флагов: чтение/запись/создание/усечение/добавление — пять флагов побитовым ИЛИ; создание и усечение разделены явно — нечаянного опустошения не будет.
- Семантика Full: успех только при ровно N байтах; полный диск и ранний EOF отказывают целиком со структурной детализацией ошибки.
- Три базиса курсора: XSEEK_START/CURRENT/END (перечисление xseek главы 3); Tell сообщает текущую позицию в байтах.
- Боковое чтение:
ReadAtFullпо абсолютному смещению не двигает курсор — выделенный канал для подсматривающих проверок и запросов к индексу. - Проверка закрытия: возвращаемое Close обязательно к проверке — проблемы сброса буферов получают вердикт при закрытии.
- Явное удаление:
xrtFileDeleteне зависит от дескриптора — удаление закрытого файла по пути, вне жизненного цикла дескриптора.
От примеров к инженерии: три хозяина файловых дескрипторов
Конфигурация и малые файлы: чтение-запись целиком одним духом — Open/ReadAll или Open/WriteFull/Close, три шага; разбор JSON-файлов главы 43... главы 32 использует его именно так.Потоковая обработка: большие файлы циклом по кускам (ReadFull по 4 КБ) — полная форма упражнения поблочного копирования; в паре с построчным читателем главы 41 — построчное сканирование.Произвольный доступ: ReadAtFull прыгает по индексу (файлы индексов, позиционирование в журнале по смещению) — не тревожащее курсор боковое чтение здесь масштабируется. Три хозяина отвечают трём режимам файлового доступа (целокупно/последовательный поток/случайные прыжки) — сперва режим доступа, потом API; изоморфно выбору контейнеров (три вопроса главы 23).
Классификация ошибок: три лица файлового сбоя (сортировочный стол стратегий восстановления)
У сбоев файловых операций три лица, и обработка у каждого своя — перепутаешь сортировку, и повтор, и отчёт пойдут вкривь. Класс доступа (нет/нет прав/занято): доложить пользователю и попросить исправить — повторы бессмысленны; класс пространства (диск полон/квота превышена): доложить эксплуатации — после чистки или расширения повтор возможен; класс целостности (подсматривание в усечённый файл/провал проверки): доложить разработчику — обычно это сигнал параллельной правки или порчи данных. Категории ошибок главы 4 в файловом отображении: XERR_NOT_FOUND/PERMISSION → класс доступа, системные коды XERR_IO → класс пространства, бизнес-валидация → класс целостности. Смысл классификации — стратегия восстановления: слепой повтор для класса доступа — трата, для класса целостности — маскировка.
Одна привычка: трёхшаговое завершение файла
Трёхшаговое завершение записи файла достойно стать привычкой: написали — Flush-семантика со сверкой Close (вердикт долговечности) → переименовать в окончательное имя (атомарная видимость: читатель видит либо старый файл, либо новый; упрощённая версия атомарной записи главы 46) → на пути отказа удалить временный файл (мусора не остаётся). Проблема прямой записи под окончательным именем: крушение на середине оставляет усечённый файл, который читатель примет за полные данные. У трёхшагового завершения ровно два состояния продукта (нет/полный) — состояние «частично записано» уничтожено структурно. Привычка применима к сохранению конфигурации, переключению журналов, обновлению кеша — глава 46 даст её полную промышленную версию (временный файл + атомарное переименование + семантика fsync).
Типичные ошибки
Симптом: программа «успешно» завершилась, а хвост данных файла потерян — последний сброс буфера (полный диск, превышение квоты) молча проглочен.
Причина: Close — точка вердикта данных: у большинства реализаций сброс буферов происходит при закрытии; не проверить — расписаться за посылку, не заглянув внутрь.
xrtClose(File); /* возвращаемое значение отброшено - сброс не замечен */ return 0;
if ( !xrtClose(File) ) { ReportIoError(Path); /* в слоте ошибки главы 4 детали: полный диск/квота */ return 1; } return 0;
Симптом: при потоковом чтении большого файла «изредка продолжение с ошибочной позиции» — смещение обратного Seek посчитано неверно (или перепутано чтением параллельной ветви).
Причина: написание «подсмотреть — Seek-Read — Seek обратно» выставляет состояние курсора напоказ всей последующей логике — одна неверно посчитанная позиция расставляет всё не по местам.
xrtSeek(File, 0, XSEEK_START); /* прыгнуть в начало */ xrtReadFull(File, Magic, 4); /* прочитать magic */ xrtSeek(File, SavedPos, XSEEK_START); /* прыгнуть назад - откуда SavedPos? ошибка - катастрофа */
if ( !xrtReadAtFull(File, 0, Magic, 4) ) { /* боковое чтение по абсолютному смещению */ return false; } CheckMagic(Magic); /* курсор так и не двинулся - потоковая позиция безопасна сама собой */
Практические задания
Пятью комбинациями флагов (только чтение/чтение-запись/перестройка/добавление/создание без усечения) выполните открытие-операцию-закрытие; зафиксируйте поведение каждой комбинации для случаев «файл есть» и «файла нет» — десять клеток, сперва бумага, потом измерения.
Реализуйте copy(源, 目标) (источник, цель): цикл кусками по 4 КБ ReadFull/WriteFull (хвостовой блок — по фактически прочитанному объёму, обычным Read на финале); отпечатки Hash64 (глава 12) источника и цели сравните. Подсказка: последний блок короче 4 КБ — единственное место, где короткое чтение разрешено.
Реализуйте sniff(路径) (путь): ReadAtFull боком читает первые 16 байтов, распознаёт magic-числа gzip (1F 8B)/PNG/ZIP и выводит вердикт; затем, не мешая, потоково читает весь файл, считая строки (доказательство, что курсор не потревожен подсматриванием). Критерии приёмки: по тестовому файлу на каждый из трёх форматов вердикт верен; число строк совпадает с версией прямого чтения без нюханья.
Итоги
| Тема | Кратко |
|---|---|
| Открытие | флаги комбинируются побитово (чтение/запись/создание/усечение/добавление); без шифроблокнота строк режимов |
| Семейство Full | прочитать/записать полностью или отказ; полный диск и ранний EOF - отказ целиком |
| Позиционирование | Seek по трём базисам (START/CURRENT/END, xseek главы 3) + запрос Tell |
| Боковое чтение | ReadAtFull по абсолютному смещению, курсор не двинут (доказуемо Tell-ом) - только подсматривание/индекс |
| Закрытие | возвращаемое Close обязательно к проверке - вердикт долговечности; сбои вскрываются здесь, не крахом в продакшене |
| Три среды | Reader/WriterFromFile адаптируют в единый интерфейс главы 41 |
| Три хозяина | целокупно (конфигурация, малые файлы) / последовательный поток (большие файлы кусками) / случайные прыжки (смещения индекса) - режим доступа прежде API |
| Сортировка ошибок | класс доступа → пользователю / класс пространства → эксплуатации / класс целостности → разработчику - сортировка прежде повтора |
| Трёхшаговое завершение | Close выносит вердикт → переименование атомарно видно → удаление временного файла на пути отказа - состояния частичной записи уничтожены структурно |
📖 Справочники API в этой главе:file (132 записей)io (62 записей)
Полные сигнатуры, ограничения параметров, возвращаемые значения и ошибки