Русское сообщество по скриптингу

Работа с файлами и директориями

Статьи или фрагменты кода для новичков и уже опытных скриптеров по AMXX.

Модератор: Chuvi

Правила форума
1. Запрещено материться и оскорблять других участников форума.
2. Запрещен флуд, оффтоп, дабл постинг во всех разделах форума, кроме раздела "Болтовня".
3. Запрещено взламывать сайт/форум или наносить любой вред проекту.
4. Запрещено рекламировать другие ресурсы.
5. Запрещено создавать темы без информативного названия. Название темы должно отображать ее смысл.

В данном разделе форума разрешено создавать темы, касающие только обучающему материалу по AMX Mod X.

Работа с файлами и директориями

Сообщение DJ_WEST » 15 окт 2009, 12:32

Автор: DJ_WEST

Данная статья дает вам представление о том, как работать с файлами и директориями в AMX Mod X.

Работа с директориями
Код: Выделить всё
read_dir(const dirname[], pos, output[], len, &outlen) 

Данная функция предназначена для чтения содержания директории. Функция возвращает индекс следующего элемента в папке или 0, если достигнут конец директории и файлов больше нет.
Примечание: Данная функция неэффективна и вместо нее рекомендуется использовать open_dir.
Код: Выделить всё
dir_exists(const dir[]) 

Данная функция предназначена для проверки, существует ли указанная директория. Возвращает 1, если директория существует или 0 в противном случае.
Код: Выделить всё
rmdir(const path[]) 

Данная функия предназначена для удаления директории. Если в директории содержатся файлы, то ее удалить нельзя.
Код: Выделить всё
mkdir(const dirname[]) 

Данная функция предназначена для создания директории. Она возвращает 0 при успешном выполнении.
Код: Выделить всё
open_dir(dir[], firstfile[], length) 

Данная функция предназначена для открытия директории для чтения. Возвращает указатель на директорию или 0 в противном случае. Указывая директорию в параметре dir, считается, что вы находитесь в директории мода (к примеру, cstrike). Но можно выйти из директории и на уровни выше (используя ../), к примеру:
Код: Выделить всё

new i_Dir
, s_File[128]
i_Dir = open_dir("../", s_File, charsmax(s_File))
 

Мы уже окажемся в главной директории сервера, там где находится hlds.exe. Но существует лимит, вы не сможете выйти дальше логического раздела, где находится сервера, на вашем жеском диске.
Код: Выделить всё
next_file(dirh, buffer[], length) 

Данная функция читает следующий файл по указанному указателю на директорию. Возвращает 1 при успешном выполнении или 0 в противном случае.
Код: Выделить всё
close_dir(dirh) 

Закрывает директорию по указанному указателю. Вы должны использовать функцию на верном указателе, полученном в open_dir.

Например, напишем плагин, который будет считывать все карты из директории ../cstrike/maps и выводить их в консоль сервера:
Код: Выделить всё
#include <amxmodx>

#define PLUGIN "Read Maps"
#define VERSION "1.0"
#define AUTHOR "DJ_WEST"

public plugin_init() 
{
    register_plugin(PLUGIN, VERSION, AUTHOR)
    
    new i_Dir
, s_File[128]
    
    
// Сохраняем указатель на директорию cstrike/maps в i_Dir
    i_Dir = open_dir("maps", s_File, charsmax(s_File))
    
    
// Проверяем существует ли директория
    if (i_Dir)
    {
        server_print("*** MAPS ***")
    
        
// Пока next_file возвращает значения != 0 выводим карту в консоль
        while (next_file(i_Dir, s_File, charsmax(s_File)))
        {
            // Проверяем если первый символ файла равен '.', то не выводим
            // Функция считывает '.' и '..', которые не являются файлами, 
            // а просто говорят о том, что есть уровень каталога выше
            if (s_File[0] == '.')
                continue
                
            
// Проверяем, чтобы файл содержал .bsp, что говорит нам о том, что это карта
            // Потому что в директории могут быть и другие файлы или директории
            if (containi(s_File, ".bsp") != -1)
                server_print(s_File)
        }
    }
}
 


Работа с файлами (старый способ)
Код: Выделить всё
file_exists(const file[]) 

Данная функция предназначена для проверки, существует ли указанный файл. Возвращает 1, если существует или 0 в противном случае.
Код: Выделить всё
delete_file(const file[]) 

Данная функция предназначена для удаления указанного файла. Возвращает 1 при успешном выполнении или 0 в противном случае.
Код: Выделить всё
read_file(const file[], line, text[], len, &txtlen) 

Данная функция предназначена для чтения строки в файле. Возвращает индекс на следующую строку или 0, если достигнут конец файла. Строки в файлах начинаются с 0 номера. Указанная строка (параметр line), считывается в text с указанным длиной len. Количество прочтенных символов хранится в txtLen.
Код: Выделить всё
write_file(const file[], const text[], line = -1) 

Данная функция предназначена для запись строки в файл по указанному месту. Возвращает 0 при неуспешном выполнении. Если line указать -1, то запись строки произойдет в конец файла. Функция автоматически создает файл, если он не существует.
Код: Выделить всё
rename_file(const oldname[], const newname[], relative=0) 

Данная функция предназначена для переименования файла. Возвращает 1 при успешном выполнении или 0 в противном случае. Если relative не 0, то будет считаться, что текущая директория - это директория мода. В противном случае, текущая директория не определена (но является директорией HLDS сервера).
Код: Выделить всё
file_size(const file[], flag=0) 

Данная функция возвращает размер файла. Если flag:
0 - размер файла в байтах
1 - количество строк в файле
2 - возвращает 1, если файл заканчивается переводом строки
Если файл не существует, возвращает -1.
Код: Выделить всё
filesize(const filename[], any:...) 

Данная функция возвращает размер файла. filename можно формировать по стилю format, то есть указывать переменные в any.

Работа с файлами (новый способ, более функциональный)
Код: Выделить всё
fopen(const filename[], const mode[]) 

Данная функция предназначена для открытия файла в одном из указанных режимов. Возвращает указатель на файл или 0 в противном случае.
Список режимов:
Первый символ:
"a" - append
"r" - read
"w" - write
Второй символ:
"t" - text
"b" - binary
Вы также можете использовать + одновременно для чтения и записи.
Код: Выделить всё
fclose(file) 

Данная функция предназначена для закрытия файла по указателю.
Код: Выделить всё
feof(file) 

Данная функция проверяет достигнут ли конец файла. Возвращает 1, если достигнут или 0 в противном случае.
Код: Выделить всё
fread(file, &data, mode) 

Данная функция предназначена для чтения элемента из файла.
Возможные режимы mode:
BLOCK_INT - для записи любого числа или символов
BLOCK_SHORT - для записи символов и значений в пределах -32767 -> 32767
BLOCK_CHAR - для записи символов и значений в пределах -127 -> 127
BLOCK_BYTE - похоже на BLOCK_CHAR
Возвращает 1 при успешном выполнении или 0 в противном случае.
Код: Выделить всё
fread_blocks(file, data[], blocks, mode) 

Данная функция предназначена для чтения блока данных из файла. Чтение блока данных и сохранения в массив данных data. blocks - размер массива.
Возможные режимы mode:
BLOCK_INT - для записи любого числа или символов
BLOCK_SHORT - для записи символов и значений в пределах -32767 -> 32767
BLOCK_CHAR - для записи символов и значений в пределах -127 -> 127
BLOCK_BYTE - похоже на BLOCK_CHAR
Возвращает число успешно прочитанных блоков.
Код: Выделить всё
fread_raw(file, stream[], blocksize, blocks) 

Данная функция предназначена для чтения блока данных из файла. Что такое RAW, можно прочитать здесь:
http://ru.wikipedia.org/wiki/RAW_(формат_данных)

Прочитать количество блоков blocknum размером blocksize из файла в массив stream.
Возвращает число успешно прочитанных блоков.
Код: Выделить всё
fwrite(file, data, mode) 

Данная функция предназначена для записи элемента в файл.
Возможные режимы mode:
BLOCK_INT - для записи любого числа или символов
BLOCK_SHORT - для записи символов и значений в пределах -32767 -> 32767
BLOCK_CHAR - для записи символов и значений в пределах -127 -> 127
BLOCK_BYTE - похоже на BLOCK_CHAR
Код: Выделить всё
fwrite_blocks(file, const data[], blocks, mode) 

Данная функция предназначена для записи блока данных в файл. Запись блока данных в file из массив данных data. blocks - размер массива.
Возможные режимы mode:
BLOCK_INT - для записи любого числа или символов
BLOCK_SHORT - для записи символов и значений в пределах -32767 -> 32767
BLOCK_CHAR - для записи символов и значений в пределах -127 -> 127
BLOCK_BYTE - похоже на BLOCK_CHAR
Возвращает число успешно записанных блоков.
Код: Выделить всё
fwrite_raw(file, const stream[], blocksize, mode) 

Данная функция предназначена для записи блока данных в файл. Что такое RAW, можно прочитать здесь:
http://ru.wikipedia.org/wiki/RAW_(формат_данных)

Записать в файл данные из масиива stream размером blocksize в режиме mode.
Возможные режимы mode:
BLOCK_INT - для записи любого числа или символов
BLOCK_SHORT - для записи символов и значений в пределах -32767 -> 32767
BLOCK_CHAR - для записи символов и значений в пределах -127 -> 127
BLOCK_BYTE - похоже на BLOCK_CHAR
Возвращает число успешно записанных блоков.
Код: Выделить всё
fprintf(file, const fmt[], any:...) 

Данная функция предназначена для записи формированный строки в файл.
Код: Выделить всё
fseek(file, position, start) 

Данная функция предназначена для установки текущей позиции курсора в файле. position - это смещение от start. start может быть:
SEEK_SET - начало файла
SEEK_CUR - текущая позиция курсора
SEEK_END - конец файла
Код: Выделить всё
ftell(file) 

Данная функция предназначена для возвращения текущей позиции курсора в файле.
Код: Выделить всё
fgets(file, buffer[], maxlength) 

Данная функция предназначена для чтения строки из файла. Считывает также символы переноса каретки и новой строки.
Код: Выделить всё
fgetc(file) 

Данная функция предназначена для чтения одного символа (1 байта) из файла по указанному указателю на него.
Код: Выделить всё
fgetf(file) 

Данная функция предназначена для чтения float значения (4 байта, 8 на AMD64) из файла по указанному указателю на него. Данная функция удалена в AMXX 1.1.
Код: Выделить всё
fgeti(file) 

Данная функция предназначена для чтения integer значения (4 байта) из файла по указанному указателю на него. Данная функция удалена в AMXX 1.1.
Код: Выделить всё
fgetl(file) 

Данная функция предназначена для чтения long значения (4 байта) из файла по указанному указателю на него. Данная функция удалена в AMXX 1.1.
Код: Выделить всё
fputs(file, const text[]) 

Данная функция предназначена для записи строки в файл по текущей позиции курсора файла. Возращает число усппешно записанных символов.
Код: Выделить всё
fputc(file) 

Данная функция предназначена для записи одного символа (1 байта) в файл по указанному указателю на него.
Код: Выделить всё
fputf(file) 

Данная функция предназначена для записи float значения (4 байта, 8 на AMD64) в файл по указанному указателю на него. Данная функция удалена в AMXX 1.1.
Код: Выделить всё
fputi(file) 

Данная функция предназначена для записи integer значения (4 байта) в файл по указанному указателю на него. Данная функция удалена в AMXX 1.1.
Код: Выделить всё
fputl(file) 

Данная функция предназначена для записи long значения (4 байта) в файл по указанному указателю на него. Данная функция удалена в AMXX 1.1.
Код: Выделить всё
fungetc(file, data) 

Данная функция предназначена для сохранения символа (1 байта) в data.

Например, плагин, который использует RAW чтение и запись, сохранение steam id, ника, фрагов и убийств игроков, которые на сервере в stats.dat, а также чтение их после смены карты (stats.dat будет хранить статистику игроков, играющих на сервере в данный момент за одну карту):
Код: Выделить всё
#include <amxmodx>

#define PLUGIN "Player Database"
#define VERSION "1.0"
#define AUTHOR "DJ_WEST & schnitzelmaker"

#define MAX_PLAYERS 32

new g_FileName[256]

enum PLAYER_DATABASE
{
    STEAMID[32],
    NAME[32],
    FRAGS,
    DEATHS,
    
    PLAYER_DATABASE_END
}

new g_PlayerData[MAX_PLAYERS+1][PLAYER_DATABASE]
new bool:g_PlayerConnected[MAX_PLAYERS+1]

public plugin_init() 
{
    register_plugin(PLUGIN, VERSION, AUTHOR)
    
    
// Получаем путь к data директории AMXX
    get_datadir(g_FileName, charsmax(g_FileName))
    // Формируем путь к файлу stats.dat
    format(g_FileName, charsmax(g_FileName),"%s/stats.dat", g_FileName)
    
    
// Через 1 секунду выполнить Read_Stats
    set_task(1.0, "Read_Stats")
    
    
// За 2 секунды перед окончанием времени карты выполнить Write_Stats
    set_task(2.0, "Write_Stats", 0, "", 0, "d")
}

stock get_datadir(s_Name[], i_Len)
{
    return get_localinfo("amxx_datadir", s_Name, i_Len)
}

public client_connect(id)
{
    g_PlayerConnected[id] = true
}

public client_disconnect(id)
{
    g_PlayerConnected[id] = false
}

public Write_Stats()
{
    new i_File, i_Frags, i_Deaths
    
    
// Открыть файл
    i_File = fopen(g_FileName, "a+")
    
    
// Если файл существует
    if (i_File)
    {
        for (new i = 1; i <= MAX_PLAYERS; i++)
        {
            if (g_PlayerConnected[i])
            {
                i_Frags = get_user_frags(i)
                i_Deaths = get_user_deaths(i)
                get_user_authid(i, g_PlayerData[i][STEAMID], charsmax(g_PlayerData))
                get_user_name(i, g_PlayerData[i][NAME], 31)
                num_to_str(i_Frags, g_PlayerData[i][FRAGS], charsmax(g_PlayerData))
                num_to_str(i_Deaths, g_PlayerData[i][DEATHS], charsmax(g_PlayerData))
                
                
// Записываем
                fwrite_raw(i_File, g_PlayerData[i][PLAYER_DATABASE:0], sizeof(g_PlayerData[]), BLOCK_INT)
            }
        }
         /*
          g_PlayerData[id] - данных игрока c id = i
          [PLAYER_DATABASE:0] - начала данных
          Для записи только Steam ID необходимо использовать: g_PlayerData[id][PLAYER_DATABASE:STEAMID]
          sizeof(g_PlayerData[]) - длина данных, которые необходимо записать
        */
    
    
}
    
    
// Закрыть файл
    fclose(i_File)  
}

public Read_Stats()
{
    new s_PlayerData[PLAYER_DATABASE], i_File
        
    
// Открыть файл
    i_File = fopen(g_FileName, "a+")
    
    
// Если файл существует
    if (i_File)
        while (fread_raw(i_File, s_PlayerData[PLAYER_DATABASE:0], sizeof(g_PlayerData[]), BLOCK_INT))
        {
            server_print("==============================")
            server_print("STEAM ID: %s", s_PlayerData[STEAMID])
            server_print("NAME: %s", s_PlayerData[NAME])
            server_print("FRAGS: %s", s_PlayerData[FRAGS])
            server_print("DEATHS: %s", s_PlayerData[DEATHS])
            server_print("==============================")
        }

    
     
// Удаляем stats.dat
    if (file_exists(g_FileName))
        delete_file(g_FileName)
        
    
// Закрыть файл
    fclose(i_File)
}
 

Результат из консоли сервера (для примера):
Код: Выделить всё

==============================
STEAM ID: BOT
NAME: [P*D]Player_1 (100)
FRAGS: 00
DEATHS: 0
==============================
==============================
STEAM ID: BOT
NAME: [POD]Player_3 (100)
FRAGS: 00
DEATHS: 0
==============================
==============================
STEAM ID: BOT
NAME: [P0D]Player_5 (100)
FRAGS: 00
DEATHS: 0
==============================
==============================
STEAM ID: BOT
NAME: [POD]Player_2 (100)
FRAGS: 00
DEATHS: 0
==============================
==============================
STEAM ID: BOT
NAME: [P*D]Player_10 (96)
FRAGS: 00
DEATHS: 0
==============================
==============================
STEAM ID: BOT
NAME: [POD]Player_9 (100)
FRAGS: 00
DEATHS: 0
==============================
==============================
STEAM ID: BOT
NAME: [P0D]Player_8 (97)
FRAGS: 00
DEATHS: 0
==============================
Не пишите мне в ЛС: если вам нужна помощь на бесплатной основе. Любые вопросы на форум.
Аватара пользователя
DJ_WEST
Администратор
 
Сообщения: 3641
Зарегистрирован: 22 авг 2009, 00:38
Благодарил (а): 48 раз.
Поблагодарили: 2209 раз.
Опыт программирования: Больше трех лет
Языки программирования: Counter-Strike 1.6
Counter-Strike: Source
Left 4 Dead
Left 4 Dead 2

Re: Работа с файлами и директориями

Сообщение qpAHToMAS » 06 мар 2013, 11:55

Не могу понять в чем проблема. Пытаюсь делать записть INT'а в конец файла:
[pawn]
  1.                 new origin_irs[3]

  2.                 get_user_origin(players_i[i], origin_irs)

  3.                

  4.                 new file_id = fopen(cfgdir_s_map_file_name, "wb")

  5.                 if (file_id) {

  6.                         fseek(file_id, 0, SEEK_END)

  7.                         fwrite(file_id, origin_irs[0], BLOCK_INT)

  8.                         //fwrite_raw(file_id, origin_irs[0], 4, BLOCK_INT)

  9.                         fclose(file_id)

  10.                 }
[/pawn]
Но в итоге, сколько бы записей не было (этот кусок кода из цикла), файл будет весить 4 байта :(. Будто fseek() не работает.
Аватара пользователя
qpAHToMAS
 
Сообщения: 707
Зарегистрирован: 02 ноя 2009, 18:45
Благодарил (а): 79 раз.
Поблагодарили: 204 раз.
Языки программирования: CStrike

Re: Работа с файлами и директориями

Сообщение Asmodai » 06 мар 2013, 12:05

"wb" на "ab" поменяй. fseek не нужен.
Аватара пользователя
Asmodai
Адмирал
 
Сообщения: 466
Зарегистрирован: 24 фев 2011, 20:48
Благодарил (а): 0 раз.
Поблагодарили: 393 раз.
Языки программирования: Counter-Strike 1.6

Re: Работа с файлами и директориями

Сообщение qpAHToMAS » 06 мар 2013, 14:08

"wb" на "ab" поменяй. fseek не нужен.

Тоже самое.
Аватара пользователя
qpAHToMAS
 
Сообщения: 707
Зарегистрирован: 02 ноя 2009, 18:45
Благодарил (а): 79 раз.
Поблагодарили: 204 раз.
Языки программирования: CStrike

Пред.

Вернуться в Статьи / фрагменты кода

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 6