MyTetra Share
Делитесь знаниями!
Извлечение таблиц из pdf (в excel), прив. w/vba
Время создания: 31.07.2019 22:37
Раздел: Разные закладки - VBA
Запись: xintrea/mytetra_db_adgaver_new/master/base/1523183464v9kbdgqmdi/text.html на raw.githubusercontent.com

Извлечение таблиц из pdf (в excel), прив. w/vba

Я пытаюсь извлечь таблицы из pdf файлов с помощью vba и экспортировать их в excel. Если все работает так, как должно, оно должно идти автоматически. Проблема в том, что таблица не стандартизирована.

Это то, что у меня есть до сих пор.

  • VBA (Excel) запускает XPDF и преобразует все .pdf файлы, найденные в текущей папке, в текстовый файл.
  • VBA (Excel) читает каждый текстовый файл по строкам.

И код:

With New Scripting.FileSystemObject

With .OpenTextFile(strFileName, 1, False, 0)


If Not .AtEndOfStream Then .SkipLine

Do Until .AtEndOfStream

//do something

Loop

End With

End With


Все это прекрасно работает. Но теперь я сталкиваюсь с проблемой извлечения таблиц из текстовых файлов.То, что я пытаюсь сделать, это VBA, чтобы найти строку, например. "Year Income", а затем вывести данные после него в столбцы. (Пока таблица не закончится.)

Первая часть не очень сложна (найдите определенную строку), но как бы я обошел вторую часть. Текстовый файл будет выглядеть как этот Pastebin . Проблема в том, что текст не стандартизирован. Таким образом, например, некоторые таблицы имеют 3-летние столбцы (2010 2011 2012), а некоторые только две (или 1), некоторые таблицы имеют больше пробелов между столбцом, а некоторые не включают определенные строки (такие как Capital Asset, net).

Я думал о том, чтобы делать что-то подобное, но не уверен, как это сделать в VBA.

  • Найти строку, определенную пользователем. например. "Таблица 1: Возврат лет".
  • а. Следующая строка найти годы; если есть два, нам понадобятся три столбца на выходе (заголовки +, 2x год), если их три, нам понадобятся четыре (титры +, 3 раза в год).. и т.д.
    б. Создайте столбец столбца + столбец за каждый год.
  • При достижении конца строки перейдите к следующей строке
  • а. Читать текст → вывод в столбец 1.
    б. Распознавать пробелы (пробелы > 3?) Как начало столбца 2. Чтение чисел → вывод в столбец 2.
    с. (если столбец = 3) Признать пробелы как начало столбца 3. Чтение чисел → вывод в столбец 3.
    д. (если столбец = 4) Распознавать пробелы как начало столбца 4. Считать числа → вывод в столбец 4.
  • Каждая строка, цикл 4.
  • Следующая строка не включает числа - Конечная таблица. (возможно, easiet только пользовательский номер, после 15 символов нет номера конечной таблицы)

Я основывал свою первую версию на Pdf, чтобы преуспеть , но чтение онлайн-людей не рекомендуется OpenFile, а скорее FileSystemObject (хотя это, кажется, намного медленнее).

Любые указатели, чтобы начать меня, главным образом на шаге 2?


2 ответов

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

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

  • Вы можете использовать логические значения, чтобы помочь вам определить, какой "раздел" текстового файла вы находитесь. Т.е. используйте InStr в текущей строке, чтобыопределите, что вы находитесь в таблице, ища текст "Таблица", а затемкак только вы знаете, что находитесь в разделе "Таблица" начала файлаищет раздел "Активы" и т.д.
  • Вы можете использовать несколько методов для определения количества лет (или столбцов), которое у вас есть. Функция Split вместе с циклом будетработа.
  • Если ваши файлы всегда имеют постоянное форматирование, даже в определенных частях, вы можете воспользоваться этим. Например, если вы знаетестрока файла всегда будет иметь знак доллара перед ними, затемвы знаете, что это определит ширину столбцов, и вы можете использовать это напоследующие строки текста.

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

Sub ReadInTextFile()

Dim fs As Scripting.FileSystemObject, fsFile As Scripting.TextStream

Dim sFileName As String, sLine As String, vYears As Variant

Dim iNoColumns As Integer, ii As Integer, iCount As Integer

Dim bIsTable As Boolean, bIsAssets As Boolean, bIsLiabilities As Boolean, bIsNetAssets As Boolean


Set fs = CreateObject("Scripting.FileSystemObject")

sFileName = "G:\Sample.txt"

Set fsFile = fs.OpenTextFile(sFileName, 1, False)


'Loop through the file as you've already done

Do While fsFile.AtEndOfStream <> True

'Determine flag positions in text file

sLine = fsFile.Readline


Debug.Print VBA.Len(sLine)


'Always skip empty lines (including single spaceS)

If VBA.Len(sLine) > 1 Then


'We've found a new table so we can reset the booleans

If VBA.InStr(1, sLine, "Table") > 0 Then

bIsTable = True

bIsAssets = False

bIsNetAssets = False

bIsLiabilities = False

iNoColumns = 0

End If


'Perhaps you want to also have some sort of way to designate that a table has finished. Like so

If VBA.Instr(1, sLine, "Some text that designates the end of the table") Then

bIsTable = False

End If


'If we're in the table section then we want to read in the data

If bIsTable Then

'Check for your different sections. You could make this constant if your text file allowed it.

If VBA.InStr(1, sLine, "Assets") > 0 And VBA.InStr(1, sLine, "Net") = 0 Then bIsAssets = True: bIsLiabilities = False: bIsNetAssets = False

If VBA.InStr(1, sLine, "Liabilities") > 0 Then bIsAssets = False: bIsLiabilities = True: bIsNetAssets = False

If VBA.InStr(1, sLine, "Net Assests") > 0 Then bIsAssets = True: bIsLiabilities = False: bIsNetAssets = True


'If we haven't triggered any of these booleans then we're at the column headings

If Not bIsAssets And Not bIsLiabilities And Not bIsNetAssets And VBA.InStr(1, sLine, "Table") = 0 Then

'Trim the current line to remove leading and trailing spaces then use the split function to determine the number of years

vYears = VBA.Split(VBA.Trim$(sLine), " ")

For ii = LBound(vYears) To UBound(vYears)

If VBA.Len(vYears(ii)) > 0 Then iNoColumns = iNoColumns + 1

Next ii


'Now we can redefine some variables to hold the information (you'll want to redim after you've collected the info)

ReDim sAssets(1 To iNoColumns + 1, 1 To 100) As String

ReDim iColumns(1 To iNoColumns) As Integer

Else

If bIsAssets Then

'Skip the heading line

If Not VBA.Trim$(sLine) = "Assets" Then

'Increment the counter

iCount = iCount + 1


'If iCount reaches it limit you'll have to redim preseve you sAssets array (I'll leave this to you)

If iCount > 99 Then

'You'll find other posts on stackoverflow to do this

End If


'This will happen on the first row, it'll happen everytime you

'hit a $ sign but you could code to only do so the first time

If VBA.InStr(1, sLine, "$") > 0 Then

iColumns(1) = VBA.InStr(1, sLine, "$")

For ii = 2 To iNoColumns

'We need to start at the next character across

iColumns(ii) = VBA.InStr(iColumns(ii - 1) + 1, sLine, "$")

Next ii

End If


'The first part (the name) is simply up to the $ sign (trimmed of spaces)

sAssets(1, iCount) = VBA.Trim$(VBA.Mid$(sLine, 1, iColumns(1) - 1))

For ii = 2 To iNoColumns

'Then we can loop around for the rest

sAssets(ii, iCount) = VBA.Trim$(VBA.Mid$(sLine, iColumns(ii) + 1, iColumns(ii) - iColumns(ii - 1)))

Next ii


'Now do the last column

If VBA.Len(sLine) > iColumns(iNoColumns) Then

sAssets(iNoColumns + 1, iCount) = VBA.Trim$(VBA.Right$(sLine, VBA.Len(sLine) - iColumns(iNoColumns)))

End If

Else

'Reset the counter

iCount = 0

End If

End If

End If


End If

End If

Loop


'Clean up

fsFile.Close

Set fsFile = Nothing

Set fs = Nothing

End Sub

Я не могу проверить образцы данных, поскольку PasteBin был удален. Основываясь на том, что я могу почерпнуть из описания проблемы, мне кажется, что использование регулярных выражений упростит анализ данных.

Добавьте ссылку на скрипт Runtime scrrun.dll для FileSystemObject.
Добавьте ссылку на регулярные выражения Microsoft VBScript 5.5. библиотека для объекта RegExp.

Создайте экземпляр объекта RegEx с помощью   Dim objRE As New RegExp

Задайте для свойства Pattern значение "(\ bd {4}\b) {1,3}" Вышеупомянутый шаблон должен соответствовать строкам, содержащим строки, такие как:20102010 20112010 2011 2012

Число пробелов между строками года не имеет значения, если существует хотя бы один (поскольку мы не ожидаем, что будем сталкиваться с такими строками, как 201020112012)

Установите для свойства Global значение True

Захваченные группы будут найдены в отдельных объектах Match из MatchCollection, возвращенных методом Execute объекта OBRERE объекта RegEx. Поэтому объявите соответствующие объекты:

Dim objMatches as MatchCollection

Dim objMatch as Match

Dim intMatchCount 'tells you how many year strings were found, if any


Предполагая, что вы создали объект FileSystemObject и просматриваете текстовый файл, считывая каждую строку в переменной strLine

Первый тест, чтобы увидеть, содержит ли текущая строка искомый шаблон:

If objRE.Test(strLine) Then

'do something

Else

'skip over this line

End If


Set objMatches = objRe.Execute(strLine)

intMatchCount = objMatches.Count


For i = 0 To intMatchCount - 1

'processing code such as writing the years as column headings in Excel

Set objMatch = objMatches(i)

e.g. ActiveCell.Value = objMatch.Value

'subsequent lines beneath the line containing the year strings should

'have the amounts, which may be captured in a similar fashion using an

'additional RegExp object and a Pattern such as "(\b\d+\b){1,3}" for

'whole numbers or "(\b\d+\.\d+\b){1,3}" for floats. For currency, you

'can use "(\b\$\d+\.\d{2}\b){1,3}"

Next i


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

Так же в этом разделе:
 
MyTetra Share v.0.65
Яндекс индекс цитирования