# 00092. Работа с файлами ( форматированное чтение )

Работа с файлами ( форматированное чтение )|В этом уроке мы вернёмся к файлам, а точнее будет записывать и считывать форматированный текст.|wmysterio|wmysterio||||Привет всем пользователям нашего сайта! Пришло время для очередного урока! Этот способ отличается от работы с ini-файлами по всем статьям, но прочтя этот урок вы многое узнаете и, надеюсь, будете активно использовать в своих скриптах.

Прежде всего нам нужно настроить наш Санник, так как именно настройка ( а точнее не полная настройка ) не давала мне писать скрипты изучаемым сегодня методом. Как оказалось, всё дело в регистре букв, который компилятор использует по-умолчанию. Давайте откроем настройки и перейдём во вкладку "Форматирование" и обратим внимание на радио-кнопку "Регист букв":

![](https://github.com/wmysterio/scm-scripting-lessons/raw/resources/_pu/1/25511210.png)

Как видим, по-умолчанию у нас стоит ВЕРХНИЙ регист. Так, как режим открытия файла должен обязательно компилироваться в нижнем регистре, то меняет радиокнопку на **Как есть** ( **нижний** - тоже неплохой вариант, это уже наш вкус, но главное, что бы не стоял верхний регистр ). После этого нажимаем "ОК".

В отличии от ini-режима, в этом режиме нет никаких ключевых слов или секций. Как и Cleo - считывание происходит построчно. Существует 6 основных режимов открытия файла:

| Таблица 1       |                                                                                                                                                         |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Текстовый режим |                                                                                                                                                         |
| Режим           | Описание                                                                                                                                                |
| rt              | Режим открытия файла для чтения. Файл должен существовать.                                                                                              |
| wt              | Режим создания пустого файла для записи. Если файл с таким именем уже существует его содержимое стирается, и файл рассматривается как новый пустой файл |
| at              | Дописать в файл. Операция добавления данных в конец файла. Файл создается, если он не существует.                                                       |
| Бинарный режим  |                                                                                                                                                         |
| Режим           | Описание                                                                                                                                                |
| rb              | Режим открытия файла для чтения. Файл должен существовать.                                                                                              |
| wb              | Режим создания пустого файла для записи. Если файл с таким именем уже существует его содержимое стирается, и файл рассматривается как новый пустой файл |
| ab              | Дописать в файл. Операция добавления данных в конец файла. Файл создается, если он не существует.                                                       |

Рассмотрим опкод, для открытия файла:

```
0A9A: $hFILE = openfile "Cleo&#92;config.txt" mode "rb" // IF and SET
```

Здесь:\
$hFILE - хендл нашего файла\
"Cleo\config.txt" - путь у нашему файлу относительно директории GTA SA\
"rb" - режим открытия ( см. Таблица 1 )

Этот опокод является так же явлется условным, поэтому советую использовать его в блоке условий "if...jf". После того, так работа с файлом завершена, обязательно нужно закрыть его опкодом:

```
0A9B: closefile $hFILE
```

Обращаю Ваше внимание, что прежде чем открывать файл, нужно убедится в том, что он не открыт в каком-то другом приложении. Если в ini-файле можно было динамически менять значения, то этим методом так не получится.

Так, так тема нашего урока "форматированное считывание и запись", то мы обязаны рассмотреть какие идентификаторы есть, которые пишутся в опкод в зависимости от типа принимаемых значений:

| Таблица 2     |                                                                                                |
| ------------- | ---------------------------------------------------------------------------------------------- |
| Идентификатор | Описание                                                                                       |
| %d            | Идентификатор целого числа                                                                     |
| %f            | Идентификатор дробного числа ( по-умолчанию число и остаток с шестью символами после запятой ) |
| %s            | Идентификатор строки                                                                           |
| %c            | Идентификатор символа (номер символа)                                                          |

Здесь:\
% - специальный идентификатор, который указывает компилятору какой тип данных нужно записывать или читать. Они стоят в том порядке, который мы задали в файле, или планируем записать.\
d, f, c, s - собственно и есть типы

Давайте рассмотрим 2 опкода, которые позволяют осуществлять запись и чтение:

```
0AD9: write_formatted_text "Записать %d значений" in_file $hFILE 2
0ADA: 0@ = scan_file $hFile format "Считать %d" 1@
```

Опкод "0AD9" записует форматированный текст в файл "$hFILE". Будет записан текст, с таким содержимым: "Записать 2 значений". Как видите, в поле строки указываются только идентификаторы, а после опкода идёт перечисление всех значений. То есть, если мы хотим, что бы в файл записало стоку с содержимым "Бокал пива вмещает 0.5 литра" и хотим в скрипте задать объём пива, то наш скрипт приобретёт следующий вид:

```
0@ = 0.5
0AD9: write_formatted_text "Бокал пива вмещает %.1f литра" in_file $hFILE 0@
```

Таким образом мы сами задаём в переменную значение, а за тем размещаем в нужном нам месте текста. Но не нужно забывать, что не все числа нужно объявлять через идентификаторы. Можно обойтись и без них, если он не содержит нужной нам информации:

```
0@ = 1.25
0AD9: write_formatted_text "В 2 бокала пива вмещается %.2f литра" in_file $hFILE 0@
```

Соответственно в файл будет записан текст: "В 2 бокала пива вмещается 1.25 литра". То есть, если не задать параметр как идентификатор, то он воспринимается как символ. Давайте на этом же примере, напишем текст, с использованием 2-х параметров:

```
0@ = 1.25
1@ = 2
0AD9: write_formatted_text "В %d бокала пива вмещается %.2f литра" in_file $hFILE 1@ 0@
```

Думаю вы обратили внимание на "%.2f". Собственно для чего мы поставили это? Дело в том, что при такой установке в файл будет записываться только 2 символа после запятой. Если мы поставим "%.3f", то в файл будет записано 3 символа. Если этого не сделать ( напишем просто "%f" ), то число в файл запишет число "1.250000". Эти идентификаторы уже нужно ставить на своё усмотрение, но как минимум "%.1f". Иначе если дробное число не будет иметь остатка ( например, "1.0"), то во многих случаях в файл запишется просто "1", что вызывает проблемы при считывании. Данные выражения с точкой действуют только на дробные цисла!

Что касается записи строк, то здесь всё тоже самое. Указываем стринговую переменную и задаём ей значение. После этого записуем в файл:

```
1@v = "Привет"
0AD9: write_formatted_text "text %s" in_file $hFILE 1@v
```

Содержимое файла: "text Привет". Вот пример простейшего скрипта, который запишет в файл данные:

```
{$CLEO}
0000:
thread 'WRITE_TEXT'

:WRITE_TEXT
wait 0
if
file.Open(0@, "cleo&#92;text.txt", "wt")
jf @WRITE_TEXT
1@ = 2
2@v = "пива"
3@ = 1.25
0AD9: write_formatted_text "в %d бакалах %s вмещается %.2f литра" in_file 0@ 1@ 2@v 3@
file.Close(0@)
0A93: end_custom_thread
```

Результат:

![](https://github.com/wmysterio/scm-scripting-lessons/raw/resources/_pu/1/39936709.png)\
\
× \*\*ВАЖНО:\*\* Если файл имеет какие то записи, то при открытии его режимом "wt" или "wb" его содержимое теряется ( файл перезапишеться ).

Опкод "0ADA" осуществляет считывание файла и очень похож на запись, но здесь есть одно примечание, что содержимое строки файла должно точно совпадать с содержимым строки опкода! Если строка файла "Сейчас 16:21 часов", то содержимое строки опкода будет "Сейчас %d:%d часов" ( планируется считать 2 значения из файла ). Следовательно в конце опкода нужно дописать необходимые параметры:

```
0ADA: 0@ = scan_file $hFile format "Сейчас %d:%d часов" 1@ 2@
```

Таким образом в переменные "1@" и "2@" будут записаны числа, считанные из файла. В переменную "0@" запишется количество удачных преобразований. Если оно не совпадёт с параметрами файла, то выражение вернёт **false** и в переменные "1@" и "2@" нифига не запишется =) Это условный опкод, поэтому используйте его в условных конструкциях с "if". Старайтесь правильно указывать идентификаторы. Во многих случаях это и есть решение проблем.

Давайте напишем простой пример скрипта, который будет считывать цвета для автомобиля а за тем задавать их транспорту, в который мы сели:

```
{$CLEO}
0000:
thread 'READ_TEXT'

:READ_TEXT
wait 0
if
file.Open(0@, "cleo&#92;text.txt", "rt")
jf @READ_TEXT

:READ_TEXT_1
wait 0
if 
0ADA: 1@ = scan_file 0@ format "Color car: %d, %d" 2@ 3@
jf @READ_TEXT_1
file.Close(0@)

:READ_TEXT_2
wait 0
if
actor.Driving($PLAYER_ACTOR)
jf @READ_TEXT_2
03C0: 5@ = actor $PLAYER_ACTOR car
0229: set_car 5@ primary_color_to 2@ secondary_color_to 3@
0A93: end_custom_thread
```

В результате наш скрипт прекрасно перекрашивает наш транспорт на заданный в файле цвет.

![](https://github.com/wmysterio/scm-scripting-lessons/raw/resources/_pu/1/52341360.png)

В большинстве случаев файлы используют для хранения одинаковой по-структуре информации. Затем считывают весь файл и задают параметры для разных субъектов. К примеру, в файле в нас будет хранится информация о транспорте. Считая её мы создадим несколько машин в генераторе транспорта. Сначала рассмотрим опкод для определения количества параметров в строке:

```
014B: $CAR = init_parked_car_generator #RHINO color -1 -1 1 alarm 0 door_lock 0 0 10000 at 2435.302 -1671.848 12.8007 angle 90.0
```

Первый параметр будет у нас модель транспорта. Второй параметр пусть будет цена автомобиля. А следующие четыре: координаты спавна и угол поворота. Остальные пусть будут по-умолчанию. Что бы не терять ваше время, я создал файл с уже готовыми параметрами:

445 5000 2483.021 -1659.0421 12.94315 180.0\
446 7000 2477.022 -1659.0422 12.9432 180.0\
447 4000 2471.023 -1659.0423 12.9431 180.0\
448 8000 2465.024 -1659.0424 12.94321 180.0\
491 10000 2459.025 -1659.0425 12.94322 180.0\
471 13000 2453.026 -1659.0426 12.94311 180.0\
455 20000 2447.027 -1659.0427 12.94314 180.0\
438 8000 2441.028 -1659.0428 12.94321 180.0

Дальше уже напишем скрипт:

```
{$CLEO}
0000:
thread 'READ_TEXT'

var
$CARS: array 8 of car
end

:READ_TEXT
wait 0
if
file.Open(0@, "cleo&#92;text.txt", "rt")
jf @READ_TEXT
$INDEX = 0

while not file.EOF(0@)
 if 
 0ADA: 1@ = scan_file 0@ format "%d %d %f %f %f %f" 2@ 3@ 4@ 5@ 6@ 7@ // IF AND SET
 then
 014B: $CARS[$INDEX] = init_parked_car_generator 2@ color -1 -1 1 alarm 0 door_lock 0 0 3@ at 4@ 5@ 6@ angle 7@
 014C: set_parked_car_generator $CARS[$INDEX] cars_to_generate_to 101
 $INDEX += 1
 end
wait 0
end
file.Close(0@)
0A93: end_custom_thread
```

Так как в нас файл состоит из 8 строк, то и в массиве автомобилей тоже будет 8 машин. Дальше считывая файл мы подставляем считанные значения в опкод, тем самым создаём новый элемент генератора транспорта.

Думаю, Вы обратили внимание на опкод "0AD6: end\_of\_file 0@ reached" ( он же **file.EOF(0@)** ). Это проверка "Достигнут ли конец файла 0@?". Используя её мы узнаём когда прекращать чтение. В результате мы получили:

![](https://github.com/wmysterio/scm-scripting-lessons/raw/resources/_pu/1/59636185.png)

Если разобраться, то нет ничего сложного. Используйте файлы и в своих модах. В большинстве случаев, это существенно уменьшает время и код! =)|2411|1|0|52341360`png`500`388`400`310``\|59636185`png`500`300`400`240``\|25511210`png`460`422`400`366``|39936709`png`403`301`400\`298\`\`||rabota\_s\_fajlami\_formatirovannoe\_schityvanie|1504425699


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://lessons.sannybuilder.com/00100/00200/00092.-rabota-s-failami-formatirovannoe-chtenie.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
