Чтение CSV файла в двумерный массив

Функция TextFile2Array предназначена для преобразования файла CSV в двумерный массив

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

Чтобы упростить весь процесс - от выбора файла CSV в диалоговом окне, до разбиения загруженного из файла текста в двумерный массив, и была написана эта функция.

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

Пример использования функции для загрузки данных из файла CSV:

Sub ЗагрузкаДанныхИзCSV()
    ' выбор файла по умолчанию предлагается в той же папке,
    ' где расположен текущий файл Excel
    CSVarr = TextFile2Array(, ThisWorkbook.Path, , "*.csv")
 
    ' проверка результата загрузки данных (выход из макроса, если данные не загружены)
    If Not IsArray(CSVarr) Then MsgBox "Файл CSV не обработан", vbCritical, "Ошибка": Exit Sub
 
    ' ваш код обработки двумерного массива
    Debug.Print "Загружен двумерный массив размерами " & _
                UBound(CSVarr, 1) & " строк на " & UBound(CSVarr, 2) & " столбцов"
End Sub

Результат в окне Immediate:

Загружен двумерный массив размерами 1244 строк на 9 столбцов

Код функции TextFile2Array:

Function TextFile2Array(Optional ByVal Title As String = "Выберите файл для обработки", _
                        Optional ByVal InitialPath As String = "c:\", _
                        Optional ByVal FilterDescription As String = "Текстовые файлы", _
                        Optional ByVal FilterExtention As String = "*.*", _
                        Optional ByVal ColumnsSeparator$ = ";", _
                        Optional ByVal RowsSeparator$ = vbNewLine) As Variant
    ' Функция запрашивает имя файла (текстового, CSV, и т.п.), и обрабатывает его содержимое
    ' В качестве параметров можно задать  разделители строк и столбцов для разбиваемой строки
    ' Возвращает двумерный массив - результат преобразования текстового файла в двумерный массив

    On Error Resume Next
    With Application.FileDialog(msoFileDialogOpen)    ' диалоговое окно выбора файла CSV
        .ButtonName = "Выбрать": .Title = Title: .InitialFileName = InitialPath
        .Filters.Clear: .Filters.Add FilterDescription, FilterExtention
        If .Show <> -1 Then Exit Function
        Filename$ = .SelectedItems(1)
    End With
 
    Set fso = CreateObject("scripting.filesystemobject")    ' читаем текст из выбранного файла
    Set ts = fso.OpenTextFile(Filename$, 1, True): txt$ = ts.ReadAll: ts.Close
    Set ts = Nothing: Set fso = Nothing
 
    txt = Trim(txt): Err.Clear    ' разделяем текст на строки и столбцы
    If txt Like "*" & RowsSeparator$ Then txt = Left(txt, Len(txt) - Len(RowsSeparator$))
 
    tmpArr1 = Split(txt, RowsSeparator$): RowsCount = UBound(tmpArr1) + 1
    ColumnsCount = UBound(Split(tmpArr1(0), ColumnsSeparator$)) + 1
 
    If Err.Number > 0 Then MsgBox "Строка не может быть разбита на двумерный массив", vbCritical: End
    ReDim arr(1 To RowsCount, 1 To ColumnsCount)
 
    For i = LBound(tmpArr1) To UBound(tmpArr1)
        tmpArr2 = Split(Trim(tmpArr1(i)), ColumnsSeparator$)
        For j = 1 To UBound(tmpArr2)+1
            arr(i + 1, j) = tmpArr2(j - 1)
        Next j
    Next i
    TextFile2Array = arr    ' возвращаем результат в виде двумерного массива
End Function


Ещё одна функция, - без вывода диалогового окна выбора файла

Function LoadArrayFromTextFile(ByVal filename$, Optional ByVal FirstRow& = 1, _
                               Optional ByVal ColumnsSeparator$ = ";", Optional ByVal RowsSeparator$ = vbNewLine) As Variant
    ' Функция открывает текстовый (CSV) файл filename$,
    ' и загружает макссив данных, начиная со строки FirstRow&
    ' В качестве параметров можно задать  разделители строк и столбцов для разбиваемой строки
    ' Возвращает двумерный массив - результат преобразования текстового файла в двумерный массив

    On Error Resume Next
    Set FSO = CreateObject("scripting.filesystemobject")        ' читаем текст из выбранного файла
    Set ts = FSO.OpenTextFile(filename$, 1, True): txt$ = ts.ReadAll: ts.Close
    Set ts = Nothing: Set FSO = Nothing
 
    txt = Trim(txt): Err.Clear        ' разделяем текст на строки и столбцы
    If txt Like "*" & RowsSeparator$ Then txt = Left(txt, Len(txt) - Len(RowsSeparator$))
 
    If FirstRow& > 1 Then        ' обрезаем ненужные строки
        txt = Split(txt, RowsSeparator$, FirstRow&)(FirstRow& - 1)
    End If
 
    Err.Clear: tmpArr1 = Split(txt, RowsSeparator$): RowsCount = UBound(tmpArr1) + 1
    ColumnsCount = UBound(Split(tmpArr1(0), ColumnsSeparator$)) + 1
 
    If Err.Number > 0 Then MsgBox "Текст файла " & Dir(filename$, vbNormal) & _
     " не может быть считан в двумерный массив", vbCritical: Exit Function
    ReDim arr(1 To RowsCount, 1 To ColumnsCount)
 
    For i = LBound(tmpArr1) To UBound(tmpArr1)
        tmpArr2 = Split(Trim(tmpArr1(i)), ColumnsSeparator$)
        For j = 1 To UBound(tmpArr2) + 1
            arr(i + 1, j) = tmpArr2(j - 1)
        Next j
    Next i
    LoadArrayFromTextFile = arr        ' возвращаем результат в виде двумерного массива
End Function

Комментарии

Настройки просмотра комментариев

Выберите нужный метод показа комментариев и нажмите "Сохранить установки".

Если разделители столбцов внутри одного файла - разные, то это уже нестандартный файл CSV, тут готовое решение не подойдёт (нужно заметно усложнять макрос)

Да ничего я не хочу. Я это и сам легко сделаю, без Вашего платного участия. Я о функциональной полноте функции вёл речь. Ведь, по логике, вроде бы, если есть начало (грузить с такой-то строки) должна быть и возможность "грузить до такой-то строки". Хотя для Вас это скорее всего не очевидно, потому на том и закончили. Ну, в принципе и так не плохо.

Этой функции и так цены нет (ибо она бесплатная)))

Вы хотите, чтобы я бесплатно дорабатывал функции под ваши потребности?
У меня нет на это времени.
Либо обратитесь на форумы по Excel (там наверняка помогут, и бесплатно),
либо оформляйте заказ (сделаю сам, но уже за денежку)

А если функцию LoadArrayFromTextFile дополнить помимо "Optional ByVal FirstRow&" ещё и возможностью загрузки требуемого количества строк из файла - цены бы ей не было !!!!

Так он же (vbNewLine) уже прописан был в коде функции?,здесь приведённой? А с символом vbLf - совсем другой результат. Правда ни в первом, ни во-втором случае результат не соответствует действительности. А где можно почитать об этих аргументах? ColumnsSeparator$ и RowsSeparator$ ???

Спасибо большое за ответ.Я потом нашла эту переменную. Ваш код очень полезен для меня (после долгого перерыва в работе с VBA). Поэтому у меня возникли еще вопросы по работе функции (м.б. дилетантские, прошу прощения):
- обрабатываемый csv-файл достаточно большой (до 30 тыс. строк). Количество столбцов определяется правильно, но количество строк - в несколько раз меньше. Дело в кодировке csv (USC2)? Попробовала вывести то, что считалось из файла в массив, на лист Эксель - данных нет. Тогда как из переменной txt определилась размерность массива?

Скорее всего, вы в коде прописали не тот разделитель строк
не обязательно строки разделяются символом vbNewLine - может быть символ vbLf

Здравствуйте, подскажите пжлст, по какой причине может не работать разделение на строки, т.е. tmpArr1 = Split(txt, RowsSeparator$).

Помогите запустить данное чудо, у меня почему то не получается. Что куда нужно сохранить?

Должен заметить, что если в первой строке количество разделителей меньше, чем в последующих, то размер массива ограничивается именно числом, определенным по первой строке. М-да. В моем случае такая функция не работает. :(

Спасибо, Алексей, за замечание.
Исправил код в статье.

Только написал For j = 1 To UBound(tmpArr2) + 1
(так правильнее)

Функция хороша и быстро работает. Спасибо автору.

Однако если CSV/TXT- файл имеет строки разной длины, и, соотв., разное число разделителей в строке, например:

FIO;OKLAD;SUMMA
Ян;3000;3000
Ун;3000
Он;3000;5000

- то возникнет на Ун ошибка, т.к. цикл

For j = 1 To ColumnsCount

использует ColumnsCount, взятый из первой строки - а там, как правило, поля (то есть эта строка всегда длиннее всех других и имеет все разделители)

Если написать так:

For j = 1 To UBound(tmpArr2) ' а у автора было "To ColumnsCount", что вело к ошибке на файлах массивах с короткими строками

- то функция станет универсальней. Пустые элементы будут иметь значение Empty, что нужно учитывать в дальнейшем. Их можно сделать равными нулю, пустому тексту "" итп.

Отправить комментарий

Содержание этого поля является приватным и не предназначено к показу.
CAPTCHA
Подтвердите, пожалуйста, что вы - человек:
               ____    _   _   ____    _     
___ ____ | _ \ | \ | | / ___| | |
/ __| |_ / | |_) | | \| | \___ \ | |
| (__ / / | __/ | |\ | ___) | | |___
\___| /___| |_| |_| \_| |____/ |_____|
Введите код, изображенный в стиле ASCII-арт.

Не получается применить макрос? Не удаётся изменить код под свои нужды?

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