Як я дивлюсь файл про зміни?


323

У мене файл журналу записується іншим процесом, який я хочу спостерігати за змінами. Кожного разу, коли відбувається зміна, я хотів би прочитати нові дані, щоб виконати обробку.

Який найкращий спосіб зробити це? Я сподівався, що з бібліотеки PyWin32 з’явиться якась гачка. Я знайшов цю win32file.FindNextChangeNotificationфункцію, але поняття не маю, як попросити її переглянути певний файл.

Якщо хтось зробив щось подібне, я був би дуже вдячний почути, як ...

[Редагувати] Я мав би зазначити, що я шукав рішення, яке не вимагає опитування.

[Редагувати] Прокляття! Здається, це не працює над відображеним мережевим накопичувачем. Я здогадуюсь, що Windows не чує жодних оновлень файлу так, як це робиться на локальному диску.


1
для Linux можна використовувати дзвінки з straceмоніторингу writeдля цього
test30

@ відповідь simao використовує сторожового пітона. У Python-Watchdog є чудова документація -> ось посилання на документацію ["QuickStart"], яка забезпечує мінімальний приклад коду, який спостерігає за поточною робочою каталогом.
Тревор Бойд Сміт

Відповіді:


79

Ви вже подивилися документацію, доступну на веб-сайті http://timgolden.me.uk/python/win32_how_do_i/watch_directory_for_changes.html ? Якщо вам це потрібно лише для роботи в Windows, 2-й приклад, здається, саме те, що ви хочете (якщо ви обмінюєте шлях до каталогу на той файл, який ви хочете переглянути).

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

Примітка. Я не пробував жодного з цих рішень.


5
Ця відповідь стосується Windows, але, здається, тут були розміщені деякі кросплатформенні рішення цієї проблеми.
Андерсон Грін

Чи є орієнтир, якщо цей процес проходить повільніше, то реалізовувати його рідною мовою, як c ++?
користувач1767754

Краще вставити відповідний вміст із цитованих джерел, оскільки вони можуть застаріти.
Триларіон

2
(1.) наприкінці цієї відповіді є рішуча відмова від відповідальності ... "Я не пробував жодного з цих рішень". (2.) ця відповідь - це більш-менш відповідь лише на "посилання" (3.) у відповіді згадується "опитування", але не надає нічого корисного після цього ... де як відповідь @ Deestan дає хорошу інформацію про опитування
Тревор Бойд Сміт

283

Ви спробували використати сторожовий дог ?

Бібліотека та утиліти API Python для моніторингу подій файлової системи.

Моніторинг каталогів, спрощений за допомогою

  • API крос-платформи.
  • Інструмент оболонки для запуску команд у відповідь на зміни каталогу.

Почніть швидко з простого прикладу в Quickstart ...


56
Встановлюється за допомогою easy_install? Перевірка. Безкоштовна ліцензія? Перевірка . Вирішує проблему на великих платформах? Перевірка . Я схвалюю цю відповідь. Зауважте лише: приклад на їхній сторінці проекту не працює. Використовуйте замість того, що знаходиться на їхньому github .
Інаіматі

6
Ми використовуємо сторожову службу. Ми можемо перейти на QFileSystemWatcher. Просто чесне попередження - це добре, але далеко не ідеально на всіх платформах (на даний момент). У кожної ОС є свої ідіосинкразії. Отже, якщо ви не будете присвячені тому, щоб зробити його ідеальним, ви витягнете волосся. Якщо ви просто хочете переглянути 10 файлів або близько того, я б опитував. Кешування диска ОС дуже зріле, і Watchdog так чи інакше включає опитування API. Це в основному для перегляду величезних структур папок IMHO.
SilentSteel

3
Моє одне захоплення сторожовим собакою полягає в тому, що він має багато залежностей. Мало, ніж PyQt, звичайно, але він не працює і відчуває себе як мінімальне, найкраща практика, рішення "одна робота" та "роби" це "правильно".
AndreasT

1
Чи правильно тут @denfromufa? Чи дійсно сторож блокує файли, тому їх не можна редагувати одночасно, щоб спостерігач спостерігав за ними? Я навряд чи вірю, що це було б абсолютно марно.
Мішель Мюллер

1
@ MichelMüller Я щойно перевірив цей приклад (див. Посилання нижче), і він працює! не впевнений, що було не так раніше, але ця відповідь не дає жодного прикладу. stackoverflow.com/a/18599427/2230844
denfromufa

92

Якщо опитування досить добре для вас, я б просто спостерігав, чи змінюється статистика файлу "модифікований час". Щоб прочитати:

os.stat(filename).st_mtime

(Також зауважте, що нативне рішення Windows зміни подій працює не за будь-яких обставин, наприклад, на мережевих накопичувачах.)

import os

class Monkey(object):
    def __init__(self):
        self._cached_stamp = 0
        self.filename = '/path/to/file'

    def ook(self):
        stamp = os.stat(self.filename).st_mtime
        if stamp != self._cached_stamp:
            self._cached_stamp = stamp
            # File has changed, so do something...

1
Як ви можете це зробити з інтервалом?
допатраман

1
@dopatraman Ось як це можна зробити на інтервалі `import sys import time pub = Monkey (), а True: спробуйте: time.sleep (1) pub.watch (), крім KeyboardInterrupt: розрив print ('\ nDone'), за винятком : print (f'Unhandled error: {sys.exc_info () [0]} ') `
Влад Безден

Чудове просте рішення! Я додав перевірку , щоб зберегти його від звітності файлу змінений при першому запуску: if self._cached_stamp is not None.
Номенон

50

Якщо ви хочете отримати багатоплатформенне рішення, то перевірте QFileSystemWatcher . Ось приклад коду (не санізований):

from PyQt4 import QtCore

@QtCore.pyqtSlot(str)
def directory_changed(path):
    print('Directory Changed!!!')

@QtCore.pyqtSlot(str)
def file_changed(path):
    print('File Changed!!!')

fs_watcher = QtCore.QFileSystemWatcher(['/path/to/files_1', '/path/to/files_2', '/path/to/files_3'])

fs_watcher.connect(fs_watcher, QtCore.SIGNAL('directoryChanged(QString)'), directory_changed)
fs_watcher.connect(fs_watcher, QtCore.SIGNAL('fileChanged(QString)'), file_changed)

6
Я думаю, що це, можливо, найкраща відповідь групи, зважаючи на те, що вони або: a) покладаються на об'єкт File32 SystemSwatcher Win32 і не можуть бути перенесені, або b) опитування для файлу (що погано для продуктивності і не буде масштабувати). Шкода, що Python не має вбудованого засобу, оскільки PyQt - це величезна залежність, якщо все, що ви використовуєте, - це клас QFileSystemWatcher.
CadentOrange

4
Мені подобається це рішення. Я хотів би зазначити, що вам потрібен екземпляр QApplication, щоб він працював, я додав "app = QtGui.QApplication (sys.argv)" прямо під імпортом, а потім "app.exec_ ()" після підключення сигналу.
spencewah

Тільки тестуючи це на вікні Linux, я бачу, що викликається метод directory_changed, але не file_changed.
Кен Кіндер

@CadentOrange, якщо ви цього не зробите , як залежність PyQt, пакет є правильна відповідьwatchdog
Майк Пеннінгтон

чому б не використовувати PySideдля цього замість PyQtтакого невеликого використання.
Ciasto piekarz

29

Він не повинен працювати у Windows (можливо, з cygwin?), Але для користувача Unix слід використовувати системний виклик "fcntl". Ось приклад у Python. Це здебільшого той самий код, якщо вам потрібно записати його в C (ті ж назви функцій)

import time
import fcntl
import os
import signal

FNAME = "/HOME/TOTO/FILETOWATCH"

def handler(signum, frame):
    print "File %s modified" % (FNAME,)

signal.signal(signal.SIGIO, handler)
fd = os.open(FNAME,  os.O_RDONLY)
fcntl.fcntl(fd, fcntl.F_SETSIG, 0)
fcntl.fcntl(fd, fcntl.F_NOTIFY,
            fcntl.DN_MODIFY | fcntl.DN_CREATE | fcntl.DN_MULTISHOT)

while True:
    time.sleep(10000)

3
Працює як шарм з ядром Linux 2.6.31 у файловій системі ext4 (на Ubuntu 10.04), хоча лише для каталогів - це піднімає IOError "не каталог", якщо я використовую його з файлом.
Девід Андерхілл

1
ВЕЛИКИЙ! Те саме для мене, працює лише в каталогах і переглядає файли в цьому каталозі. Але він не працюватиме для модифікованих файлів у підкаталогах, тому, схоже, вам потрібно пройтись через підкаталоги та переглянути їх. (чи є кращий спосіб це зробити?)
lfagundes

20

Ознайомтеся з пінотифікацією .

inotify замінює dnotify (з попередньої відповіді) на новіші Linux і дозволяє моніторинг на рівні файлів, а не на рівні каталогу.


5
Не ставити заслінку на цю відповідь, але, прочитавши цю статтю, я б сказав, що це може бути не таким гламурним рішенням, як думка. serpentine.com/blog/2008/01/04/why-you-should-not-use-pyinotify
NuclearPeon

1
pyinotify має безліч недоліків, починаючи від дуже непітонічної бази коду до споживання пам'яті. Краще шукати інші варіанти ..
Tyto

13

Після декількох злому сценарію Тіма Голда, у мене є таке, що, здається, працює досить добре:

import os

import win32file
import win32con

path_to_watch = "." # look at the current directory
file_to_watch = "test.txt" # look for changes to a file called test.txt

def ProcessNewData( newData ):
    print "Text added: %s"%newData

# Set up the bits we'll need for output
ACTIONS = {
  1 : "Created",
  2 : "Deleted",
  3 : "Updated",
  4 : "Renamed from something",
  5 : "Renamed to something"
}
FILE_LIST_DIRECTORY = 0x0001
hDir = win32file.CreateFile (
  path_to_watch,
  FILE_LIST_DIRECTORY,
  win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE,
  None,
  win32con.OPEN_EXISTING,
  win32con.FILE_FLAG_BACKUP_SEMANTICS,
  None
)

# Open the file we're interested in
a = open(file_to_watch, "r")

# Throw away any exising log data
a.read()

# Wait for new data and call ProcessNewData for each new chunk that's written
while 1:
  # Wait for a change to occur
  results = win32file.ReadDirectoryChangesW (
    hDir,
    1024,
    False,
    win32con.FILE_NOTIFY_CHANGE_LAST_WRITE,
    None,
    None
  )

  # For each change, check to see if it's updating the file we're interested in
  for action, file in results:
    full_filename = os.path.join (path_to_watch, file)
    #print file, ACTIONS.get (action, "Unknown")
    if file == file_to_watch:
        newText = a.read()
        if newText != "":
            ProcessNewData( newText )

Можливо, це може зробити з навантаженням більше перевірки помилок, але для простого перегляду файлу журналу та проведення деякої обробки на ньому, перш ніж виплюнути його на екран, це працює добре.

Дякую всім за ваш внесок - чудові речі!


10

Для перегляду одного файлу з опитуванням та мінімальними залежностями, ось приклад, повністю розроблений на основі відповіді Деестана (вище):

import os
import sys 
import time

class Watcher(object):
    running = True
    refresh_delay_secs = 1

    # Constructor
    def __init__(self, watch_file, call_func_on_change=None, *args, **kwargs):
        self._cached_stamp = 0
        self.filename = watch_file
        self.call_func_on_change = call_func_on_change
        self.args = args
        self.kwargs = kwargs

    # Look for changes
    def look(self):
        stamp = os.stat(self.filename).st_mtime
        if stamp != self._cached_stamp:
            self._cached_stamp = stamp
            # File has changed, so do something...
            print('File changed')
            if self.call_func_on_change is not None:
                self.call_func_on_change(*self.args, **self.kwargs)

    # Keep watching in a loop        
    def watch(self):
        while self.running: 
            try: 
                # Look for changes
                time.sleep(self.refresh_delay_secs) 
                self.look() 
            except KeyboardInterrupt: 
                print('\nDone') 
                break 
            except FileNotFoundError:
                # Action on file not found
                pass
            except: 
                print('Unhandled error: %s' % sys.exc_info()[0])

# Call this function each time a change happens
def custom_action(text):
    print(text)

watch_file = 'my_file.txt'

# watcher = Watcher(watch_file)  # simple
watcher = Watcher(watch_file, custom_action, text='yes, changed')  # also call custom action function
watcher.watch()  # start the watch going

2
Ви могли б зробити watch_fileі _cached_stampв списки, і ітерація по ним в циклі. Не дуже добре масштабувати файли, хоча
4Oh4,

Чи це не запускає дію щоразу, коли вона виконується? _cached_stamp встановлюється в 0, а потім порівнюється з os.stat (self.filename) .st_mtime. _cached_stamp слід встановити на os.stat (self.filename) .st_mtime в конструкторі, ні?
Сенонімічний

1
call_func_on_change()буде запущено під час першого запуску look(), але потім _cached_stampоновиться, тому не буде запущено знову, поки значення не os.stat(self.filename).st_mtime. _cached_stampзміниться.
4Oh4

1
Ви можете встановити значення _cached_stampконструктора, якщо ви не хочете, call_func_on_change()щоб вас викликали під час першого запуску
4Oh4

Я використовував ваш сценарій для виклику певної функції щодо зміни файлів. Моя функція не приймає ніяких аргументів на відміну від ваших. Я думав, що для того, щоб це працювало, потрібно видалити * args, ** kwargs. Це виглядало так (я ставив лише рядки зі змінами): self.call_func_on_change(self) def custom_action(): watcher = Watcher(watch_file, custom_action())Але це не спрацювало. Дія викликалася лише під час першої ітерації: Файл змінився так, змінився Файл змінено Файл змінено Файл змінено. Він почав працювати, коли я зберігав * аргументи та викликав: watcher = Watcher(watch_file, custom_action)я намагаюся задатися питанням, чому?
zwornik

7

Перевірте мою відповідь на подібне запитання . Ви можете спробувати ту саму петлю в Python. Ця сторінка пропонує:

import time

while 1:
    where = file.tell()
    line = file.readline()
    if not line:
        time.sleep(1)
        file.seek(where)
    else:
        print line, # already has newline

Також дивіться файл хвоста () з Python .


Ви можете sys.stdout.write (рядок). Код не працює, якщо файл усічений. Python має вбудований функціональний файл ().
jfs

Я опублікував модифіковану версію вашого коду. Ви можете включити його у свою відповідь, якщо він працює для вас.
jfs

7

Найпростішим рішенням для мене є використання інструмента сторожового годинника

З https://pypi.python.org/pypi/watchdog я тепер маю процес, який шукає файли sql у каталозі та виконує їх за необхідності.

watchmedo shell-command \
--patterns="*.sql" \
--recursive \
--command='~/Desktop/load_files_into_mysql_database.sh' \
.

6

Отже, оскільки ви використовуєте Python, ви можете просто відкрити файл і продовжувати читати з нього рядки.

f = open('file.log')

Якщо прочитаний рядок не порожній , ви обробляєте його.

line = f.readline()
if line:
    // Do what you want with the line

Можливо, вам не вистачає, що це нормально, щоб продовжувати телефонувати readline в EOF. У цьому випадку він просто повертатиме порожню рядок. І коли щось додано до файлу журналу, зчитування продовжиться там, де воно зупинилося, як вам потрібно.

Якщо ви шукаєте рішення, яке використовує події або певну бібліотеку, вкажіть це у своєму запитанні. Інакше я вважаю, що це рішення просто чудове.


6

Ось спрощена версія коду Кендера, яка, схоже, робить той самий трюк і не імпортує весь файл:

# Check file for new data.

import time

f = open(r'c:\temp\test.txt', 'r')

while True:

    line = f.readline()
    if not line:
        time.sleep(1)
        print 'Nothing New'
    else:
        print 'Call Function: ', line

6

Це ще одна модифікація сценарію Тіма Голдана, яка працює на типах unix і додає простий дисерт для модифікації файлів за допомогою dict (file => time).

використання: whatName.py path_to_dir_to_watch

#!/usr/bin/env python

import os, sys, time

def files_to_timestamp(path):
    files = [os.path.join(path, f) for f in os.listdir(path)]
    return dict ([(f, os.path.getmtime(f)) for f in files])

if __name__ == "__main__":

    path_to_watch = sys.argv[1]
    print('Watching {}..'.format(path_to_watch))

    before = files_to_timestamp(path_to_watch)

    while 1:
        time.sleep (2)
        after = files_to_timestamp(path_to_watch)

        added = [f for f in after.keys() if not f in before.keys()]
        removed = [f for f in before.keys() if not f in after.keys()]
        modified = []

        for f in before.keys():
            if not f in removed:
                if os.path.getmtime(f) != before.get(f):
                    modified.append(f)

        if added: print('Added: {}'.format(', '.join(added)))
        if removed: print('Removed: {}'.format(', '.join(removed)))
        if modified: print('Modified: {}'.format(', '.join(modified)))

        before = after

Оновлено для підтримки python3
ronedg

4

Як ви бачите у статті Тіма Голден , на яку вказував Хорст Гутманн , WIN32 порівняно складний і переглядає каталоги, а не один файл.

Я б запропонував вам заглянути в IronPython , що є реалізацією .NET python. За допомогою IronPython ви можете використовувати всі функції .NET - у тому числі

System.IO.FileSystemWatcher

Який обробляє окремі файли за допомогою простого інтерфейсу подій .


@Ciasto, тому що тоді у вас повинен бути доступний Iron Python, а не основна установка Python.
Джон Кейдж

1

Це приклад перевірки файлу на зміни. Той, який може бути не найкращим способом зробити це, але це, звичайно, короткий шлях.

Зручний інструмент для перезавантаження програми, коли внесені зміни до джерела. Я зробив це під час гри з пігамою, щоб я міг бачити, що ефекти відбуваються відразу після збереження файлу.

При використанні в pygame переконайтеся, що матеріали в циклі "while" розміщені в ігровому циклі. Інакше ваша програма застрягне в нескінченному циклі, і ви не побачите оновлення своєї гри.

file_size_stored = os.stat('neuron.py').st_size

  while True:
    try:
      file_size_current = os.stat('neuron.py').st_size
      if file_size_stored != file_size_current:
        restart_program()
    except: 
      pass

У випадку, якщо вам потрібен код перезавантаження, який я знайшов в Інтернеті. Ось. (Не стосується питання, хоча це може стати в нагоді)

def restart_program(): #restart application
    python = sys.executable
    os.execl(python, python, * sys.argv)

Забавляйте, щоб електрони робили те, що ви хочете, щоб вони робили.


Начебто використання .st_mtimeзамість цього .st_sizeбуло б більш надійним і не менш коротким способом цього зробити, хоча ОП зазначила, що він не хотів це робити шляхом опитування.
мартино

1
ACTIONS = {
  1 : "Created",
  2 : "Deleted",
  3 : "Updated",
  4 : "Renamed from something",
  5 : "Renamed to something"
}
FILE_LIST_DIRECTORY = 0x0001

class myThread (threading.Thread):
    def __init__(self, threadID, fileName, directory, origin):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.fileName = fileName
        self.daemon = True
        self.dir = directory
        self.originalFile = origin
    def run(self):
        startMonitor(self.fileName, self.dir, self.originalFile)

def startMonitor(fileMonitoring,dirPath,originalFile):
    hDir = win32file.CreateFile (
        dirPath,
        FILE_LIST_DIRECTORY,
        win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE,
        None,
        win32con.OPEN_EXISTING,
        win32con.FILE_FLAG_BACKUP_SEMANTICS,
        None
    )
    # Wait for new data and call ProcessNewData for each new chunk that's
    # written
    while 1:
        # Wait for a change to occur
        results = win32file.ReadDirectoryChangesW (
            hDir,
            1024,
            False,
            win32con.FILE_NOTIFY_CHANGE_LAST_WRITE,
            None,
            None
        )
        # For each change, check to see if it's updating the file we're
        # interested in
        for action, file_M in results:
            full_filename = os.path.join (dirPath, file_M)
            #print file, ACTIONS.get (action, "Unknown")
            if len(full_filename) == len(fileMonitoring) and action == 3:
                #copy to main file
                ...

1

Ось приклад, орієнтований на перегляд вхідних файлів, які записують не більше одного рядка в секунду, але зазвичай набагато менше. Метою є додавання останнього рядка (останнього запису) до вказаного вихідного файлу. Я скопіював це з одного зі своїх проектів і просто видалив усі неактуальні рядки. Вам доведеться заповнити або змінити відсутні символи.

from PyQt5.QtCore import QFileSystemWatcher, QSettings, QThread
from ui_main_window import Ui_MainWindow   # Qt Creator gen'd 

class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        QMainWindow.__init__(self, parent)
        Ui_MainWindow.__init__(self)
        self._fileWatcher = QFileSystemWatcher()
        self._fileWatcher.fileChanged.connect(self.fileChanged)

    def fileChanged(self, filepath):
        QThread.msleep(300)    # Reqd on some machines, give chance for write to complete
        # ^^ About to test this, may need more sophisticated solution
        with open(filepath) as file:
            lastLine = list(file)[-1]
        destPath = self._filemap[filepath]['dest file']
        with open(destPath, 'a') as out_file:               # a= append
            out_file.writelines([lastLine])

Звичайно, що охоплює клас QMainWindow не є строго необхідним, тобто. ви можете використовувати QFileSystemWatcher поодинці.



0

Здається, ніхто не опублікував fswatch . Це спостерігач файлової системи між платформами. Просто встановіть його, запустіть його та дотримуйтесь підказок.

Я використовував його з програмами python та golang, і він просто працює.


0

пов'язане рішення @ 4Oh4 - плавна зміна списку файлів, які слід переглянути;

import os
import sys
import time

class Watcher(object):
    running = True
    refresh_delay_secs = 1

    # Constructor
    def __init__(self, watch_files, call_func_on_change=None, *args, **kwargs):
        self._cached_stamp = 0
        self._cached_stamp_files = {}
        self.filenames = watch_files
        self.call_func_on_change = call_func_on_change
        self.args = args
        self.kwargs = kwargs

    # Look for changes
    def look(self):
        for file in self.filenames:
            stamp = os.stat(file).st_mtime
            if not file in self._cached_stamp_files:
                self._cached_stamp_files[file] = 0
            if stamp != self._cached_stamp_files[file]:
                self._cached_stamp_files[file] = stamp
                # File has changed, so do something...
                file_to_read = open(file, 'r')
                value = file_to_read.read()
                print("value from file", value)
                file_to_read.seek(0)
                if self.call_func_on_change is not None:
                    self.call_func_on_change(*self.args, **self.kwargs)

    # Keep watching in a loop
    def watch(self):
        while self.running:
            try:
                # Look for changes
                time.sleep(self.refresh_delay_secs)
                self.look()
            except KeyboardInterrupt:
                print('\nDone')
                break
            except FileNotFoundError:
                # Action on file not found
                pass
            except Exception as e:
                print(e)
                print('Unhandled error: %s' % sys.exc_info()[0])

# Call this function each time a change happens
def custom_action(text):
    print(text)
    # pass

watch_files = ['/Users/mexekanez/my_file.txt', '/Users/mexekanez/my_file1.txt']

# watcher = Watcher(watch_file)  # simple



if __name__ == "__main__":
    watcher = Watcher(watch_files, custom_action, text='yes, changed')  # also call custom action function
    watcher.watch()  # start the watch going


-2

Я не знаю жодної функції Windows. Ви можете спробувати отримати хеш файлу MD5 щосекунди / хвилину / годину (залежить від того, наскільки швидко вам це потрібно) і порівняти його з останнім хешем. Коли він відрізняється, ви знаєте, що файл було змінено, і ви прочитали новіші рядки.


-6

Я б спробував щось подібне.

    try:
            f = open(filePath)
    except IOError:
            print "No such file: %s" % filePath
            raw_input("Press Enter to close window")
    try:
            lines = f.readlines()
            while True:
                    line = f.readline()
                    try:
                            if not line:
                                    time.sleep(1)
                            else:
                                    functionThatAnalisesTheLine(line)
                    except Exception, e:
                            # handle the exception somehow (for example, log the trace) and raise the same exception again
                            raw_input("Press Enter to close window")
                            raise e
    finally:
            f.close()

Цикл перевіряє, чи є новий рядок (и) з часу останнього читання файлу часу - якщо є, він читається та передається у functionThatAnalisesTheLineфункцію. Якщо ні, сценарій чекає 1 секунди і повторює процес.


4
-1: Відкриття файлу та читання рядків не є чудовою ідеєю, коли файли можуть бути величиною 100 Мб. Вам також доведеться запускати його для кожного файлу, що було б погано, коли ви хочете переглянути 1000 файлів.
Джон Кейдж

1
Дійсно? Відкрити файл для змін?
Farsheed
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.