Работа с файлами.

Частые вопросы возникающие при работе с ботами. Загляните сюда перед созданием новой темы.

Модератор: Модераторы

Работа с файлами.

Сообщение tvrsh » 11 окт 2008 12:40

Возникает множество вопросов о чтении файлов, замены строк в файле, удалении строк и т.д. Вот небольшой мануал по работе с файлами и строками.

1. Чтение всех строк из файла в список.
Это одна из самых основных операций при работе с файлами, которая используется в очень многих случаях, ознакомьтесь внимательно с ней!

TCL: [ Скачать ] [ Скрыть ]
# Имя файла который будем читать.
set fname "yourfile.txt"

# Открываем доступ к файлу для чтения (мы не ловим ошибки, однако
# можно использовать catch {} в случае если файл отсутствует.
set fp [open $fname "r"]

# Тут мы считываем все данные из файла.
set data [read -nonewline $fp]

# Закрываем файл.
close $fp

# Теперь разбиваем данные на отдельные строки.
set lines [split $data "\n"]

Теперь у нас в переменной lines все строки из файла, далее смотрим другие операции с файлами.

2. Чтение случайной строки из файла (маленького).
существует множество причин для использования этого, скрипт цитат, случайных топиков. В данном примере расмотрим выбор одной строки, но вы можете легко выбирать и несколько строк из разных частей файла.

TCL: [ Скачать ] [ Скрыть ]
# Используйте код из примера (1.) для чтения всех строк из файла.
# Мы продолжаем сразу после: set lines [split $data "\n"]

# получаем количество строк в файле.
set numlines [llength $lines]

# Выбираем номер случайной строки используя функцию rand.
set num [rand $numlines]

# Забираем эту строку!
set randline [lindex $lines $num]

Теперь в переменной $randline у нас находится случайная строка. Если вы хотите получить несколько строк запустите последнюю часть года в цикле.

3. Удаление строки из файла.
Вам може понадобиться удалить строку из файла. Запомните что во многих языках, включая TCL номера элементов начинаются с 0. То-есть, чтобы удалить первую строку вам надо удалить строку с индексом 0, чтобы удалить вторую, с индексом 1. И так далее.

TCL: [ Скачать ] [ Скрыть ]
# Используйте код из примера (1.) для чтения всех строк из файла.
# Мы продолжаем сразу после: set lines [split $data "\n"]

# Удаляем первую строку.
set line_to_delete 0

# Если хотите удалить последнюю строку то используйте:
# set line_to_delete [expr {[llength $lines] - 1}]

# Теперь удаляем строку из списка в памяти.
proc K {x y} { set x }
set lines [lreplace [K $lines [set lines {}]] $line_to_delete $line_to_delete]

# И наконец, перезаписываем файл с новыми данными.
set fp [open $fname "w"]
puts $fp [join $lines "\n"]
close $fp
 

Как вы видите, для того, чтобы удалить строку из фала, сначала мы должны считать файл в память, удалить строку в памяти и затем переписать данные из памяти в файл. Если вы часто используете удаление и добавление строк лучше использовать базы данных. Также если файл с которы вы работаете очень большой (несколько мегабайт) эти операции будут занимать определенное время.

4. Добавление строки в файл.
Обычно строки добавляют в начало или в конец файла. В примере ниже рассмотрим последний вариант.

TCL: [ Скачать ] [ Скрыть ]
# Используйте код из примера (1.) для чтения всех строк из файла.
# Мы продолжаем сразу после: set lines [split $data "\n"]

# Для добавления в начало используйте индекс строки 0, так как счет начинается с 0.
# Если хотите добавить строку в конец файла смотрите следующий пример.
set line_to_insert "это новая строка"
set where_to_insert end

# Добавляем эту строку с список находящийся в памяти.
set lines [linsert $lines $where_to_insert $line_to_insert]

# И переписываем файл с новыми данными как в примере (3.).
set fp [open $fp "w"]
puts $fp [join $lines "\n"]
close $fp


5. Добавление строки в конец файла.
Для добавления стоки в конец файла не обязательно считывать и перезаписывать файл полностью. Мы будем открывать файл в режиме "append mode".

TCL: [ Скачать ] [ Скрыть ]
# Новая строка.
set line_to_add "это новая строка"

# Файл куда мы будем ее добавлять.
set fname "yourfile.txt"

# Открываем файл в append mode.
set fp [open $fname "a"]

# Добавляем строку.
puts $fp $line_to_add

# Все готово!
close $fp


Вольный перевод поста http://forum.egghelp.org/viewtopic.php?t=6885
Have fun.
-
Получить помощь можно на каналах #egghelp в сети IrcNet.ru и #eggdrop в сети RusNet(Ключ канала eggdrop).
Перед созданием новой темы внимательно читайте Правила оформления топиков.
Аватара пользователя
tvrsh
 
Сообщения: 1230
Зарегистрирован: 19 авг 2008 16:55
Откуда: Russian Federation, Podolsk
Благодарил (а): 6 раз.
Поблагодарили: 130 раз.
Версия бота: Eggdrop 1.6.20+suzi

Re: Работа с файлами.

Сообщение Bart » 05 апр 2015 21:16

Если бы кто-то привёл пример, как взять не строку из файла, а искомое слово со всей строкой, было бы замечательно.
Например, имеется test.data c
nick t3gwtg35h e4gegwww4
nick2 g33g3g4h hwsh4wh5
nick3 erygh34y4 5y4h45j

Два вопроса:
1. Как взять данные, например, из второй строки и вывести их в канал по команде: !команда nick2?
2. И как удалить эту строку?
Похожие процедуры есть в calc, qoute, но что-то я не понял их там. :?
Аватара пользователя
Bart
 
Сообщения: 24
Зарегистрирован: 05 фев 2014 01:35
Благодарил (а): 17 раз.
Поблагодарили: 0 раз.
Версия бота: Eggdrop 1.6.21+suzi

Re: Работа с файлами.

Сообщение Vertigo » 06 апр 2015 20:18

Bart писал(а):1. Как взять данные, например, из второй строки и вывести их в канал по команде: !команда nick2?
2. И как удалить эту строку?

Сиди, разбирайся. Максимально добавил комментов в код :)
TCL: [ Скачать ] [ Скрыть ]
namespace eval ::testdb {;# объявляем пространство имён

variable db "test.data";# название и путь к файлу

if {![file readable $db]} {;# создаем пустой файл, если его нет
        set f [open $db w+]; close $f
}

bind pub - !read "[namespace current]::pub read";# биндим команду на чтение
bind pub - !write "[namespace current]::pub write";# и на запись (в ту же процедуру)

# если биндить несколько команд на одну процедуру (для экономии кода), то первым аргументом в процедуре будет указанный в конце бинда
# в данном случае, этот аргумент - $mode

proc pub {mode nick uhost hand chan text} {
        if {[string is space $text]} {;# если ввели команду без параметров, выдаем справку по использованию для соответствующего режима
                if {$mode eq "read"} {
                        putserv "PRIVMSG $chan :$nick: Используй: $::lastbind <ник>"
                } elseif {$mode eq "write"} {
                        putserv "PRIVMSG $chan :$nick: Используй: $::lastbind <ник> = данные1 данные2 (для добавления) | $::lastbind <nick> = (для удаления)"
                }
                return
        }
       
        if {$mode eq "read"} {;# если режим работы "чтение"
                set nick_ [string trim [lindex [split $text] 0]];# берем из текста первый аргумент в качестве ника и убираем возможные пробелы по краям
                set data [readfile];# читаем данные из файла в переменную
                if {[string is space $data]} {;# в базе ничего нет
                        putserv "PRIVMSG $chan :$nick: В базе пусто!"
                        return                 
                }
                set list [lsearch -all -inline -nocase -index 0 $data $nick_];# ищем в файле по нулевому (первому) индексу нужный ник
       
                if {![llength $list]} {;# не нашли
                        putserv "PRIVMSG $chan :$nick: Ничего не найдено."
                        return
                }
                putserv "PRIVMSG $chan :$nick: Найдено: [join $list "\; "]";# выводим как есть, всё, что нашли (дальше можно с найденными данными работать как захочется)
                return
        } elseif {$mode eq "write"} {;# если режим работы "запись"
                if {[string first "=" $text] == "-1"} {;# разделитель в тексте ника и данных знак "=", если его нет, значит ввели непонятную команду, говорим об этом
                        putserv "PRIVMSG $chan :$nick: Неправильный синтаксис. Используй: $::lastbind <ник> = данные1 данные2 (для добавления) | $::lastbind <nick> = (для удаления)"
                        return
                }
               
                lassign [split $text =] nick_ data_;# делим текст по знаку "=" на ник и данные
                set nick_ [string trim $nick_];# убираем "левые" пробелы по краям (если они есть)
                set data_ [string trim $data_]
                if {[string is space $data_]} {;# указан только ник, значит удаляем его из базы
                        set data [readfile];# читаем данные из файла в переменную
                        if {[set id [lsearch -nocase -index 0 $data $nick_]] == "-1"} {;# запрашиваемый ник не найден, заодно сохраним $id для последующего удобства
                                putserv "PRIVMSG $chan :$nick: Запись \"$nick_\" не найдена."
                                return
                        } else {
                                variable db
                                set data [lreplace $data $id $id];# удаляем из списка элемент с индексом $id
                                writefile $data;# записываем изменения
                                putserv "PRIVMSG $chan :$nick: Запись удалена.";# говорим об этом
                                return
                        }
                } else {;# иначе, добавляем в базу новую (или изменяем старую) с указанным ником
                        if {[llength [split $data_]] > 2} {;# ввели больше 2х параметров как значение, говорим об этом
                                putserv "PRIVMSG $chan :$nick: Неправильный синтаксис. Используй: $::lastbind <ник> = данные1 данные2 (для добавления)"
                                return                 
                        }
                        set data [readfile];# читаем данные из файла в переменную
                        set id [lsearch -nocase -index 0 $data $nick_]
                        if {$id == "-1"} {;# такой записи нет, создаём новую
                                set data [linsert $data end [list $nick_ $data_]];# добавляем в конец списка новую запись
                                writefile $data;# записываем изменения
                                putserv "PRIVMSG $chan :$nick: Запись добавлена.";# говорим об этом
                                return
                        } else {;# изменяем имеющуюся
                                set data [lreplace $data $id $id [list $nick_ $data_]]
                                writefile $data;# записываем изменения
                                putserv "PRIVMSG $chan :$nick: Запись изменена.";# говорим об этом
                                return
                        }
                }
               
        }
}

proc readfile {} {
        variable db
        set f [open $db r];# открываем файл на чтение
        set data [lrange [split [read $f] \n] 0 end-1];# читаем данные из файла
        close $f;# закрываем файл
        return $data;# возвращаем прочитанные данные
}

proc writefile {data} {
        variable db
        set f [open $db w+];# открываем файл на запись
        if {![llength $data]} {;# дабы не плодить пустые строки в файле, просто закроем его, если в данных пусто (ключ w предварительно очищает файл)
                close $f
        } else {
                puts $f [join $data \n];# записываем в файл, разбивая по символу новой строки
                flush $f;# принудительно сбрасываем данные в файл
                close $f;# закрываем файл
        }
}

putlog "[file tail [info script]] loaded";# пишем в лог, что скрипт загружен
}
Аватара пользователя
Vertigo
 
Сообщения: 107
Зарегистрирован: 20 авг 2008 23:49
Откуда: Москва
Благодарил (а): 0 раз.
Поблагодарили: 37 раз.
Версия бота: Eggdrop 1.8

Re: Работа с файлами.

Сообщение Bart » 06 апр 2015 21:24

Остановился на моменте, где полученные данные я хотел разделить, то есть то, что в скобках {данные1 данные2}.
В ирц копировал:
curl -s --data token=данные1 --data user=данные2 --data-urlencode и тд.
Спасибо за подробный ответ.
Аватара пользователя
Bart
 
Сообщения: 24
Зарегистрирован: 05 фев 2014 01:35
Благодарил (а): 17 раз.
Поблагодарили: 0 раз.
Версия бота: Eggdrop 1.6.21+suzi

Re: Работа с файлами.

Сообщение Vertigo » 06 апр 2015 22:05

Замени
putserv "PRIVMSG $chan :$nick: Найдено: [join $list "\; "]"

на
TCL: [ Скачать ] [ Скрыть ]
                foreach line $list {
                        set data_ [lindex $line 1]
                        lassign [split $data_] data1 data2
                        putserv "PRIVMSG $chan :curl -s --data token=$data1 --data user=$data2 --data-urlencode"
                }

У меня выводит в духе:
|06.04.2015 / 21:04:47| <Vertigo> !read *
|06.04.2015 / 21:04:47| <Katrina> curl -s --data token=test1 --data user=test2 --data-urlencode
|06.04.2015 / 21:04:47| <Katrina> curl -s --data token=data1 --data user=data5 --data-urlencode

В базе записано так:
nick1 {test1 test2}
nick2 {data1 data5}

За это сообщение автора Vertigo поблагодарил:
Bart (06 апр 2015 22:34)
Аватара пользователя
Vertigo
 
Сообщения: 107
Зарегистрирован: 20 авг 2008 23:49
Откуда: Москва
Благодарил (а): 0 раз.
Поблагодарили: 37 раз.
Версия бота: Eggdrop 1.8


Вернуться в FAQ + HowTo

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

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

cron