Nachdem ich vor einiger Zeit mal um Hilfe für die Sprachenauswahl zur COM-Programmierung gesucht habe, einige Tage später endlich ein paar Lösungsansätze gefunden habe, ist der Quälcode nun fertig.
Geschrieben habe ich es in VB.NET. Die Programmierer streiten sich sehr ausgelassen darüber, ob nun C# oder VB.NET die bessere Lösung ist.
Für meinen Teil haben beide Sprache ihre Berechtigung, wobei VB.NET nochmal schwieriger zu verstehen ist, wenn man schon C gelernt hatte.
Doch genug gequatscht, hier mal der Code:
Imports System.IO 'Ein / Ausgabe Imports System.Text.RegularExpressions 'Regex Imports Microsoft.Office.Interop 'Excel Module Module1 Dim objDateiLeser As StreamReader 'Übergabeparameter: Pfad zur PDF und zur Ausgabe.txt Sub Pdf2Txt(ByVal pdfFile As String, ByVal txtFile As String) Dim oProcess As Process Dim arguments As String = "-layout" & " " & pdfFile & " " & txtFile oProcess = System.Diagnostics.Process.Start("T:\LIEFERANTEN\Wieland\Auftragsbestätigungen\apps\pdf\pdftotext.exe", arguments) oProcess.WaitForExit() 'Warten bis pdf2text fertig.End Sub Sub Main() '============================================================================== 'ALLE PDF DATEIEN IN EINEM bestimmten VERZEICHNIS AUSLESEN UND IN TXT UMWANDELN Dim sPath As String = "T:\LIEFERANTEN\Wieland\Auftragsbestätigungen\" 'Pfadangabe Dim oDir As New DirectoryInfo(sPath) 'Directory Object von System.IO Dim oFiles As FileInfo() = oDir.GetFiles("*.pdf") 'Nur PDFs suchen Dim oFile As FileInfo 'Um später das Array oFiles auszelesen Dim pdf As String Dim i As Integer = 0 'Zähler für alle PDFs 'pdf2text aufrufen und Textdatei erzeugen For Each oFile In oFiles pdf = oFile.ToString Dim pdfverz As String = Chr(34) & sPath & pdf & Chr(34) 'Chr(34) für das Anführungszeichen Dim ausverz As String = sPath & "ausgabe" & i & ".txt" 'txt verzeichnis Call Pdf2Txt(pdfverz, ausverz) i = i + 1 Next Console.WriteLine("PDFs wurden in TXT umgewandelt. Nun folgt der BPNexT Export.") Dim j As Integer = i 'wird später noch gebraucht beim TXT AUSLESEN i = 0 'PDF ins Archiv verschieben For Each oFile In oFiles pdf = oFile.ToString File.Move(sPath & pdf, sPath & "\Archiv\" & pdf) i = i + 1 Next Console.WriteLine("PDFs wurden ins Archiv verschoben.") 'PDF ENDE '======== '========================================== 'bpStarter aufrufen und export.xls erzeugen Dim oProcess As Process Dim arguments As String = "-once" oProcess = System.Diagnostics.Process.Start("T:\LIEFERANTEN\Wieland\Auftragsbestätigungen\apps\bpstarter\bpStarter.exe", arguments) oProcess.WaitForExit() Console.WriteLine("export.xls wurde aus BüroPlus gezogen.") Console.WriteLine("REGEX Prozedur wird eingeleitet") 'bpStarter ENDE '============== '================================================================================================= 'EXCEL DEKLARIEREN, NOTWENDIGE ZÄHLER FÜR DIE EINZELNEN SPALTEN UND SPALTENÜBERSCHRIFTEN SCHREIBEN 'Zähler für Zeilen Dim zählerb As Integer = 2 Dim exWB As Excel.Workbook 'Datei Dim exWS As Excel.Worksheet 'Arbeitsblatt Dim exAPP As Excel.Application 'Excel Anwendung exAPP = New Excel.Application exAPP.Visible = False 'Excel unsichtbar / sichtbar exWB = exAPP.Workbooks.Add() 'Neue Datei erstellen exWS = exWB.ActiveSheet exAPP.DisplayAlerts = False 'Hinweise, dass die Datei bereits existiert unterdrücken und automatisch überschreiben 'Überschriften in Datei schreiben exWS.Range("A1").Value = "BL-Nummer" exWS.Range("B1").Value = "Auftragsnummer" exWS.Range("C1").Value = "Wieland-Nummer" exWS.Range("D1").Value = "h-team-Nr" exWS.Range("E1").Value = "PE" exWS.Range("F1").Value = "EK-Preis" exWS.Range("G1").Value = "Lieferdatum" 'ENDE EXCEL '========== '================== 'REGEX DEFINITIONEN 'Allgemeiner Matchstring Dim re1 As String = ".*?" 'Non-greedy match on filler 'BL-Nummer Beispiel: BL0413412 Dim re2 As String = "(BL[0-9]{7})" 'Word 1 Dim r As Regex = New Regex(re1 + re2, RegexOptions.IgnoreCase Or RegexOptions.Singleline) 'Auftragsnummer Dim re5 As String = "(Nummer: [0-9]{7,})" 'Word 4 Dim r3 As Regex = New Regex(re1 + re5, RegexOptions.IgnoreCase Or RegexOptions.Singleline) 'Wieland Artikelnummer Beispiel: 56.702.7053.0 Dim re3 As String = "((\w{1}\d{1}|[0-9]{2})\.[0-9]{3}\.[0-9]{4}\.[0-9]{1})" 'Word 2 Dim r1 As Regex = New Regex(re1 + re3, RegexOptions.IgnoreCase Or RegexOptions.Singleline) 'h-team Nummer Dim re4 As String = "(Ihre Artikelnummer: [0-9]{5,})" 'Word 3 Dim r2 As Regex = New Regex(re1 + re4, RegexOptions.IgnoreCase Or RegexOptions.Singleline) 'PE Dim re8 As String = "(?< =Positionsnetto.+ EUR.+) [0-9]{1,3}" 'word 7 Dim r6 As Regex = New Regex(re8, RegexOptions.IgnoreCase Or RegexOptions.Singleline) 'Preis Dim re7 As String = "(?<=Positionsnetto.+ ST\s+)([0-9]{1,3}.)*[0-9]{1,3},[0-9]{2}" 'word 5 Dim r4 As Regex = New Regex(re7, RegexOptions.IgnoreCase Or RegexOptions.Singleline) 'Liefertermin Dim re6 As String = "(?<=Liefertermin )(\(Tag\): \d{2}\.\d{2}\.\d{4})|(unbest){1}" 'word 6 Dim r5 As Regex = New Regex(re6, RegexOptions.IgnoreCase Or RegexOptions.Singleline) 'ENDE REGEX DEFINITIONEN '======================= '================================================================================== 'SCHLEIFE UM ALLE ERZEUGTEN TXT DATEIEN EINZULESEN UND REGEX DRÜBERLAUFEN ZU LASSEN For i = 0 To j - 1 ' Dim txt As String 'erzeugte Datei öffnen objDateiLeser = New StreamReader(sPath & "ausgabe" & i & ".txt") '=================================================================== 'SCHLEIFE UM VERGLEICHEN MIT REGEX AUF MUSTER UND SCHREIBEN IN EXCEL Do txt = objDateiLeser.ReadLine Dim m As Match = r.Match(txt) 'BL-Nummer Dim m3 As Match = r3.Match(txt) 'Auftragsnummer Dim m1 As Match = r1.Match(txt) 'Wieland Nummer Dim m2 As Match = r2.Match(txt) 'h-team Nummer Dim m6 As Match = r6.Match(txt) 'PE Dim m4 As Match = r4.Match(txt) 'Preis Dim m5 As Match = r5.Match(txt) 'Liefdatum Dim BL As String Dim Auftragsnr As String If (m.Success) Then 'BL-Nummer Dim word1 As Group = m.Groups(1) 'Console.WriteLine(word1.ToString) exWS.Range("A" & zählerb).Value = word1.ToString BL = word1.ToString End If If (m3.Success) Then 'Auftragsnummer Dim word4 As Group = m3.Groups(1) 'erste dreizehn Zeichen sprich "Nummer: " abschneiden Dim aufnr As String = word4.ToString.Remove(0, 13) exWS.Range("B" & zählerb).Value = aufnr Auftragsnr = aufnr End If If (m1.Success) Then 'Wieland Nummer Dim word2 As Group = m1.Groups(1) exWS.Range("C" & zählerb).Value = word2.ToString exWS.Range("A" & zählerb).Value = BL 'BL-Nummer in Spalte A kopieren exWS.Range("B" & zählerb).Value = Auftragsnr 'BL-Nummer in Spalte A kopieren zählerb = zählerb + 1 End If If (m2.Success) Then 'h-team Nummer Dim word3 As Group = m2.Groups(1) 'erste zwanzig Zeichen sprich "Ihre Artikelnummer: " abschneiden Dim hteam As String = word3.ToString.Remove(0, 20) exWS.Range("D" & zählerb - 1).Value = hteam End If If (m6.Success) Then 'PE Dim word7 As Group = m6.Groups(0) Dim hteam As String = word7.ToString exWS.Range("E" & zählerb - 1).Value = hteam End If If (m4.Success) Then 'Preis Dim word5 As Group = m4.Groups(0) Dim hteam As String = word5.ToString exWS.Range("F" & zählerb - 1).Value = hteam End If If (m5.Success) Then 'Liefertermin Dim word6 As Group = m5.Groups(0) Dim treffer As String = word6.ToString If treffer = "unbest" Then ' bis zum ä treffer = "unbestätigt" Else treffer = treffer.Replace("(Tag): ", "") ' den Vorspann vor dem Datum entfernen End If exWS.Range("G" & zählerb - 1).Value = treffer End If Loop Until objDateiLeser.EndOfStream 'REGEXSCHLEIFE UND EXCELSCHREIBSCHLEIFE VORBEI '============================================= objDateiLeser.Close() objDateiLeser = Nothing Next i 'HAUPTSCHLEIFE ZUR DATEI AUSWAHL VORBEI '====================================== Console.WriteLine("Excel wird nach: " & sPath & " gespeichert. Bitte kontrollieren!") exAPP.ActiveWorkbook.SaveAs(sPath & "VB-Excel.xls") exAPP.ActiveWorkbook.Close() exWB = exAPP.Workbooks.Open(sPath & "apps\makro.xls") exAPP.Run("Datei_zusammen") exAPP.ActiveWorkbook.SaveAs(sPath & "Import.xls") exAPP.ActiveWorkbook.Close() Console.WriteLine("Ordner wird von Arbeitsdateien befreit") 'ORDNER AUFRÄUMEN '======================================================= File.Delete(sPath & "VB-Excel.xls") File.Delete(sPath & "export.xls") For i = 0 To j - 1 File.Delete(sPath & "ausgabe" & i & ".txt") Next 'ORDNER AUFGERÄUMT '======================================================= Console.WriteLine("Dateien werden nach BüroPlus übertragen. Dies kann einige Minuten dauern!") Dim COMInstanz As Process COMInstanz = System.Diagnostics.Process.Start("T:\LIEFERANTEN\Wieland\Auftragsbestätigungen\apps\COM\Bestelleingang.Import.exe") COMInstanz.WaitForExit() Dim ordner As Process = System.Diagnostics.Process.Start("explorer", "T:\LIEFERANTEN\Wieland\Auftragsbestätigungen\") Console.WriteLine("Nun ist alles fertig, du musst nur noch eine Taste drücken!") 'Console.ReadKey() 'Programm bis zum Drücken einer Taste anhalten 'MsgBox("Programm fertig. Bitte in Verzeichnis: " & sPath & "nachsehen") End Sub End Module
Natürlich ist viel einfach aus Beispielen zusammengeschustert und manches vielleicht auch überladen. Aber es tut und für meine Zwecke brauche ich auch keine grafische Oberfläche, die verwirrt bloß den Anwender.
Was das Programm macht, wäre noch erwähnenswert.
1. Alle *.pdf Dateien im Ordner suchen
2. Gefundene *.pdf Dateien durch den pdftotext Automaten schicken (freies und tolles Tool)
3. Export einer aktuellen Liste aus BüroPlus mit bpStarter (auch ein fremdes Programm, aber es tut wunderbar)
4. Eine neue Exceldatei öffnen und Spaltennamen vorbereiten
5. Erzeugte *.txt Dateien aus Punkt 2 einzeln einlesen und mit REGEX Konstrukten nach den gewünschten Daten suchen.
6. Gewünschte Daten in die Exceldatei schreiben
7. Die exportierte Excel mit der erzeugten Excel zusammenführen und eine neue Excel daraus erzeugen mittels folgendem Makro
Sub Datei_zusammen() ' ' Makro2 Makro ' Makro am 09.03.2010 von Testumgebung aufgezeichnet ' ' Cells.Select Selection.ClearContents Workbooks.Open Filename:="T:\LIEFERANTEN\Wieland\Auftragsbestätigungen\export.xls" Workbooks.Open Filename:="T:\LIEFERANTEN\Wieland\Auftragsbestätigungen\VB-Excel.xls" Windows("VB-Excel.xls").Activate Sheets("Tabelle1").Select Sheets("Tabelle1").Copy After:=Workbooks("export.xls").Sheets(1) ActiveWorkbook.Save ActiveWindow.Close ActiveWindow.Close With ActiveSheet.QueryTables.Add(Connection:= _ "ODBC;DSN=Excel-Dateien;DBQ=T:\LIEFERANTEN\Wieland\Auftragsbestätigungen\export.xls;DefaultDir=T:\LIEFERANTEN\Wieland\Auftragsbestätigungen\;DriverId=790;MaxBufferSize=2048;PageTimeout=5;" _ , Destination:=Range("A1")) .CommandText = Array( _ "SELECT `Sheet1$`.`BL-Nr`, `Sheet1$`.`BL-Pos`, `Tabelle1$`.Auftr" _ , _ "agsnummer, `Tabelle1$`.`Wieland-Nummer`, `Tabelle1$`.`h-team-Nr`, `Tabelle1$`.Lieferdatum" & Chr(13) & "" & Chr(10) & "FROM `T:\LIEFERANTEN\Wieland\Auftragsbestätigungen\export`.`Sheet1$` `Sheet1$`, `T:\LIEFERANTEN\Wieland\Auftragsbestätigungen\expor" _ , _ "t`.`Tabelle1$` `Tabelle1$`" & Chr(13) & "" & Chr(10) & "WHERE `Sheet1$`.Kubez1 = `Tabelle1$`.`Wieland-Nummer` AND `Sheet1$`.`BL-Nr` = `Tabelle1$`.`BL-Nummer`" _ ) .Name = "Abfrage von Excel-Dateien" .FieldNames = True .RowNumbers = False .FillAdjacentFormulas = False .PreserveFormatting = True .RefreshOnFileOpen = False .BackgroundQuery = True .RefreshStyle = xlInsertDeleteCells .SavePassword = False .SaveData = True .AdjustColumnWidth = True .RefreshPeriod = 0 .PreserveColumnInfo = True .Refresh BackgroundQuery:=False End With Columns("F:F").Select Selection.NumberFormat = "m/d/yyyy" End Sub
8. Das C# Programm aufrufen und den COM-Import durchführen. Den Code habe ich freundlicherweise von jemanden zugemailt bekommen, da die API-Aufrufe doch recht umständlich sind. Nach ein paar Anpassungen an meine Bedürfnisse (Excelaufruf und Zeilenweise auslesen in ein Array), dann Vergleich ob der Datensatz existiert und ein paar break; ging es doch recht gut.
9. Nun läuft alles, muss es nur noch meinem Vorgesetzten zeigen und das OK einholen. Dann vielleicht ein paar kleine Anpassung machen.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; using Excel = Microsoft.Office.Interop.Excel; using System.Reflection; namespace Bestelleingang.Import { class Program { static void Main(string[] args) { int x, y, zeile =100, spalte = 7; Excel.Application oXL; Excel._Workbook oWB; Excel._Worksheet oSheet; Excel.Range CurRange; oXL = new Excel.Application(); //oXL.Visible = true; oWB = (Excel._Workbook)(oXL.Workbooks.Open(@"T:\LIEFERANTEN\Wieland\Auftragsbestätigungen\Import.xls", Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value)); oSheet = (Excel._Worksheet)oWB.ActiveSheet; string[,] L = new string[zeile, spalte]; for(x=2; x() { { "BelegNr", L[x, 1] }, { "BelegPosNr", L[x, 2] } }; int recordCount = bestelleingang.ApplyRange("BelegNr", rangeArgs); //Debug.WriteLine(String.Format("Selected {0} records", recordCount)); if (recordCount > 0) { // An existing row can be locked if (bestelleingang.BeginEdit()) { bestelleingang.AuftrNr = L[x, 3]; if (L[x, 6] != "") { bestelleingang.LiefDat = DateTime.Parse(L[x, 6]); } bestelleingang.EndEdit(); } //Debug.WriteLine("Row Edited"); } else { bestelleingang.BeginAppend(); // set fields here bestelleingang.EndAppend(); } } } Console.Write("So viele Datensaetze wurden geändert: "); Console.Write(x - 2); Console.WriteLine("Please press ANY KEY. Bitte nicht sagen: Ich kann die ANY KEY Taste nicht finden!"); //Console.ReadLine(); } } }
Die 3 Klassen für die API-Aufrufe sind nicht von mir, deshalb lass ich diese hier mal außen vor. Natürlich sind diese für das C# Programm auch wichtig!
Als Anmerkung: Das Programm ist nicht dynamisch, sprich der Pfad muss immer derselbe sein. Wenn man dieses Programm nun mit einer Installationsroutine ausstatten würde, wäre vielleicht ein Config-Datei nützlich um den Programmpfad auszulesen.
Ein Gedanke zu „PDFs automatisch in txt umwandeln und auswerten“
Kommentare sind geschlossen.