Что нового

Парсер CSV

dumoed

Знающий
Сообщения
34
Репутация
5
Версия AutoIt
3.3.14.0
Версия
3.3.14.0
При использовании _FileReadToArray для парсинга CSV файлов в массив столкнулся с ошибкой. Если в какой-либо из строк исходной таблицы не хватает элементов, либо присутствует лишний, скрипт вываливается.
Одним словом, функция _FileReadToArray работает только с "правильными" файлами.

Первое решение проверяет существует ли очередной элемент массива и если нет, оставляет в массиве пустое место, выглядит оно так:

Код:
#include <MsgBoxConstants.au3>
#include <FileConstants.au3>
#include <array.au3>
#include <String.au3>
#include <File.au3>


Func _elementExists($array, $element) ; function to check if array element exists
    If $element > Ubound($array)-1 Then Return False ; element is out of the array bounds
Return true ; element is in array bounds
EndFunc

Func CSVParserDotComma($FileOpen) ; parser
    Dim $aArray
    _FileReadToArray($FileOpen, $aArray);загоняем файл в одномерный массив построчно, в нулевом элементе число строк
    $columnsCounter = stringsplit($aArray[1],';');  определяем количество столбцов, разделитель ';'
    Dim $aResult[$aArray[0] + 1][$columnsCounter[0] +1 ];объявляем массив с уже известным количеством строк и столбцов, в нулевых элементах содержится вся нужная информация
        For $x = 1 to ($aArray[0]);заполняем итоговый массив, строки
        $oneRow = stringsplit($aArray[$x],';'); разбиваем каждую строку в имеющемся одномерном массиве
                    For $y = 1 to ($columnsCounter[0]);заполняем итоговый массив, столбцы
                        if _elementExists($oneRow, $y) then; проверяем на наличие очередного элемента в столбце;вариант парсера с проверкой на наличие элемента
                            $aResult[$x][$y] = $oneRow[$y];
                            ;MsgBox (1,1, 'row='&$x )
                            ;$aResult[$x][$y] = $oneRow[$y]; вариант парсера без проверки на наличие элемента
                        EndIf
                    Next
        Next
Return $aResult
EndFunc


$FileOpen = "table.csv" 
Dim $aResult
CSVParserDotComma($FileOpen)

_ArrayDisplay($aResult)


Первый вариант достаточно быстрый.

Второй вариант реализовал через функцию _ArrayAdd, при работе с большими массивами он более долгий и в нем в случае ошибки (имеется ввиду - несовпадение количества элементов с эталонным) пропускается вся строка :

Код:
#include <MsgBoxConstants.au3>
#include <FileConstants.au3>
#include <array.au3>
#include <String.au3>
#include <File.au3>


Func CSVParserDotComma($FileOpen) ; parser
    Dim $aArray
    _FileReadToArray($FileOpen, $aArray);загоняем файл в одномерный массив построчно, в нулевом элементе число строк
    $columnsCounter = stringsplit($aArray[1],';');  определяем эталонное количество столбцов, разделитель ';'
    Dim $aResult[1][$columnsCounter[0] +1 ];объявляем из одной строки массив с уже известным количеством столбцов
        For $x = 1 to ($aArray[0]);заполняем итоговый массив, строки
    local    $oneRow = stringsplit($aArray[$x],';'); разбиваем каждую строку в имеющемся одномерном массиве
                If $oneRow[0]=$columnsCounter[0] then; если эталонное количество столбцов и текущее совпадают
                    _ArrayAdd($aResult, $aArray[$x], 1, ";") ;то добавляем всю строку в новый массив, начиная с 1го столбца, разделитель столбцов ";"
                Else
                    ;MsgBox(1,1,"ошибка в строке "&$x)
                EndIf
        Next
Return $aResult
EndFunc


$FileOpen = "table.csv";
Dim $aResult
CSVParserDotComma($FileOpen)

_ArrayDisplay($aResult)
 
Последнее редактирование:

Norm

Продвинутый
Сообщения
279
Репутация
74
Тоже это постоянно раздражало, но после небольшой переделки функции в самой библиотеке забйыл об этой проблеме.
Решил немного проще и добавил ещё поддержку дескрипторов от функции FileOpen().
Также добавил отображение прогресса (это закоментировал), если файлы большие.

Код:
Func _FileReadToArray($sFilePath, ByRef $vReturn, $iFlags = $FRTA_COUNT, $sDelimiter = "")
    ; Clear the previous contents
    $vReturn = 0

    If $iFlags = Default Then $iFlags = $FRTA_COUNT
    If $sDelimiter = Default Then $sDelimiter = ""

    ; Set "array of arrays" flag
    Local $bExpand = True
    If BitAND($iFlags, $FRTA_INTARRAYS) Then
        $bExpand = False
        $iFlags -= $FRTA_INTARRAYS
    EndIf
    ; Set delimiter flag
    Local $iEntire = $STR_CHRSPLIT
    If BitAND($iFlags, $FRTA_ENTIRESPLIT) Then
        $iEntire = $STR_ENTIRESPLIT
        $iFlags -= $FRTA_ENTIRESPLIT
    EndIf
    ; Set row count and split count flags
    Local $iNoCount = 0
    If $iFlags <> $FRTA_COUNT Then
        $iFlags = $FRTA_NOCOUNT
        $iNoCount = $STR_NOCOUNT
    EndIf

    ; Check delimiter
    If $sDelimiter Then
        ; Read file into an array
        Local $aLines = FileReadToArray($sFilePath)
        If @error Then Return SetError(@error, 0, 0)

        ; Get first dimension and add count if required
        Local $iDim_1 = UBound($aLines) + $iFlags
        ; Check type of return array
        If $bExpand Then ; All lines have same number of fields
            ; Count fields in first line
            Local $iDim_2 = UBound(StringSplit($aLines[0], $sDelimiter, $iEntire + $STR_NOCOUNT))
            ; Size array
            Local $aTemp_Array[$iDim_1][$iDim_2]
            ; Declare the variables
            Local $nN, $iFields, $aSplit
;            ProgressOn("Загрузка ", "Ожидание...", "",-1,-1,16)
;            Local $nPr = 0, $nZE = 0, $nDiff = Ceiling(($iDim_1-$iFlags)/100)
            ; Loop through the lines
            For $i = 0 To $iDim_1 - $iFlags - 1
                ; Split each line as required
                $aSplit = StringSplit($aLines[$i], $sDelimiter, $iEntire + $STR_NOCOUNT)
                ; Count the items
                $iFields = UBound($aSplit)
                If $iFields < $iDim_2 Then
                    For $nN = $iFields - 1 to $iDim_2 - 1
                        _ArrayAdd($aSplit, "")
                    Next
                ElseIf $iFields > $iDim_2 Then
                    ReDim $aTemp_Array[$iDim_1][$iFields]
                EndIf
                ; Fill this line of the array
                For $j = 0 To $iFields - 1
                    $aTemp_Array[$i + $iFlags][$j] = $aSplit[$j]
                Next
;                $nZE = Floor($i / $nDiff)
;                If $nPr < $nZE Then
;                    $nPr = $nZE
;                    ProgressSet($nPr, "Обработано "& $nPr &" %")
;                EndIf
            Next
;            ProgressOff()
            ; Check at least 2 columns
            If $iDim_2 < 2 Then Return SetError(4, 0, 0)
            ; Set dimension count
            If $iFlags Then
                $aTemp_Array[0][0] = $iDim_1 - $iFlags
                $aTemp_Array[0][1] = $iDim_2
            EndIf
        Else ; Create "array of arrays"
            ; Size array
            Local $aTemp_Array[$iDim_1]
            ; Loop through the lines
            For $i = 0 To $iDim_1 - $iFlags - 1
                ; Split each line as required
                $aTemp_Array[$i + $iFlags] = StringSplit($aLines[$i], $sDelimiter, $iEntire + $iNoCount)
            Next
            ; Set dimension count
            If $iFlags Then
                $aTemp_Array[0] = $iDim_1 - $iFlags
            EndIf
        EndIf
        ; Return the array
        $vReturn = $aTemp_Array
    Else ; 1D
        If $iFlags Then
            If Not IsInt($sFilePath) Then
                Local $hFileOpen = FileOpen($sFilePath, $FO_READ)
            Else
                $hFileOpen = $sFilePath
            EndIf
            If $hFileOpen = -1 Then Return SetError(1, 0, 0)
            Local $sFileRead = FileRead($hFileOpen)
            FileClose($hFileOpen)

            If StringLen($sFileRead) Then
                $vReturn = StringRegExp(@LF & $sFileRead, "(?|(\N+)\z|(\N*)(?:\R))", $STR_REGEXPARRAYGLOBALMATCH)
                $vReturn[0] = UBound($vReturn) - 1
            Else
                Return SetError(2, 0, 0)
            EndIf
        Else
            $vReturn = FileReadToArray($sFilePath)
            If @error Then
                $vReturn = 0
                Return SetError(@error, 0, 0)
            EndIf
        EndIf

    EndIf
    Return 1
EndFunc   ;==>_FileReadToArray
 
Автор
D

dumoed

Знающий
Сообщения
34
Репутация
5
удобное решение, забрал в коллекцию
 

joiner

Модератор
Локальный модератор
Сообщения
3,556
Репутация
628
Автор, просьба, в этом разделе публикуются проекты. Содержание вашей темы больше похожа на вопрос.
Проблемы функций решаем в других темах.
 
Верх