将结果写入txt文件的Python脚本 - 为什么滞后?

时间:2016-05-24 16:29:19

标签: python

我正在使用Windows 7,我有一个超级简单的脚本遍历图像目录,检查每个图像的指定条件(在我的情况下,图像中是否有脸,使用dlib),而将满足条件的图像路径写入文本文件:

def process_dir(dir_path):
    i = 0
    with open(txt_output, 'a') as f:
        for filename in os.listdir(dir_path):
            # loading image to check whether dlib detects a face:
            image_path = os.path.join(dir_path, filename)
            opencv_img = cv2.imread(image_path)
            dets = detector(opencv_img, 1)
            if len(dets) > 0 : 
                f.write(image_path)
                f.write('\n')
                i = i + 1
                print i

现在发生了以下情况:在向文件追加行时似乎存在明显滞后。例如,我可以看到脚本已“完成”检查多个图像(即,控制台打印~20,表示已找到满足条件的20个文件),但.txt文件仍为空。起初我以为我的脚本存在问题,但是等了一会儿之后我发现它们实际上被添加到了文件中,只是它似乎在“批处理”中更新了。

这似乎不是最关键的问题(而且绝对不是),但我仍然在想 - 这是什么解释了这种行为?据我所知,每次执行f.write(image_path)行时文件都会被更改 - 那为什么我会看到更新有延迟?

5 个答案:

答案 0 :(得分:1)

您是否尝试使用buffersize 0,open(txt_output,' a',0)。

答案 1 :(得分:1)

我,不是肯定关于Windows(如果我错了,请有人在这里纠正我),但我相信这是因为写缓冲区的处理方式。虽然您正在请求写入,但缓冲区只会经常写入(当缓冲区已满时)以及文件关闭时。您可以使用较小的缓冲区打开文件:

import java.awt.*;
import java.awt.event.*;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.*;
import javax.swing.table.*;

public class ItemDeletion extends JPanel
{
    private JList<String> list;
    private JTable table;

    public ItemDeletion()
    {
        setLayout( new BorderLayout(5, 5) );

        String[] items =
        {
            "One",
            "Two",
            "Three",
            "Four",
            "Five",
            "Six",
            "Seven",
            "Eight",
            "Nine",
            "Ten"
        };

        //  Add the list

        DefaultListModel<String> listModel = new DefaultListModel<String>();

        for (String item: items)
            listModel.addElement( item );

        list = new JList<String>( listModel );


        JButton listDelete = new JButton( "Delete From List" );
        listDelete.addActionListener( new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                DefaultListModel model = (DefaultListModel)list.getModel();
                int row = list.getSelectedIndex();

                while (row != -1)
                {
                    model.removeElementAt( row );
                    row = list.getSelectedIndex();
                }
            }
        });

        JPanel listPanel = new JPanel( new BorderLayout(5, 5) );
        listPanel.add(new JScrollPane( list ), BorderLayout.CENTER);
        listPanel.add(listDelete, BorderLayout.PAGE_END);

        //  Add the table

        DefaultTableModel tableModel = new DefaultTableModel(0, 1);
        List<String> tableItems = Arrays.asList( items );
        Collections.shuffle( tableItems );

        for (String item: tableItems)
        {
            System.out.println( item );
            tableModel.addRow( new String[]{item} );
        }

        table = new JTable( tableModel );

        table.setAutoCreateRowSorter(true);
        ((DefaultRowSorter)table.getRowSorter()).toggleSortOrder(0);

        JButton tableDelete = new JButton( "Delete From Table" );
        tableDelete.addActionListener( new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                DefaultTableModel model = (DefaultTableModel)table.getModel();
                int row = table.getSelectedRow();

                while (row != -1)
                {
                    int modelRow = table.convertRowIndexToModel( row );
                    model.removeRow( modelRow );
                    row = table.getSelectedRow();
                }
            }
        });

        JPanel tablePanel = new JPanel( new BorderLayout(5, 5) );
        tablePanel.add(new JScrollPane( table ), BorderLayout.CENTER);
        tablePanel.add(tableDelete, BorderLayout.PAGE_END);

        add(listPanel, BorderLayout.LINE_START);
        add(tablePanel, BorderLayout.LINE_END);
    }

    private static void createAndShowGUI()
    {
        JFrame frame = new JFrame("Multiple Item Deletion");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new ItemDeletion(), BorderLayout.NORTH);
        frame.setLocationByPlatform( true );
        frame.pack();
        frame.setVisible( true );
    }

    public static void main(String[] args)
    {
        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                createAndShowGUI();
            }
        });
    }
}

或在循环结束时手动冲洗它:

with open(txt_output, 'a', 0) as f:

我个人建议您在需要时手动进行冲洗。

答案 2 :(得分:1)

写入文件对象的数据不一定会立即显示在磁盘上。

为了提高效率,大多数操作系统都会缓冲写入,这意味着只有在累积了一定数量(通常为4K)时才将数据写入磁盘。

如果您想立即编写数据,请使用flush()功能,正如其他人所说的那样。

答案 3 :(得分:1)

听起来你正在进行文件流缓冲。

简而言之,写入文件是一个非常慢的过程(相对于处理器所做的其他事情)。除了打印到屏幕之外,修改硬盘是你能做的最慢的事情。

因此,大多数文件I / O库都会&#34;缓冲&#34;您的输出,这意味着当您写入文件时,库将把数据保存在内存缓冲区中,而不是立即修改硬盘。只有当缓冲区填满时,它才会刷新&#34;缓冲区(将数据写入磁盘),之后它再次开始填充缓冲区。这通常会大大减少实际写入操作的次数。

要回答您的问题,首先要回答的问题是,每次找到面孔时,是否真的需要立即追加到文件中?它可能会使处理速度显着降低,特别是如果您正在处理大量文件。

如果您确实需要立即更新,您基本上有两个选择:

  1. 每次写入文件时手动刷新写缓冲区。在Python中,这通常意味着调用f.flush(),正如@JamieCounsell指出的那样。
  2. 告诉Python不要使用缓冲区,或者更准确地说使用大小为0的缓冲区。正如@VikasMadhusudana所指出的那样,你可以告诉Python有多大的缓冲区用于open()的第三个参数:open(txt_output, 'a', 0)表示0字节缓冲区。
  3. 同样,你可能不需要这个;我认为可能需要这种事情的唯一情况是,如果你有一些其他外部操作正在观察文件并触发添加到其中的新数据。

    希望有所帮助!

答案 4 :(得分:0)

flush相关,请尝试:

print(image_path, file=f)  # Python 3

print >>f, image_page  # Python 2

而不是:

f.write(image_path)
f.write('\n')

print冲洗。

关于print的另一个好处是,它为您免费提供换行符。