I would like to cycle through several hundreds of XBRL-files automatically and gather certain specific pieces of data and paste them into an excel sheet. I managed to get the "tangential code" working, but cannot answer the core question.
E.g., in the XBRL file I need the value of this fact, reported against the concept pfs:GainLossBeforeTaxes:
<pfs:GainLossBeforeTaxes
unitRef="U-EUR"
decimals="INF"
contextRef="CurrentDuration">1091134.68</pfs:GainLossBeforeTaxes>
==> I need to obtain 1091134.68
This is doubtlessly something which is easy with Regex, but I cannot seem to get this working. And time constraints are also a thing for me, so I would like to obtain some sort of minimal viable product so far and later on expand that, but at this point the code is more of a means to an end, rather than the endproduct (analysis) itself.
So far, I came up with the following:
Sub EDI_Input()
Dim myFile As String
Dim textline As String
Dim StartPos As Integer
Dim EndPos As Integer
myFile = Application.GetOpenFilename()
Open myFile For Input As #EDI
Do Until EOF(EDI)
Line Input #EDI, textline
If InStr(textline, "NonRecurringFinancialCharges") <> 0 And InStr(textline, "CurrentDuration") <> 0 Then
Endpos = InStr(textline, "</pfs:NonRecurringFinancialCharges><")
result = Left(textline, Endpos - 1)
StartPos = InStr(textline, "Char(34)&CurrentDuration&Char(34)&>")
textline = Left(textline, StartPos + 18)
Debug.Print (textline)
End If
Loop
I keep stumbling on the "invalid call procedure or argument error", possible because I load to many data in my string.
Anybody who has any opinion on how to get at least a partially working programma - in that way I can at least partially start my analysis - Or a tutorial for beginners/experience with this problem?
Welcome to StackOverflow!
I recommend using an XBRL processor, for example Arelle, which is open source. If I correctly remember, you should be able to export facts to formats like CSV and import it into Excel.
Otherwise, you will end up reimplementing an XBRL processor in VBA. There are many involved details to consider in order to get the correct values (joining with context, considering dimensions, etc). Values could be reported against a concept for multiple periods, etc.
An XBRL processor will do that out of the box.
Related
This question already has answers here:
How to extract file name from path?
(16 answers)
Extract filename from path [duplicate]
(9 answers)
Closed 1 year ago.
Background:
I have written some VB to search through a directory for a file (the date of the file will be variable, hence the wildcard) the code will then open the file, copy a list of names to another spreadsheet, store the names in an array so I can return their relative position in the other spreadsheet - pos, I need to do this as the next part of the code writes a link to that spreadsheet.
My filename search code is as follows:
Dim lcFound As String
lcFound = Dir("C:\Users\KDelaney\Desktop\*XYZ Select XYZ List.xlsm")
If lcFound <> "" Then
Workbooks.Open Filename:=lcFound
End If
Not going to add all the code as I don't think it's relative to the question. The writing a link formula to each cell in a f loop is as below, the below code was fine before I was using the file search, as I was testing it with the full file name and path:
Cells(f, 4).Formula = "=" & "'" & lcPath & "[" & lcFile & "]" & "Template" & "'!" & "AB" & pos
Question:
Basically the question is what would be the best way to convert C:\Users\KDelaney\Desktop\*XYZ Select XYZ List.xlsm into lcPath and lcFile, or is there a way round separating the strings? (Have briefly tested it unseparated but it seems like the square brackets around the filename is required).
I'm guessing the best course of action would be using InStr function? to return the position of the end of the file path? e.g. I know the filepath will always end with "Desktop\" so if I use InStr to return that position I will then know the filepath is between 1 and the returned value of InStr. However this is where I have stalled, not sure where to go from here, in terms of separating the two strings. I also know the start of the file name will always begin with a number and end with the file extension. I am probably making a meal of this problem and someone will come back with a very simple solution (hopefully)
Any help would be appreciated. Apologies if the question was too in depth just wanted to give you the full picture.
Thanks.
Sorry for:
a) Not researching enough if the question has already been asked/answered
b) answering my own question
Link contains two good answers, a function and using file system object.
How to extract file name from path?
I am going to persevere using the split function for my own knowledge and if things don't go well I will resort to one of the other two methods in the link above.
Further edit: haven't cleaned the code up yet, but I have realised I was massively overcomplicating the problem, all it required was a bit of messy string manipulation. If anyone comes across a similar problem:
Dim WrdArray() As String
Dim lcFound As String
strPath = "C:\Users\KDelaney\Desktop\*XYZ Select XYZ List.xlsm"
newstrL = InStrRev(strPath, "\")
newstrL = newstrL - 1
strlen = Len(strPath)
lnPath = strlen - newstrL
MsgBox Left(strPath, lnPath)
Definitely not the best way of doing it, have realized there are 'multiple ways to skin a cat' in this situation.
Link below for lots of info on string manipulation:
https://www.excel-easy.com/vba/string-manipulation.html
I would like to limit certain textboxes to accept only [A-Za-z]
I hope, a counterpart to Like exists.
With Like I would have to make a long list of not allowed characters to be able to filter.
Not MyString like [?;!°%/=....]
I can think of a solution in the form of:
For Counter = 1 To Len(MyString)
if Mid(MyString, Counter, 1) Like "*[a-z]*" = false then
MsgBox "String contains bad characters"
exit sub
end if
next
... but is there a more sophisticated 1liner solution ?
Until then, I have created a function to make it "Oneliner":
Function isPureString(myText As String) As Boolean
Dim i As Integer
isPureString = True
For i = 1 To Len(myText)
If Mid(myText, i, 1) Like "*[a-zA-Z_íéáűúőöüóÓÜÖÚŐŰÁÉÍ]*" = False Then
isPureString = False
End If
Next
End Function
If i add 1 more parameter, its also possible to define the allowed characters upon calling the function.
Ok, it seems my question was a bit of a duplicate, even though that did not pop in my search results.
So credits for #QHarr for posting the link.
The solution I can forge from that idea for my "oneliner" is:
If myText Like WorksheetFunction.Rept("[a-zA-Z]", Len(myText))=false then 'do something.
Using .rept is inspiringly clever and elegant in my oppinion.
So what is does: Multiplies the search criteria for each charater instead of looping through the characters.
EDIT:
In an overaboundance of nice and elegant solutions, the most recent leader is:
If not myText Like "*[!A-Za-z]*" then '... do something
Statistics update:
I have tested the last 3 solutions' performance:
I have pasted # in the below text strin at the beginning, at the end or nowhere.
The criteria were: "*[a-zA-Z \S.,]*"
For 100000 repetitions
text = "This will be a very Long text, with one unwanted in the middle, to be able to test the difference in performance of the approaches."
1.) Using the [!...] -> 30ms with error, 80ms if no error
2.) Using .Rept -> around 1800ms for all cases
3.) Using characterLoop+Mid -> around 3000ms if no error / 40-80ms ms if early error
I'm currently trying to automate our accounting process. From the bank, I download a .csv file that I'd like to transform in a certain way. I'm also attempting to eliminate all IBAN and BIC numbers from the document as they're not necessary for the accounting process.
Now, every IBAN and BIC follows a certain pattern. How do I replace all strings with a certain pattern (i.e. XX00000000000000 and DEXXXXXXXXX) or at least how do I find them using Visual Basic? I'm familiar with the .replace method already, I just cannot manage to find the string.
Thank you so much in advance!
I think this should help you:
RegEx
An another way could be to load each textline of the .csv file into an array and just Loop through them.
Something like:
Dim Textline() As String 'array
Dim IBAN As String
Dim posIBAN As Integer
Dim iban_length As Integer
textlinelength = UBound(Textline)
iban_length = 22
For i = 0 To textlinelength
If InStr(Textline(i), "DE") Then 'if array contains DE
posIBAN = InStr(Textline(i), "DE") 'find position of IBAN
IBAN = Mid(Textline(i), posIBAN, iban_length) 'get IBAN
Textline(i) = Replace(Textline(i), IBAN, "") 'replace IBAN with ""
End If
Next i
After that you could create a new file and write the arrays in it.
So you would have a IBAN-free txt-file
PS: Is there a way to properly link other questions/answers?
I have a subroutine that generates a report of performance of different portfolios within 5 families. The thing is that the portfolios in question are never the same and the amount in each family neither. So, I copy paste a template (that is formated and...) and add the formated row (containing the formula and...) in the right family for each portfolio in the report. Everything works just fine, the code is not optimal and perfect of course, but it works fine for what we need. The problem is not the code itself, it is that when I execute the code the first time, it goes really fast (like 1 second)... but from the second time, the code slows down dramatically (almost 30 second for a basic task identical to the first one). I tried all the manual calculation, not refreshing the screen and ... but it is really not where the problem comes from. It looks like a memory leak to me, but I cannot find where is the problem! Why would the code runs very fast but sooooo much slower right after... Whatever the length of the report and the content of the file, I would need to close excel and reopen it for each report.
**Not sure if I am clear, but it is not because the code makes the excel file larger or something, because after the first (fast) execution, if I save the workbook, close and reopen it, the (new) first execution will again be very fast, but if I would have done the same excat thing without closing and reopening it would have been very slow...^!^!
Dim Family As String
Dim FamilyN As String
Dim FamilyP As String
Dim NumberOfFamily As Integer
Dim i As Integer
Dim zone As Integer
Sheets("RapportTemplate").Cells.Copy Destination:=Sheets("Rapport").Cells
Sheets("Rapport").Activate
i = 3
NumberOfFamily = 0
FamilyP = Sheets("RawDataMV").Cells(i, 4)
While (Sheets("RawDataMV").Cells(i, 3) <> "") And (i < 100)
Family = Sheets("RawDataMV").Cells(i, 4)
FamilyN = Sheets("RawDataMV").Cells(i + 1, 4)
If (Sheets("RawDataMV").Cells(i, 3) <> "TOTAL") And _
(Sheets("RawDataMV").Cells(i, 2) <> "Total") Then
If (Family <> FamilyP) Then
NumberOfFamily = NumberOfFamily + 1
End If
With Sheets("Rapport")
.Rows(i + 8 + (NumberOfFamily * 3)).EntireRow.Insert
.Rows(1).Copy Destination:=Sheets("Rapport").Rows(i + 8 + (NumberOfFamily * 3))
.Cells(i + 8 + (NumberOfFamily * 3), 6).Value = Sheets("RawDataMV").Cells(i, 2).Value
.Cells(i + 8 + (NumberOfFamily * 3), 7).Value = Sheets("RawDataMV").Cells(i, 3).Value
End With
End If
i = i + 1
FamilyP = Family
Wend
For i = 2 To 10
If Sheets("Controle").Cells(16, i).Value = "" Then
Sheets("Rapport").Cells(1, i + 11).EntireColumn.Hidden = True
Else
Sheets("Rapport").Cells(1, i + 11).EntireColumn.Hidden = False
End If
Next i
Sheets("Rapport").Cells(1, 1).EntireRow.Hidden = True
'Define printing area
zone = Sheets("Rapport").Cells(4, 3).End(xlDown).Row
Sheets("Rapport").PageSetup.PrintArea = "$D$4:$Y$" & zone
Sheets("Rapport").Calculate
Sheets("RANK").Calculate
Sheets("SommaireGroupeMV").Calculate
Sheets("SommaireGroupeAlpha").Calculate
Application.CutCopyMode = False
End Sub
I do not have laptop with me at the moment but you may try several things:
use option explicit to make sure you declare all variables before using them;
from what I remember native vba type for numbers is not integer but long, and integers are converted to long, to save the computation time use long instead of integers;
your Family variables are defined as strings but you store in them whole cells and not their values i.e. =cells() instead of =cells().value;
a rule of a thumb is to use cells(rows.count, 4).end(xlup).row
instead of cells(3, 4).end(xldown).row.;
conditional formatting may slow down things a lot;
use for each loop on a range if possible instead of while, or even copy range to variant array and iterate over that (that is the fastest solution);
use early binding rahter of late binding, i.e., define objects in a proper type as soon a possible;
do not show printing area (page breaks etc.);
try to do some pofiling and look for the bottlenecks - see finding excel vba bottlenecks;
paste only values if you do not need formats;
clear clipboard after each copy/paste;
set objects to Nothing after finishing using them;
use Value2 instead of Value - that will ignore formatting and take only numeric value instead of formatted value;
use sheet objects and refer to them, for example
Dim sh_raw As Sheet, sh_rap As Sheet
set sh_raw = Sheets("RawDataMV")
set sh_rap = Sheets("Rapport")
and then use sh_raw instead of Sheets("RawDataMV") everywhere;
I had the same problem, but I finally figured it out. This is going to sound ridiculous, but it has everything to do with print page setup. Apparently Excel recalculates it every time you update a cell and this is what's causing the slowdown.
Try using
Sheets("Rapport").DisplayPageBreaks = False
at the beginning of your routine, before any calculations and
Sheets("Rapport").DisplayPageBreaks = True
at the end of it.
I had the same problem. I am far from expert programer. The above answers helped my program but did not solve the problem. I'm running excel 2013 on a 5 year old lap top. Open the program without running it, go to File>OptionsAdvanced, Scroll down to Data and uncheck "Disable undo for large Pivot table refresh...." and "Disable undo for large data Model operation". You could also try leaving them checked but decreasing their value. One or both of these seem to be creating a ever increase file that slows the macro and eventual grinds it to a stop. I assume closing excel clears the files they create so that's why it runs fast when excel is closed and reopened at least for a while. Someone with more knowledge will have to explain what these changes will do and what the consequences are of unchecking them. It appears these changes will be applied to any new spread sheets you create. Maybe these changes would not be necessary if I had a newer more powerful computer.
I wrote a User Defined Fucntion in Excel. It works great with no issues. I even wrote a description for it under the object properties menu.
The problem is, my UDF never shows up in the Excel drop down menu that appears when I start to type a function. I want the user to be able to see my UDF, named removeNumbers, when they go into a cell and start to type out a function.
I would also like them to be able to see the description which I wrote, just like the standard Excel functions.
And finally, is there a way that I can provide a description for each argument which my function takes as input?
Here is the actual code, although I don't think it will be necessary to answer my questions.
Function removeNumbers(sInput As String, sChoice As Boolean) As String
Dim sSpecialChars As String
Dim i As Long
If (sChoice = True) Then 'if true is selected, will remove all number including 0
sSpecialChars = "0123456789" 'This is your list of characters to be removed
For i = 1 To Len(sSpecialChars)
sInput = Replace$(sInput, Mid$(sSpecialChars, i, 1), "")
Next
End If
If (sChoice = False) Then 'if false is selected, will remove all numbers excluding zero
sSpecialChars = "123456789" 'This is your list of characters to be removed
For i = 1 To Len(sSpecialChars)
sInput = Replace$(sInput, Mid$(sSpecialChars, i, 1), "")
Next
End If
removeNumbers = sInput
End Function
To make the function appear in the drop-down you must place it in a standard module rather than the worksheet code area.
Another poster has already covered the need for the code to be in a standard module. With regards the argument descriptions, you should look at the MacroOptions code in this answer - although it only works in Excel 2010 or later.
For Excel 2007 and earlier, the only solution I have seen is in an article by JK Pieterse. This involves using the ExecuteExcel4Macro and looks a bit complicated.