I need to loop and permutate through 2000 lines of a text file(which will always increase in size), get the total length, and based on the length, i need to copy the two records to another file.
The problem is that it takes to long to process everything. I am not sure that this is the best approach but any help is appreciated.
filename = "Jul2017.txt"
Set fso = CreateObject("Scripting.FileSystemObject")
Set f = fso.OpenTextFile(filename)
Do Until f.AtEndOfStream
r1 = f.ReadLine
Do Until f.AtEndOfStream
r2 = f.ReadLine
if len(r1 & r2) > 17 then
'Do something
end if
Loop
Loop
WScript.Echo "Done!"
f.Close
This should solve the loop nesting problem.
filename = "Jul2017.txt"
Set fso = CreateObject("Scripting.FileSystemObject")
Set f = fso.OpenTextFile(filename, 1)
For x = 1 to 2000
r1 = f.ReadLine
For z = 1 to 2000
r2 = f.ReadLine
if len(r1 & r2) > 17 then
'Do something
end if
next
next
WScript.Echo "Done!"
f.Close
Input
-----------
TMM87R2
YUU52R7VVB
VLL73IOP3
TMM54Y2
VLL21CSZ
YUU56
VLL71BVR54
...
What i need to do:
First iteration
TMM87R2 & TMM87R2 < 17 characters ( do nothing )
TMM87R2 & YUU52R7VVB > 17 characters ( copy the lines )
TMM87R2 & VLL73IOP3 etc.
...
TMM87R2 & VLL71BVR54
Second iteration
YUU52R7VVB & TMM87R2
YUU52R7VVB & YUU52R7VVB
...
Until last iteration
VLL71BVR54 & VLL71BVR54
Each line should be "placed" next to each line in the file and if the total size is excedes 17 characters,
copy the two records to another file. I know i am looping 2000 times and it is repetitive but the order of records matters.
Why not read the file into an array in memory
This is in VB.NET but should give you a clue
Dim lines = File.ReadAllLines("Jul2017.txt")
Rem Arrays are zero based, and we cant compare the last element with anything so ...
For first = 0 To lines.Length - 2
Dim line = lines(first)
Dim len = line.Length
For perm = first + 1 To lines.Length - 1
If lines(perm).Length + len > 17 Then
Rem Do Something
Console.WriteLine(line & " & " & lines(perm))
End If
Next
Next
Related
This is the middle of the code I'm trying to work with. Is there a way to make the file it's reading open and read from line 2 to line 97? Where I need the correction is starred (****). What I'm trying to do is get the data from lines 2 through 97 to compare to another file I'll have to open from the same lines. The beginning and ends of each file are different but the middle information should match thus I need these specific lines.
' Build Aliquot file name
strFile = aBarcodeExportDir & "A-" & yearStr & "-" & splitStr2(0) & ".csv"
'msgbox("open file: " & strFile)
If (objFS.FileExists(strFile)) Then
' Open A file
Set objFile = objFS.OpenTextFile(strFile)
' Build string with file name minus extension - used later to determine EOF
strFileNameNoExtension = "A-" & yearStr & "-" & splitStr2(0)
' Create dictionary to hold key/value pairs - key = position; value = barcode
Set dictA = CreateObject("Scripting.Dictionary")
' Begin processing A file
Do Until objFile.AtEndOfStream(*****)
' Read a line
strLine = objFile.ReadLine(*****)
' Split on semi-colons
splitStr = Split(strLine, ";")
' If splitStr array contains more than 1 element then continue
If(UBound(splitStr) > 0) Then
' If barcode field is equal to file name then EOF
If(splitStr(6) = strFileNameNoExtension) Then
' End of file - exit loop
Exit Do
Else
' Add to dictionary
' To calculate position
' A = element(2) = position in row (1-16)
compA = splitStr(2)
' B = element(4) = row
compB = splitStr(4)
' C = element(5.1) = number of max positions in row
splitElement5 = Split(splitStr(5), "/")
compC = splitElement5(0)
' position = C * (B - 1) + A
position = compC * (compB - 1) + compA
barcode = splitStr(6) & ";" & splitStr(0) & ";" & splitStr(1) & ";" & splitStr(2)
'msgbox(position & ":" & barcode)
' Add to dictionary
dictA.Add CStr(position), barcode
End If
End If
Loop
' Close A file
objFile.Close
To give the exact answer, we may have to look at your text files(I mean with all the split functions you are using). But, If you just want to compare lines 2-97 of two text files, you can get a hint from the following piece of code:
strPath1 = "C:\Users\gr.singh\Desktop\abc\file1.txt" 'Replace with your File1 Path
strPath2 = "C:\Users\gr.singh\Desktop\abc\file2.txt" 'Replace with your File2 Path
Set objFso = CreateObject("Scripting.FileSystemObject")
Set objFile1 = objFso.OpenTextFile(strPath1,1)
Set objFile2 = objFso.OpenTextFile(strPath2,1)
blnMatchFailed = False
Do Until objFile1.AtEndOfStream
If objFile1.Line=1 Then
objFile1.SkipLine() 'Skips the 1st line of both the files
objFile2.SkipLine()
ElseIf objFile1.Line>=2 And objFile1.Line<=97 Then
strFile1 = objFile1.ReadLine()
strFile2 = objFile2.ReadLine()
If StrComp(strFile1,strFile2,1)<>0 Then 'textual comparison. Change 1 to 0, if you want binary comparison of both lines
blnMatchFailed = True
intFailedLine = objFile1.Line
Exit Do 'As soon as match fails, exit the Do while Loop
Else
blnMatchFailed = False
End If
Else
Exit Do
End If
Loop
If blnMatchFailed Then
MsgBox "Comparison Failed at line "&intFailedLine
Else
MsgBox "Comparison Passed"
End If
objFile1.Close
objFile2.Close
Set objFile1 = Nothing
Set objFile2 = Nothing
Set objFso = Nothing
I got this code which deletes 10 lines starting from the top.
Is it possible to do the same but starting the delete from the bottom to the top of the txt file?
So if I have 30 lines, I want the last 10 or 20 lines to be deleted.
Const FOR_READING = 1
Const FOR_WRITING = 2
strFileName = "C:\scripts\delete.txt"
iNumberOfLinesToDelete = 10
Set objFS = CreateObject("Scripting.FileSystemObject")
Set objTS = objFS.OpenTextFile(strFileName, FOR_READING)
strContents = objTS.ReadAll
objTS.Close
arrLines = Split(strContents, vbNewLine)
Set objTS = objFS.OpenTextFile(strFileName, FOR_WRITING)
For i=0 To UBound(arrLines)
If i > (iNumberOfLinesToDelete - 1) Then
objTS.WriteLine arrLines(i)
End If
Next
If you read the entire file into an array of lines you'd use more or less the same approach for removing lines from beginning or end.
To remove lines from the beginning you start at an offset after the lines that you want removed:
filename = "C:\path\to\your.txt"
numLinesToRemove = 10
Set fso = CreateObject("Scripting.FileSystemObject")
txt = Split(fso.OpenTextFile(filename).ReadAll, vbNewLine)
Set f = fso.OpenTextFile(filename, 2)
For i = numLinesToRemove To UBound(txt)
f.WriteLine txt(i)
Next
f.Close
To remove lines from the end of the file you stop before the lines that you want removed:
filename = "C:\path\to\your.txt"
numLinesToRemove = 10
Set fso = CreateObject("Scripting.FileSystemObject")
txt = Split(fso.OpenTextFile(filename).ReadAll, vbNewLine)
Set f = fso.OpenTextFile(filename, 2)
For i = 0 To UBound(txt) - numLinesToRemove
f.WriteLine txt(i)
Next
f.Close
This approach only works for small files, though. If you need to process large files you usually can't read the entire file into memory. If you did your computer would start swapping data from memory to disk, causing the system to slow down to a crawl. To avoid this you normally read the file line by line in a loop and write to a temporary file, then replace the original file with the temp file after processing is complete.
Removing lines from the beginning of a file is still fairly trivial, because TextStream objects have a Line property that holds the current line number (i.e. the number of the line that the next ReadLine call would read).
Set f = fso.OpenTextFile(filename)
Set tmp = fso.OpenTextFile(filename & ".tmp", 2, True)
Do Until f.AtEndOfStream
If f.Line <= numLinesToRemove Then
f.SkipLine
Else
tmp.WriteLine f.ReadLine
End If
Loop
f.Close
tmp.Close
However, you can't do that for removing lines from the end of the file, because you don't know the number of lines beforhand. One way to deal with this is to create a ring buffer the size of the number of lines you want to remove, fill it as you read lines from the input file, and write lines to the output file when they are removed from the buffer. That way the last numLinesToRemove lines are still in the buffer (not written to the output file) when the loop terminates.
ReDim buf(numLinesToRemove) 'ring buffer
i = -1 'ring buffer pointer
Set f = fso.OpenTextFile(filename)
Set tmp = fso.OpenTextFile(filename & ".tmp", 2, True)
Do Until f.AtEndOfStream
i = (i + 1) Mod numLinesToRemove 'advance ring buffer pointer
'if current buffer slot is filled write it to the output file ...
If Not IsEmpty(buf(i)) Then tmp.WriteLine buf(i)
'... then put current line from input file into current buffer slot
buf(i) = f.ReadLine
Next
f.Close
tmp.Close
In both cases you'd replace the original file after processing is complete, e.g. like this:
fso.DeleteFile filename
fso.MoveFile filename & ".tmp", filename
just loop backwards in your for statement
For i=UBound(arrLines) To (UBound(arrLines) -10) step -1
Next
I am creating a guy script read files in a folder, (Scripting.FileSystemObject), but I would like to relate a indice inpubox type int to determine which file in the folder I'll write on the screen.
Ex: indice = inputbox "" ← 4 grab the indice file in the folder 4 and esquever your name on the screen.
I wonder if this is possible because already tried in many ways and even by matrix, but without result.
This and my code. I do not know but where to go!
Dim sFO, NovaPasta, Folder,File, Indice
Dim inpast(4)
'Setup
Set sFO = CreateObject("Scripting.FileSystemObject")
Set Folder = sFo.GetFolder("C:\Users\502526523\Documents\Control")
NovaPasta = "Control"
'Development
If Not sFO.FolderExists (NovaPasta) = True Then
sFO.CreateFolder (NovaPasta)
Wscript.Sleep 900
WScript.Echo "Pasta Criada"
Else
WScript.Echo "Pasta Existente "
End If
' Line Verificas a quantidade de inpastas dentro da pasta, se > 5
' deleta os exedentes com data mais antiga
For Each file In folder.Files
If Folder.Files.Count > 5 And (DateDiff("d", file.DateLastModified, Now) > 7) Then
WScript.Echo (file.Name & vbLf)
WScript.Echo ("Total files :" & Folder.Files.Count)
File.Delete
End If
Next
For Each file In folder.Files
inpast(0) = (file.Name)
inpast(1) = (file.Name)
inpast(2) = (file.Name)
inpast(3) = (file.Name)
inpast(4) = (file.Name)
Indice = Inputbox ("Digite o valor do Indice de 0...30")
Select Case Indice
Case 0
WScript.Echo inpast(0)
Case 1
WScript.Echo inpast(1)
Case 2
WScript.Echo inpast(2)
Case 3
WScript.Echo inpast(3)
Case 4
WScript.Echo inpast(4)
End Select
Next
Still not sure if I understand your question correctly. You mean you have a list of filenames and you want to display the filename corresponding to the number the user entered via an InputBox? If that's what you want you should change your second For Each loop like this:
i = 0
For Each file In folder.Files
inpast(i) = file.Name
i = i + 1
Next
Indice = InputBox("Digite o valor do Indice de 0...30")
WScript.Echo inpast(CInt(Indice))
Note, however, that the condition in your first For Each loop does not guarantee you'll only ever have 5 files left after the loop. If for some reason the folder contains more than 5 files that were modified within the past 7 days the second loop would fail with a "subscript out of range" error.
There are several ways you could handle this:
Dynamically resize the inpast array so it can hold more than 5 items.
Sort the files in the folder by last modification date (e.g. like this) and delete everything except the 5 most recent files.
Cut off the second For Each loop after the 5th iteration (Exit For).
Note also, that you should sanitize your input. (What happens when users enter text, an invalid number, or press "Cancel"?)
Set fso = CreateObject("Scripting.FileSystemObject")
Dirname = InputBox("Enter Dir name")
'Searchterm = Inputbox("Enter search term")
ProcessFolder DirName
Sub ProcessFolder(FolderPath)
' On Error Resume Next
Set fldr = fso.GetFolder(FolderPath)
msgbox fls.count
Msgbox fls.item("computerlist.txt")
End Sub
To do the 7th
Set Fls = fldr.files
For Each thing in Fls
Count = Count + 1
If count = 7 then msgbox Thing.Name & " " & Thing.DateLastModified
Next
For a scavenger hunt I wanted to give my nephews 4 parts of the same letter that they would have to combine to read. I couldn't find anything online to use quickly, so I wrote one. I will updated with better code if you have some ideas.
'****************** Change things here ****************
'Change the number in perenthesis to set the number of files
Dim Letters(4)
'Set the original filename here:
originalFile = "Letter.txt"
'Set a letter to use as a syncronizer in addition to punctuation and line breaks
charSync = asc("o")
'*********************************************************
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile(originalFile , 1)
Do Until objFile.AtEndOfStream
strCharacters = objFile.Read(1)
Randomize
selLetter = Int((Ubound(Letters))*Rnd+1)
If (asc(strCharacters) < 46) or (asc(strCharacters) = charSync) then
for i = 1 to Ubound(Letters)
Letters(i) = Letters(i) & strCharacters
next
else
for i = 1 to Ubound(Letters)
if i = selLetter then
Letters(i) = Letters(i) & strCharacters
else
Letters(i) = Letters(i) & " "
end if
next
end if
Loop
For n = 1 to Ubound(Letters)
outFileName = replace(originalFile ,".",n & ".")
Set objFile = objFSO.CreateTextFile(outFileName,True)
objFile.Write Letters(n)
objFile.Close
next
Set objFSO=Nothing
Usage:
Create a text file you want to split up randomly
Edit the script for the following:
Set the number of documents to split it into
Set the file name of the file to update
Select a synch character if you want to
Run the script and it will create numbered files
Use equal character spacing font like system to read by holding up to light
'****************** Change things here ****************
'Change the number in perenthesis to set the number of files
Dim Letters(4)
'Set the original filename here:
originalFile = "Letter.txt"
'Set a letter to use as a syncronizer in addition to punctuation and line breaks
charSync = asc("o")
'*********************************************************
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile(originalFile , 1)
Do Until objFile.AtEndOfStream
strCharacters = objFile.Read(1)
'uncomment next 2 comments to simplify and change only once per word:
'If strCharacters = " " then
Randomize
selLetter = Int((Ubound(Letters))*Rnd+1)
'end if
If (asc(strCharacters) < 46) or (asc(strCharacters) = charSync) then
for i = 1 to Ubound(Letters)
Letters(i) = Letters(i) & strCharacters
next
else
for i = 1 to Ubound(Letters)
if i = selLetter then
Letters(i) = Letters(i) & strCharacters
else
Letters(i) = Letters(i) & " "
end if
next
end if
Loop
For n = 1 to Ubound(Letters)
outFileName = replace(originalFile ,".",n & ".")
Set objFile = objFSO.CreateTextFile(outFileName,True)
objFile.Write Letters(n)
objFile.Close
next
Set objFSO=Nothing
Usage:
Create a text file you want to split up randomly
Edit the script for the following:
Set the number of documents to split it into
Set the file name of the file to update
Select a synch character if you want to
Run the script and it will create numbered files
Use equal character spacing font like system to read by holding up to light
I Have a 2100 Rows and 6 Columns Table
Throughout the table there are only 12 Possible values, say A,B,C,D,E,F,G,H,I,J,K,L
The 12th value L is just a blank filler. It denotes blank cell.
Since there are only 11 possible values througout the table, patterns are observed.
First a Pattern Appears and it is later repeated somewhere in the table.
There can be any number of Patterns, but i have a specific format for a pattern which is to found and reported that way.
Solutions in EXCEL-VBA, PHP-MYSQL or C are welcome.
I have attached an example of what Iam looking for. Suggestions are most welcome to refine
the questions.
Information & Format : http://ge.tt/8QkQJet1/v/0 [ DOCX File 234 KB ]
Example in Excel Sheet : http://ge.tt/69htuNt1/v/0 [ XLSX File 16 KB ]
Please comment for more information or specific requirement.
Please try the code below, change the range to what you need it to be and the sheet number to the correct sheet number (I wouldn't put your full range in just yet because if you have 1000 pattern finds, you'll have to click OK on the message box 1000 times, just test with a partial range)
This will scan through the range, and find any pattern of two within a 10 row range, if you need it to find bigger patterns, youll need to add the same code again with an extra IF statement checking the next offset.
This will only find it if the same pattern exists and the same column structure is present, but its a start for you
Works fine on testing
Sub test10()
Dim rCell As Range
Dim rRng As Range
Set rRng = Sheets("Sheet1").Range("A1:I60") '-1 on column due to offset
'Scan through all cells in range and find pattern
For Each rCell In rRng.Cells
If rCell.Value = "" Then GoTo skip
For i = 1 To 10
If rCell.Value = rCell.Offset(i, 0).Value Then
If rCell.Offset(0, 1).Value = rCell.Offset(i, 1) Then
MsgBox "Match Found at: " & rCell.Address & ":" & rCell.Offset(0, 1).Address & " and " & rCell.Offset(i, 0).Address & ":" & rCell.Offset(i, 1).Address
End If
End If
Next i
skip:
Next rCell
End Sub
***UPDATE***
I have updated my code, the following now finds the pattern wherever it may appear in the next 10 rows:
Sub test10()
Dim rCell As Range
Dim rRng As Range
Dim r1 As Range
Dim r2 As Range
Set rRng = Sheets("Sheet1").Range("A1:I50") '-1 on column due to offset
i = 1 'row length
y = 0 'column length
'Scan through all cells in range and find pattern
For Each rCell In rRng.Cells
If rCell.Value = "" Then GoTo skip
i = 1
Do Until i = 10
y = 0
Do Until y = 10
xcell = rCell.Value & rCell.Offset(0, 1).Value
Set r1 = Range(rCell, rCell.Offset(0, 1))
r1.Select
ycell = rCell.Offset(i, y).Value & rCell.Offset(i, y + 1).Value
Set r2 = Range(rCell.Offset(i, y), rCell.Offset(i, y + 1))
If ycell = xcell Then
Union(r1, r2).Font.Bold = True
Union(r1, r2).Font.Italic = True
Union(r1, r2).Font.Color = &HFF&
MsgBox "Match Found at: " & rCell.Address & ":" & rCell.Offset(0, 1).Address & " and " & rCell.Offset(i, y).Address & ":" & rCell.Offset(i, y + 1).Address
Union(r1, r2).Font.Bold = False
Union(r1, r2).Font.Italic = False
Union(r1, r2).Font.Color = &H0&
End If
y = y + 1
Loop
i = i + 1
Loop
skip:
Next rCell
End Sub