Проблемы с разбором файла excel с vba - PullRequest
0 голосов
/ 30 января 2012

Я новичок в VBA, и у меня возникла следующая проблема.

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

Под этим я подразумеваю, что для ex.У меня есть номер дома N10 в ячейке (4: ABC) , и под этой ячейкой у меня есть случайное число плоских чисел.То же самое относится и к дому № 11, который находится в ячейке (4: DEF) , и снова под случайным числом квартир.Мне нужно привести эти данные в структурированном виде, где номер квартиры будет расположен в одном столбце с соответствующим номером дома в следующем столбце. Другая проблема заключается в том, что в одном документе есть около 15 листов с аналогичными данными, и есть около20 таких первоклассных документов.Итак, большое количество данных необходимо объединить в 1 структурированный документ.

Я не знаю, с чего начать.Разобрать эти данные довольно сложно, хотя я предполагаю, что должен использовать регулярные выражения.Что помогает, так это то, что номер дома состоит из 2 цифр, тогда как плоские номера имеют почти все одинаковый формат (5 цифр и буква).поэтому я предполагаю, что могу пройти через каждую ячейку, и те, которые соответствуют регулярному выражению, записывают в новый документ, но тогда возникнет проблема с совпадением с номером дома?Пожалуйста, кто-нибудь?любые мысли ...

Мой ввод:

   |  A   |  B   |  C   |  D   |  E   |  F   |       | ...   | N    |
  1|             Header                      |       | ...   |      |
  2|             Header  N2                  |       | ...   |      |
  3|             Header N3                   |       | ...   |      |
  4|         N9/10      |      |      N11/12         | ...
  5|Smith               |      |Jones |Tim   |       | ...
  6|Green               |      |Singh |Roth  |       | ...
  7|Abbott              |      |Patel |              | ...
  8|11111a|22222a|33333a|      |22222a|33333a| 44444c|
  9|11111b|22222b|33333b|      |22222b|33333b| 44443d|
   :
 21|11111u|22222u|33333u|      |22222u|33333u| 44444e|
 22|      |22222v|33333v|      |22222v|33333v| 77777e|
 23|      |      |33333w|      |      |      |       | 
 24|      |      |33333x|      |      |      |       |
   :
                B L A N K          CELLS
   .                                            .
   .                                            .
 31|       N375/376     |      |  N96/85     |
 32|Smith               |      |Jones |Tim   |       | ...
 33|Green               |      |Singh |Roth  |       | ...
 34|Abbott              |      |Patel |              | ...
 35|11111a|22222a|33333a|      |22222a|33333a| 44444c|
 36|11111b|22222b|33333b|      |22222b|33333b| 44443d|
   :
 45|11111u|22222u|33333u|      |22222u|33333u| 44444e|
 46|      |22222v|33333v|      |22222v|33333v| 77777e|
 47|      |      |33333w|      |      |33333w| 

Ответы на ваши вопросы:

  1. В строках с 1 по 3 только заголовки документа, названиегорода, региона (совсем не важно)
  2. Фамилии не соответствуют количеству квартир, и я не знаю почему.Эта информация для меня не важна
  3. строки 25-30 - пусто, 45-50 пусто, 61-64 - пусто, 69-78 - пусто, а в некоторых случаях 88 - 106 также пусто, в некоторых случаяхДокумент заканчивается 87
  4. Ряды разные, вот в чем проблема.Например, вы можете видеть, что под домом 11/12 ряды имеют одинаковую длину, но это просто совпадение.под каждым домом нет определенной длины строк
  5. да, есть пустые ячейки, как объяснено в 3.
  6. Я заметил, что во всех документах последний использованный столбец - N
  7. Я игнорирую фамилии, потому что они мне не нужны

Что я хочу:

 |  A   |  B   |  C   | 
1|11111a|N9/10 |      | 
2|11111b|N9/10 |      |
3|11111c|N9/10 |      | 
 : 
x|11111a|N11/12|      |
x|11111b|N11/12|      |
x|11111c|N11/12|      |

1 Ответ

0 голосов
/ 30 января 2012

Это должно помочь вам начать.Мне было сложно тестировать мой код, но я надеюсь, что ошибок не слишком много.Я включил комментарии, объясняющие цель каждого раздела, но я не знаю, достаточно ли вы знаете VBA, чтобы понять, как я достигаю этой цели.

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

В этой папке мне нужно, чтобы вы создали новую рабочую книгу с именем что-то вроде «Consolidate.xls»,(Я использую Excel 2003, у вас может быть другое расширение.)

Откройте Consolidate.xls, затем откройте редактор VB.(Я предполагаю базовые знания макросов Excel. При необходимости вам придется подождать, пока я встану. Вы на шесть часов впереди меня, а я на пенсии, поэтому я не встаю на рассвет.)

Создайте модуль и скопируйте в него код ниже.

Запустить макрос FillSourceSheets.Это создаст рабочий лист «SourceSheets» и заполнит его именами рабочих книг и рабочих листов.В моей системе это выглядело так:

 |   A  |       B             |     C     |  D   |   E  | 
 |Status|Source workbook      |Source worksheets -->    |       
 |      |Consolidate.txt      |Consolidate|      |      |
 |      |Test Parse data 1.xls|Sheet1     |Sheet2|Sheet3|
 |      |Test Parse data 2.xls|Sheet1     |Sheet4|Sheet2|Sheet3|

Пока не беспокойтесь о столбце «Состояние».

Имена в «Исходной книге» - это файлы, которые может открыть Excel.Обратите внимание, что он может открыть текстовый файл.«Test Parse data 1.xls» и «Test Parse data 2.xls» - мои рабочие тетради.Рабочие таблицы в каждом файле перечислены в найденной последовательности.

Вам необходимо удалить все строки для файлов, которые не являются исходными файлами.Мне пришлось удалить строку для «Consolidate.txt».Вам необходимо удалить имя любого листа, который не содержит исходных данных.В моем случае «Sheet4» из «Test Parse data 2.xls» не содержит исходных данных, и мне пришлось их удалить.В них не должно быть никаких пробелов.Строка, в столбце B которой ничего нет, завершает список.Ячейка, в которой ничего нет, заканчивается рядом.Поэтому после редактирования моего листа это выглядело следующим образом:

 |   A  |       B             |     C     |  D   |   E  | 
 |Status|Source workbook      |Source worksheets -->    |       
 |      |Test Parse data 1.xls|Sheet1     |Sheet2|Sheet3|
 |      |Test Parse data 2.xls|Sheet1     |Sheet2|Sheet3|

Этот список определяет другие макросы.Он сообщает им, какие книги и листы нужно посмотреть.

Другой макрос, который я включил, - ValidateSheets

У вас есть 20 книг, из которых одна содержит 15 листов.Будет удивительно, если ни одна из них не содержит ошибок, и будет удивительно, если некоторые не будут соответствовать формату, который вы мне дали.ValidateSheets выполняет проверку первого уровня, потому что нет цели делать больше, пока мы точно не знаем, что у нас есть.

ValidateSheets работает со списком рабочих книг и рабочих таблиц в «SourceSheets».Он создает текстовый файл «Process Report.txt», в котором отображается его ход.Если он находит ошибку в «SourceSheets», он немедленно останавливается с сообщением на экране.В «SourceSheets» не должно быть никаких ошибок, но если они есть, вам придется их исправить, перезапустите макрос.Если он находит ошибку в листе, он выводит сообщение об ошибке в «Process Report.txt» и переходит к следующему листу.

Попробуйте запустить ValidateSheets и посмотрите, как вы продвигаетесь.

Option Explicit
Sub FillSourceSheets()

  Dim ColCrnt As Long
  Dim ErrMsg As String
  Dim Filename As String
  Dim InxWSheet As Long
  Dim PathCrnt As String
  Dim RowCrnt As Long
  Dim WBookOther As Workbook
  Dim WBookThis As Workbook

  If Workbooks.Count > 1 Then
    ' It is easy to get into a muddle if there are multiple workbooks
    ' open at the start of a macro like this.  Avoid the problem.
    Call MsgBox("Please close all other workbooks", vbOKOnly)
    Exit Sub
  End If

  ' Record this workbook so we do not confuse it with any we open
  Set WBookThis = ActiveWorkbook
  ' Record the folder containing the current workbook
  PathCrnt = ActiveWorkbook.Path

  ' Create a new worksheet, name it SourceSheets and fill the heading row
  Sheets.Add
  With ActiveSheet
    .Name = "SourceSheets"
    .Range("A1").Value = "Status"
    .Range("B1").Value = "Source workbook"
    .Range("C1").Value = "Source worksheets -->"
    .Range("C1:E1").MergeCells = True
    .Range("A1:C1").Font.Bold = True
  End With
  RowCrnt = 2

  Filename = Dir$(PathCrnt & "\*.*")
  ' Loop for every file in the activeworkbook's folder
  Do While Filename <> ""
    If Filename <> ActiveWorkbook.Name Then
      ' This file is not the active workbook
      ' so try to open it as a workbook.
      Err.Clear
      ErrMsg = ""
      On Error Resume Next
      Set WBookOther = Workbooks.Open(PathCrnt & "\" & Filename)
      If Err.Number <> 0 Then
        ' On Error GoTo 0 clears Err.Num and Err.Description so save
        ErrMsg = Err.Number & " " & Err.Description
      End If
      On Error GoTo 0
      If ErrMsg <> "" Then
        ' This file cannot be opened by Excel
        Debug.Print Filename & " " & ErrMsg
      Else
        ' This file has been successfully opened.  Create a row for it.
        ' Start by placing the file name in column 2.
        WBookThis.Sheets("SourceSheets").Cells(RowCrnt, 2).Value = Filename
        ColCrnt = 3
        ' Place each sheet name in a cell starting from column 3
        For InxWSheet = 1 To WBookOther.Worksheets.Count
          WBookThis.Sheets("SourceSheets").Cells(RowCrnt, ColCrnt).Value = _
                                    WBookOther.Worksheets(InxWSheet).Name
          ColCrnt = ColCrnt + 1
        Next
        WBookOther.Close SaveChanges:=False
        RowCrnt = RowCrnt + 1
      End If
    End If
    Filename = Dir$       ' Get next file name
  Loop
  With WBookThis.Sheets("SourceSheets")
    .Columns.AutoFit
  End With

End Sub
Sub ValidateSheets()

  Dim CellValue As String
  Dim ColSrcList As Long
  Dim ColSrcSheetCrnt As Long
  Dim ColSrcSheetLast As Long
  Dim Found As Boolean
  Dim InxWSheetCrnt As Long
  Dim OutputFileNum As Integer
  Dim PathCrnt As String
  Dim Rng As Range
  Dim RowSrcList As Long
  Dim RowSrcSheetBlockStart As Long
  Dim RowSrcSheetCrnt As Long
  Dim RowSrcSheetFinal As Long
  Dim WBookOtherNameCrnt As String
  Dim WSheetOtherNameCrnt As String
  Dim WBookOther As Workbook
  Dim WBookThis As Workbook

  If Workbooks.Count > 1 Then
    ' It is easy to get into a muddle if there are multiple workbooks
    ' open at the start of a macro like this.  Avoid the problem.
    Call MsgBox("Please close all other workbooks", vbOKOnly)
    Exit Sub
  End If

  ' Record this workbook so we do not confuse it with any we open
  Set WBookThis = ActiveWorkbook
  ' Record the folder containing the current workbook
  PathCrnt = ActiveWorkbook.Path
  ' Open text file to which progress messages will be written
  OutputFileNum = FreeFile
  Open PathCrnt & "\Process Report.txt" For Output Lock Write As #OutputFileNum

  With WBookThis.Sheets("SourceSheets")
    ' Load name of first workbook and first worksheet
    RowSrcList = 2      ' Row of first workbook
    ColSrcList = 3      ' Column of first worksheet
    WBookOtherNameCrnt = .Cells(RowSrcList, 2).Value
    WSheetOtherNameCrnt = .Cells(RowSrcList, ColSrcList).Value
  End With

  ' This loop repeats for each worksheet listed in worksheet SourceSheets
  Do While True
    ' WBookOtherNameCrnt and WSheetOtherNameCrnt have been loaded either
    ' before this loop or by the code at the end
    If Not WBookOther Is Nothing Then
      ' There is an open workbook.  Check it is the one
      ' required for this loop.
      If LCase(WBookOtherNameCrnt) <> LCase(WBookOther.Name) Then
        ' This is not the same workbook.
        ' Close the open workbook and clear reference to it
        WBookOther.Close SaveChanges:=False
        Set WBookOther = Nothing
      End If
    End If
    If WBookOther Is Nothing Then
      ' The workbook to be tested is not open so we need to
      ' open it.  First check it exists
      If Dir$(PathCrnt & "\" & WBookOtherNameCrnt) <> "" Then
        ' The specified file exists but it may not be a valid workbook.
        ' Use Excel's error handling
        Err.Clear
        On Error Resume Next
        Set WBookOther = Workbooks.Open(PathCrnt & "\" & WBookOtherNameCrnt)
        On Error GoTo 0
        If Err.Number <> 0 Then
          Call MsgBox("Open of """ & WBookOtherNameCrnt & """ failed. " & _
                  "Error: " & Err.Number & " " & Err.Description, vbOKOnly)
          Set WBookOther = Nothing
          Close OutputFileNum       ' Close text file
          Exit Sub
        End If
      Else
        Call MsgBox("I could not find workbook """ & WBookOtherNameCrnt _
                                     & """", vbOKOnly)
        Close OutputFileNum       ' Close text file
        Exit Sub
      End If
    End If
    ' The required workbook is open.
    With WBookOther

      ' Check the worksheet exists
      Found = False
      For InxWSheetCrnt = 1 To .Worksheets.Count
        If .Worksheets(InxWSheetCrnt).Name = WSheetOtherNameCrnt Then
          Found = True
          Exit For
        End If
      Next
      If Not Found Then
        ' The workbook was not found
        Call MsgBox("I could not find worksheet """ & WSheetOtherNameCrnt _
             & """ with workbook """ & WBookOtherNameCrnt & """", vbOKOnly)
        .Close
        Close OutputFileNum       ' Close text file
        Exit Sub
      End If

      Print #OutputFileNum, "Process sheet """ & WSheetOtherNameCrnt & _
            """ of workbook """ & WBookOther.Name & """"

      With Sheets(WSheetOtherNameCrnt)

        ' Validate source sheet matches expected format
        ' Rows 1 to 3 are ignored.

        ' Find final row of sheet
        Set Rng = .Cells.Find("*", .Range("A1"), _
                                       xlFormulas, , xlByRows, xlPrevious)
        If Rng Is Nothing Then
          ' The sheet is empty
          Print #OutputFileNum, "  Sheet is empty"
          Exit Do
        End If
        RowSrcSheetFinal = Rng.Row

        ' There are one of more blocks.  The first block starts in Row 4

        RowSrcSheetBlockStart = 4
        Do While True       ' Loop for each block
          ' Row 1 of a block must consider of one or more three cell merged
          ' areas.  Each merged area contains a string with value of format:
          ' "N" number "/" number.
          ' Search backwards from the column 1 of the next row
          ' for a cell with a value
          Set Rng = .Cells.Find("*", .Cells(RowSrcSheetBlockStart + 1, 1), _
                                            xlFormulas, , xlByRows, xlPrevious)
          If Rng Is Nothing Then
            ' This should not be possible because have
            ' already check for empty sheet
            Print #OutputFileNum, "  Sheet is empty"
            Exit Do
          End If
          If Rng.Row <> RowSrcSheetBlockStart Then
            Print #OutputFileNum, "  I was expecting a value on row " & _
                                                           RowSrcSheetBlockStart
            Exit Do
          End If
          ColSrcSheetLast = Rng.Column
          For ColSrcSheetCrnt = 1 To ColSrcSheetLast Step 3
            ' Check the three cells are merged
            If .Range(.Cells(RowSrcSheetBlockStart, ColSrcSheetCrnt), _
                 .Cells(RowSrcSheetBlockStart, ColSrcSheetCrnt + 2)).MergeCells _
                                                               = True Then
              If Not .Cells(RowSrcSheetBlockStart, ColSrcSheetCrnt).Value Like "N*/*" Then
                ' Cell does not contain "N" number "/" number
                Print #OutputFileNum, "  Row " & RowSrcSheetBlockStart & _
                        " is the start of a block. I was expecting " & _
                        "columns " & ColNumToCode(ColSrcSheetCrnt) & " to " & _
                        ColNumToCode(ColSrcSheetCrnt + 2) & " to contain a value with " & _
                        "the format ""N"" number ""/"" number"
                Exit Do
              End If
            Else
              ' Three cells are not merged
              Print #OutputFileNum, "  Row " & RowSrcSheetBlockStart & _
                    " is the start of a block. I was expecting " & _
                    "columns " & ColNumToCode(ColSrcSheetCrnt) & " to " & _
                    ColNumToCode(ColSrcSheetCrnt + 2) & " to be merged"
              Exit Do
            End If
          Next
          ' Cells with Rows 2 to 4 of a block must contain
          ' surnames or be empty.
          ' Check they do not contain flat numbers
          For RowSrcSheetCrnt = RowSrcSheetBlockStart + 1 To _
                                RowSrcSheetBlockStart + 3
            For ColSrcSheetCrnt = 1 To ColSrcSheetLast + 2
              CellValue = .Cells(RowSrcSheetCrnt, ColSrcSheetCrnt).Value
              If CellValue = "" Or Not LCase(CellValue Like "#####[a-z]") Then
                ' Cell valid
              Else
                ' Cell contains a flat number.  Cannot be a surname.
                Print #OutputFileNum, "  Row " & RowSrcSheetCrnt & _
                      " should only contain surnames but column " & _
                      ColNumToCode(ColSrcSheetCrnt) & " contains a flat number"
                Exit Do
              End If
            Next
          Next
          ' Rows 5 of a block to the next blank row should contain
          ' nothing but flat numbers.
          RowSrcSheetCrnt = RowSrcSheetBlockStart + 5
          Do While True     ' Loop until find a blank row
            Found = False  ' Nothing found on this row
            For ColSrcSheetCrnt = 1 To ColSrcSheetLast + 2
              CellValue = .Cells(RowSrcSheetCrnt, ColSrcSheetCrnt).Value
              If CellValue <> "" Then
                Found = True  ' Value found on this row
                If LCase(CellValue Like "#####[a-z]") Then
                  ' Cell valid
                Else
                  ' Cell does not contain a flat number
                  Print #OutputFileNum, "  Row " & RowSrcSheetCrnt & _
                        " should only contain flat numbers but column " & _
                        ColNumToCode(ColSrcSheetCrnt) & " contains " & CellValue
                  Exit Do
                End If
              End If
            Next
            If Not Found Then
              ' This is a blank line
              Exit Do
            End If
            RowSrcSheetCrnt = RowSrcSheetCrnt + 1
          Loop
          ' This block is finished.
          Print #OutputFileNum, "  No error found in block starting " & _
                                "on row " & RowSrcSheetBlockStart
          ' Is there another block?
          If RowSrcSheetCrnt > RowSrcSheetFinal Then
            ' No more blocks
            Exit Do
          Else
            ' Find the next row with a value which should be the first
            ' row of the next block.
            Set Rng = .Cells.Find("*", .Cells(RowSrcSheetCrnt, 1), _
                                          xlFormulas, , xlByRows, xlNext)
            If Rng Is Nothing Then
              ' This should not be possible since
              ' have already found a value on a later row
              Print #OutputFileNum, "  I expected another block under row " _
                          & RowSrcSheetCrnt & " but I could not find it."
              Exit Do
            End If
            RowSrcSheetBlockStart = Rng.Row
          End If
        Loop
      End With
    End With

    ' Load details of next worksheet
    With WBookThis.Sheets("SourceSheets")
      ColSrcList = ColSrcList + 1
      ' Load name of next worksheet
      WSheetOtherNameCrnt = .Cells(RowSrcList, ColSrcList).Value
      If WSheetOtherNameCrnt = "" Then
        ' There are no more worksheets on this row
        RowSrcList = RowSrcList + 1
        WBookOtherNameCrnt = .Cells(RowSrcList, 2).Value
        If WBookOtherNameCrnt = "" Then
          ' End of list reached.
          Exit Do
        End If
        ColSrcList = 3
        WSheetOtherNameCrnt = .Cells(RowSrcList, ColSrcList).Value
        If WSheetOtherNameCrnt = "" Then
          Call MsgBox("Row " & RowSrcList & " of SourceSheets has a " & _
                      "workbook name but no worksheet name.", vbOKOnly)
          Close OutputFileNum       ' Close text file
          Exit Sub
        End If
      End If
    End With
  Loop

  If Not WBookOther Is Nothing Then
    WBookOther.Close SaveChanges:=False
    Set WBookOther = Nothing
  End If

  Close OutputFileNum       ' Close text file

End Sub
Function ColCodeToNum(ColStg As String) As Long

  Dim lcColStg                  As String

  lcColStg = LCase(ColStg)
  ColCodeToNum = IIf(Len(ColStg) > 1, (Asc(Left(ColStg, 1)) - 64) * 26, 0) + _
                 Asc(Right(ColStg, 1)) - 64

End Function
Function ColNumToCode(ColNum As Long) As String

  ColNumToCode = IIf(ColNum > 26, Chr(64 + ((ColNum - 1) \ 26)), "") & _
                 Chr(65 + ((ColNum - 1) Mod 26))

End Function
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...