在Python中读取大CSV文件的行

5 投票
2 回答
2315 浏览
提问于 2025-04-18 14:07

我有一个非常大的csv文件,无法一次性全部加载到内存中。所以我想分块读取这个文件,把它转换成numpy数组,然后再进行一些处理。

我已经查看过了:在Python中读取大文件的懒惰方法?

但这里的问题是,它是一个普通的读取器,我找不到在csvReader中指定大小的选项。

而且因为我想把行转换成numpy数组,所以我不想半读任何一行,所以我希望能有一个方法,可以让我在读取时指定“行数”。

有没有什么内置的函数或者简单的方法可以做到这一点?

2 个回答

0

我用了这个函数。基本的想法是创建一个生成器,用来逐个输出文件中的数字。

def iter_loadtxt(filename, delimiter=',', skiprows=0, read_range=None, dtype=float):
    '''
    Read the file line by line and convert it to Numpy array.
    :param delimiter: character
    :param skiprows : int
    :param read_range: [int, int] or None. set it to None and the function will read the whole file.
    :param dtype: type
    '''
    def iter_func():
        with open(filename, 'r') as infile:
            for _ in range(skiprows):
                next(infile)
            if read_range is None:
                for line in infile:
                    line = line.rstrip().split(delimiter)
                    for item in line:
                        yield dtype(item)
            else:
                counter = 0
                for line in infile:
                    if counter < read_range[0]:
                        counter += 1
                    else:
                        counter += 1
                        for item in line:
                            yield dtype(item)

                    if counter >= read_range[1]:
                        break

        iter_loadtxt.rowlength = len(line)

    data = np.fromiter(iter_func(), dtype=dtype)
    data = data.reshape((-1, iter_loadtxt.rowlength))
    return data
2

csv.reader 这个工具不会把整个文件一次性读到内存里。它是懒惰地逐行读取文件,也就是说,当你在使用 reader 对象时,它会一行一行地读取文件。所以你可以像平常一样使用 reader,但是在读取到你想要的行数后,可以用 break 来停止读取。你可以在这里看到实现 reader 对象的 C代码

Initializer for the reader objecT:
static PyObject *
csv_reader(PyObject *module, PyObject *args, PyObject *keyword_args)
{
    PyObject * iterator, * dialect = NULL;
    ReaderObj * self = PyObject_GC_New(ReaderObj, &Reader_Type);

    if (!self)
        return NULL;

    self->dialect = NULL;
    self->fields = NULL;
    self->input_iter = NULL;
    self->field = NULL;
    // stuff we dont care about here
    // ...
    self->input_iter = PyObject_GetIter(iterator);  // here we save the iterator (file object) we passed in
    if (self->input_iter == NULL) {
        PyErr_SetString(PyExc_TypeError,
                        "argument 1 must be an iterator");
        Py_DECREF(self);
        return NULL;
    }

static PyObject *
Reader_iternext(ReaderObj *self)  // This is what gets called when you call `next(reader_obj)` (which is what a for loop does internally)
{
    PyObject *fields = NULL;
    Py_UCS4 c;
    Py_ssize_t pos, linelen;
    unsigned int kind;
    void *data;
    PyObject *lineobj;

    if (parse_reset(self) < 0)
        return NULL;
    do {
        lineobj = PyIter_Next(self->input_iter);  // Equivalent to calling `next(input_iter)`
        if (lineobj == NULL) {
            /* End of input OR exception */
            if (!PyErr_Occurred() && (self->field_len != 0 ||
                                      self->state == IN_QUOTED_FIELD)) {
                if (self->dialect->strict)
                    PyErr_SetString(_csvstate_global->error_obj,
                                    "unexpected end of data");
                else if (parse_save_field(self) >= 0)
                    break;
            }
            return NULL;
        }

正如你所看到的,next(reader_object) 实际上是内部调用 next(file_object)。所以你是逐行读取这两个对象,而不是把整个文件一次性读到内存里。

撰写回答