Освой MicroPython играючи
/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000
Примеры с файлами проводились на устройстве M5Stack Fire.
Общую информацию о диске можно узнать в примере о модулях os и uos.
В основном все мои исследования связаны с доступом к карточке SD. Хотя в документации говорится, что устройство поддерживает карточку не более 16 Гб, у меня прекрасно работало с карточкой объёма 32 Гб. Но сможет ли устройство прочитать файл большого размера или работать с полностью забитым диском остаётся под вопросом.
Для работы с файловой системой нужно импортировать классы os и иногда uos.
Доступ к карточке памяти. Иногда работает криво, бывает при первом включении данные с диска не считываются, а потом всё нормально (при работе с редактором Му).
Получим список файлов и папок на карточке и выводим на экран их имена.
from m5stack import *
from m5ui import *
from uiflow import *
import uos
lcd.clear()
files = uos.listdir('/sd')
lcd.setCursor(0, 0)
for element in files:
lcd.print(element + '\n')
Аналогично можно получить список файлов флеш-памяти (/flash) или содержимое в её подпапках. Получим список файлов с подпапки /flash/res.
files = uos.listdir('/flash/res/')
В предыдущем примере файлы и папки свалены в одну кучу. Можно разделить папки и файлы отдельно.
import os
FILE_CODE = 0x8000
def isfile(path):
return os.stat(path)[0] == FILE_CODE
def main():
files = [i for i in sorted(os.listdir()) if isfile(i)]
print('files:', files)
dirs = [i for i in sorted(os.listdir()) if not isfile(i)]
print('dirs:', dirs)
main()
# Результат на M5Stack
# files: ['boot.py', 'main.py', 'temp.py', 'test.py']
# dirs: ['apps', 'blocks', 'emojiImg', 'img', 'res']
Размер файла можно узнать через седьмое значение кортежа.
# файл с флеш-памяти
lcd.print(str(uos.stat('/flash/main.py')[6]))
# файл с SD-карты
lcd.print(str(uos.stat('/sd/cats.txt')[6]))
Все опыты с файлами я проводил на SD-карте. Если будете экспериментировать на флеш-памяти, то не трогайте важные системные файла, типа boot.py и др.
С записью и чтением текстовых файлов возникло несколько проблем. Когда я пытался создать текстовый документ на компьютере и сохранить его на диске, то при чтении выводились просто нечитаемые символы в виде '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'. Пробовал и ANSI и UTF-8.
Но если создать файл средствами самого микроконтроллера, то файл потом нормально считывается. Позже я посмотрел его через компьютер при помощи редактора Notepad++. Файл сохраняется в кодировке Unix (LF) UTF-8. Но попытка создать опять же такой файл в указанной кодировке на компьютере не помогла, устройство отказывалось его считывать. Здесь надо разбираться, как укротить проблему.
Для начала получим список поддерживаемых команд для объекта file:
dir(file)
Результат: ['__class__', '__enter__', '__exit__', '__next__', 'close', 'read', 'readinto', 'readline', 'write', '__del__', 'flush', 'readlines', 'seek', 'tell'].
Перед работой с файлом, его следует открыть через метод open(). У метода много параметров, в большинстве случаев достаточно двух - имя файла (путь к файлу может быть относительным и абсолютным) и режим. Ещё используется третий параметр для кодировки.
Список режимов:
Режимы могут быть объединены. По умолчанию режим равен rt (чтение текстового файла). Для чтения в двоичном режиме используйте режим rb.
После окончания работы с файлом, его нужно обязательно закрыть через метод close().
Создадим файл с указанным именем и запишем в него две строчки текста при помощи метода write(). Я проделал это в режиме REPL.
file = open ("/sd/demo.txt", "w")
file.write("First Line \n")
file.write("Second Line")
file.close()
При вводе в режиме REPL после ввода команды write() выводится возвращаемое значение метода - количество записанный в файл байт.
Параметр w в методе open() означает write, т.е. мы открываем файл в режиме записи. Метод возвращает объект TextIOWrapper.
file = open("/sd/demo.txt", "w")
type(file)
# <class 'TextIOWrapper'>
Теперь пробуем прочитать файл. Есть два варианта: прочитать весь файл сразу целиком или считывать построчно.
Прочитаем весь файл целиком методом read() (REPL). Обратите внимание, что содержимое вывелось в одну строку с использованием служебного символа перевода строки. Мы позже к нему вернёмся.
file = open("/sd/demo.txt", "r")
file.read() # 'First Line \nSecond Line'
file.close()
В методе можно указать число символов для чтения (целое число).
file.read(2)
Есть альтернативные варианты чтения файлов. В первом варианте возвращается строковый объект и символ перевода строки не выводится. Во втором варианте возвращается объект bytes.
data = open('/sd/demo.txt').read() # объект str
print(data)
data = open('/sd/demo.txt', 'rb').read() # объект bytes
print(data)
Теперь пробуем прочитать файл построчно при помощи readline().
from m5stack import *
from m5ui import *
from uiflow import *
import uos
lcd.clear()
file = open("/sd/demo.txt", "r")
lcd.setCursor(0, 0)
lcd.print(file.readline())
lcd.print(file.readline())
file.close()
Если выводить данные на экран, как в примере выше, то увидим две строки без служебных символов.
First Line
Second Line
Если выводить данные в режиме REPL, то у первой строки увидим немного другой ответ.
'First Line \n'
Если не хочется видеть символ \n, то можно добавить вызов метода rstrip().
file.readline().rstrip("\n")
Если мы не знаем число строк, то можем использовать цикл и ждать, когда достигнем строки с нулевой длиной (конец файла).
По умолчанию функция print() выводит текст, автоматически добавляя символ перевода строки в конце. Мы подавляем этот символ, указывая end='', поскольку строки, считанные из файла, и без того оканчиваются символом перевода строки.
file = open("/sd/demo.txt", "r")
lcd.setCursor(0, 0)
while True:
line = file.readline()
if len(line) == 0: # конец файла
break
lcd.print(line, end='')
file.close()
Файл можно переименовать при помощи метода rename().
os.rename("/sd/readme.txt", "/sd/cats.txt")
os.listdir("/sd")
Чтобы удалить файл из диска, вызовем метод remove() и посмотрим список файлов на диске, чтобы убедиться в удалении файла.
os.remove("/sd/readme.txt")
os.listdir("/sd")
Для удаления папки используется метод rmdir(). Напишем универсальный метод для удаления файла или папки.
import os
FILE_CODE = 0x8000
def isfile(path):
return os.stat(path)[0] == FILE_CODE
def any_remove(path):
func = os.remove if isfile(path) else os.rmdir
func(path)
Пример создания папок.
import os
def exists(path):
try:
os.stat(path)
except OSError:
return False
return True
def mkdir_safe(path):
if not exists(path):
os.mkdir(path)
def makedirs(path):
items = path.strip('/').split('/')
count = len(items)
paths = ['/' + '/'.join(items[0:i + 1]) for i in range(count)]
for path in paths:
os.mkdir(path)