I have an output.txt file which has following content:
Windows 6543765432
Linux 4534653463
MacOS 3564325
Ubuntu 8235646255
I want to create a VBScript which searches for all numeric values in output.txt and divide them by 1024 so that memory in KB can be changed into MB.
I have tried in batch here, but due to 2 GB limitation it's not working in above case.
Your top-level task is to modify a small file of structured text. The 'design pattern' for such a task is:
Get a FileSystemObject
Specify the full file path
Read the content
Modify the content
Write the modified content back
Your sub-task of modification involves computations on non-constant /varying parts; then a 'RegExp.Replace with Function' strategy should be used:
Define a RegExp (global, identify the parts to change)
.Replace(input, GetRef("function to do the computations on the parts"))
In your case, that function should convert the (string) parts to numbers, divide then, and return the result converted to strings.
In code:
Option Explicit
Const FSPEC = "..\testdata\txt\19556079.txt"
Dim oFS : Set oFS = CreateObject( "Scripting.FileSystemObject" )
Dim sAll : sAll = Modify(oFS.OpenTextFile(FSPEC).ReadAll())
oFS.CreateTextFile(FSPEC).Write sAll
WScript.Echo oFS.OpenTextFile(FSPEC).ReadAll()
Function Modify(s)
Dim re : Set re = New RegExp
re.Global = True
re.Pattern = "\d+"
Modify = re.Replace(s, GetRef("FiMoReFunc"))
End Function
Function FiMoReFunc(sM, sP, sS)
FiMoReFunc = CStr(CDbl(sM) / 1024)
End Function
For a more fancy output:
FiMoReFunc = Right(Space(20) & FormatNumber(CDbl(sM) / 1024, 1, True) & " Unit", 20)
output:
Windows 6,390,395.9 Unit
Linux 4,428,372.5 Unit
MacOS 3,480.8 Unit
Ubuntu 8,042,623.3 Unit
Try this
Option Explicit
Const FILE = "output.txt"
Dim fso
Dim ts,line
Dim match,matches
Dim os,mem
Set fso = CreateObject("Scripting.FilesystemObject")
Set ts = fso.OpenTextFile(FILE)
With New RegExp
.IgnoreCase = True
While Not ts.AtEndOfStream
line = ts.ReadLine
.Pattern = "[a-z]+"
Set matches = .Execute(line)
For Each match In matches
os = match.Value
Next
.Pattern = "\d+"
Set matches = .Execute(line)
For Each match In matches
mem = (CDbl(match.Value) / 1024)
Next
WScript.Echo os & vbTab & mem
Wend
End With
Set ts = Nothing
Set fso = Nothing
WScript.Quit
Related
I need to help in converting a Python script to VBScript. I'm trying to load the .cal file as a binary value file and edit a particular value in the file but unfortunately, my environment only supports VBScript.
import argparse
parser = argparse.ArgumentParser(description='Sapix Cal File Sensitivity Adjustment')
parser.add_argument("-calfile", default="test.cal", help="Enter the Calfile name (ex: 09781DK5081.cal")
parser.add_argument("-vtest", default=125, help="New Vtest setting (85-205)")
parser.add_argument("-vref", default=250, help="New Vref setting (250-120)")
args = parser.parse_args()
calfile = args.calfile
vtest = args.vtest
vref = args.vref
print(calfile)
print(vtest)
print(vref)
with open(calfile, "rb") as binary_file:
# Read the whole file at once
data = bytearray(binary_file.read())
# Find Line with VTEST setting
ivteststart = data.find(bytearray('PARALLEL_VOLTAGE_TEST', 'utf-8'))
ivtestend = data.find(b'\n',ivteststart)
# Remove original VTEST line
del data[ivteststart:ivtestend+1]
# Insert New Line with new VTEST
new_vtest = bytearray("PARALLEL_VOLTAGE_TEST %s\n" % (vtest),'utf-8')
data[ivteststart:ivteststart] = new_vtest
# Find Line with VREF setting
ivrefstart = data.find(bytearray('PARALLEL_VOLTAGE_REF', 'utf-8'))
ivrefend = data.find(b'\n',ivrefstart)
# Remove original VREF line
del data[ivrefstart:ivrefend+1]
# Insert New Line with new VREF
new_vref = bytearray("PARALLEL_VOLTAGE_REF %s\n" % (vref),'utf-8')
data[ivrefstart:ivrefstart] = new_vref
# Write new sensitivity settings to cal file
with open(calfile, "wb") as binary_file:
binary_file.write(data)
I was able to make the changes if I load the file as text file but I have no clue how to load the same as Binary value and make the changes
Option Explicit
Dim objFso, objFolder, objFile, objOtF, cd, content
Dim targetDir
targetDir = "C:\Kiosk\UI"
Dim objRegExp
Set objRegExp = New RegExp
objRegExp.Pattern = "\bPARALLEL_VOLTAGE_TEST \d+\b"
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFolder = objFSO.GetFolder(targetDir)
For Each objFile in objFolder.Files
If LCase(Right(objFile.Name, 4)) = ".cal" Then
cd = objFile.Name
Set objOtF = objFso.OpenTextFile(cd, 1)
content = objOtF.ReadAll
objOtF.Close
Set objOtF = objFso.OpenTextFile(cd, 2)
objOtF.Write objRegExp.Replace(content, "PARALLEL_VOLTAGE_TEST 230")
objOtF.Close
Dim objRegExp1
Set objRegExp1 = New RegExp
objRegExp1.Pattern = "\bPARALLEL_VOLTAGE_REF \d+\b"
Set objOtF = objFso.OpenTextFile(cd, 1)
content = objOtF.ReadAll
objOtF.Close
Set objOtF = objFso.OpenTextFile(cd, 2)
objOtF.Write objRegExp1.Replace(content, "PARALLEL_VOLTAGE_REF 190")
objOtF.Close
End If
Next
Take a look at the following post: Read and write binary file in VBscript. You might be able to use ADODB.Stream to read and write binary data. Other approaches are explored also, including reading characters one by one into an array.
Here's the code from that post:
Function readBinary(strPath)
Dim oFSO: Set oFSO = CreateObject("Scripting.FileSystemObject")
Dim oFile: Set oFile = oFSO.GetFile(strPath)
If IsNull(oFile) Then MsgBox("File not found: " & strPath) : Exit Function
With oFile.OpenAsTextStream()
readBinary = .Read(oFile.Size)
.Close
End With
End Function
Function writeBinary(strBinary, strPath)
Dim oFSO: Set oFSO = CreateObject("Scripting.FileSystemObject")
' below lines pupose: checks that write access is possible!
Dim oTxtStream
On Error Resume Next
Set oTxtStream = oFSO.createTextFile(strPath)
If Err.number <> 0 Then MsgBox(Err.message) : Exit Function
On Error GoTo 0
Set oTxtStream = Nothing
' end check of write access
With oFSO.createTextFile(strPath)
.Write(strBinary)
.Close
End With
End Function
I have text files that are approximately 6MB in size. There are some lines that contain the NULL (Chr(0))character that I would like to remove.
I have two methods to do this: using Asc()=0 but this takes approximately 50s to complete, the other method uses InStr (line, Chr(0)) =0 (fast ~ 4sec)but the results remove vital info from the lines which contain the NULL characters.
First line of text file as example:
##MMCIBN.000NULL7NULL076059NULL7653NULL1375686349NULL2528NULL780608NULL10700NULL\NULL_NC_ACT.DIR\CFG_RESET.INI
First method (works but VERY slow)
function normalise (textFile )
Set fso = CreateObject("Scripting.FileSystemObject")
writeTo = fso.BuildPath(tempFolder, saveTo & ("\Output.arc"))
Set objOutFile = fso.CreateTextFile(writeTo)
Set objFile = fso.OpenTextFile(textFile,1)
Do Until objFile.AtEndOfStream
strCharacters = objFile.Read(1)
If Asc(strCharacters) = 0 Then
objOutFile.Write ""
nul = true
Else
if nul = true then
objOutFile.Write(VbLf & strCharacters)
else
objOutFile.Write(strCharacters)
end if
nul = false
End If
Loop
objOutFile.close
end function
The output looks like this:
##MMCIBN.000
7
076059
7653
1375686349
2528
780608
10700
\
_NC_ACT.DIR\CFG_RESET.INI
Second method code:
filename = WScript.Arguments(0)
Set fso = CreateObject("Scripting.FileSystemObject")
sDate = Year(Now()) & Right("0" & Month(now()), 2) & Right("00" & Day(Now()), 2)
file = fso.BuildPath(fso.GetFile(filename).ParentFolder.Path, saveTo & "Output " & sDate & ".arc")
Set objOutFile = fso.CreateTextFile(file)
Set f = fso.OpenTextFile(filename)
Do Until f.AtEndOfStream
line = f.ReadLine
If (InStr(line, Chr(0)) > 0) Then
line = Left(line, InStr(line, Chr(0)) - 1) & Right(line, InStr(line, Chr(0)) + 1)
end if
objOutFile.WriteLine line
Loop
f.Close
but then the output is:
##MMCIBN.000\CFG_RESET.INI
Can someone please guide me how to remove the NULLS quickly without losing information. I have thought to try and use the second method to scan for which line numbers need updating and then feed this to the first method to try and speed things up, but quite honestly I have no idea where to even start doing this!
Thanks in advance...
It looks like the first method is just replacing each NULL with a newline. If that's all you need, you can just do this:
Updated:
OK, sounds like you need to replace each set of NULLs with a newline. Let's try this instead:
strText = fso.OpenTextFile(textFile, 1).ReadAll()
With New RegExp
.Pattern = "\x00+"
.Global = True
strText = .Replace(strText, vbCrLf)
End With
objOutFile.Write strText
Update 2:
I think the Read/ReadAll methods of the TextStream class are having trouble dealing with the mix of text and binary data. Let's use an ADO Stream object to read the data instead.
' Read the "text" file using a Stream object...
Const adTypeText = 2
With CreateObject("ADODB.Stream")
.Type = adTypeText
.Open
.LoadFromFile textFile
.Charset = "us-ascii"
strText = .ReadText()
End With
' Now do our regex replacement...
With New RegExp
.Pattern = "\x00+"
.Global = True
strText = .Replace(strText, vbCrLf)
End With
' Now write using a standard TextStream...
With fso.CreateTextFile(file)
.Write strText
.Close
End With
I tried this method (update2) for reading a MS-Access lock file (Null characters terminated strings in 64 byte records) and the ADODB.Stream didn't want to open an already in use file. So I changed that part to :
Set fso = CreateObject("Scripting.FileSystemObject")
Set f = fso.GetFile(Lfile)
z = f.Size
set ts = f.OpenAsTextStream(ForReading, 0) 'TristateFalse
strLog = ts.Read(z)
ts.Close
set f = nothing
' replace 00 with spaces
With New RegExp
.Pattern = "\x00+"
.Global = True
strLog = .Replace(strLog, " ")
End With
' read MS-Access computername and username
for r = 1 to len(strLog) step 64
fnd = trim(mid(strLog,r, 32)) & ", " & trim(mid(strLog,r+32, 32)) & vbCrLf
strRpt = strRpt & fnd
next
I am writing a utility to collect all system information from all devices on a network to an XML document, and one of the values is a certain software version number. Unfortunately the version number is only stored in a single text file on each machine (c:\master.txt) the even more fun part is, each text file is formatted differently depending on the image that was used.
One could say
Product Number: 11dsSt2 BRANDONII,STNS6.0.2.200
The next
Ver: 22335TS BOX2 S6.1.3.011,STN
and so on.
What I did, is create a VBS that looks for a number pattern that matches the version pattern, which is
[A-Z]#.#.#.###
This works, however I just want to output that pattern, not the entire line. Here is my code. Any Suggestions?
Const ForReading = 1
Set objRegEx = CreateObject("VBScript.RegExp")
objRegEx.Pattern = "[A-Z]{1}[0-9]{1}.[0-9]{1}.[0-9]{1}.[0-9]{3}"
objregex.global = true
objregex.ignorecase = true
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("C:\master.txt", ForReading)
Do Until objFile.AtEndOfStream
strSearchString = objFile.ReadLine
set colMatches = objRegEx.Execute(strSearchString)
If colMatches.Count > 0 Then
Addmatch
End If
Loop
objFile.Close
Sub Addmatch 'Creates a text file with the part number
Const ForAppending = 8
Set objFSO = CreateObject("Scripting.FileSystemObject")
set objFile1 = objFSO.OpenTextFile("C:\test.txt", ForAppending, True)
objFile1.Writeline strSearchString
objFile1.Close
end sub
You're storing the entire line you read from the file (that you have in strSearchString) instead of just the matched text. Use something like this instead (untested!):
if ColMatches.Count > 0 then
AddMatch(ColMatches(0)) ' Just grab the matched text and pass to AddMatch
End If
Sub AddMatch(strMatchedText) ' Change to accept parameter of text to write out
' Other code
objFile1.Writeline strMatchedText
objFile1.Close
End Sub
Use the first/one and only match to obtain the value, and pass this to a slightly modified version of Addmatch:
If colMatches.Count > 0 Then
Addmatch colMatches(0).Value
End If
...
Sub Addmatch(sWhatToWriteLn) ' some truthful comment
Const ForAppending = 8
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile1 = objFSO.OpenTextFile("C:\test.txt", ForAppending, True)
objFile1.Writeline sWhatToWriteLn
...
I'm new to vbscripting and I just received a task that requires me to find 6 files with matching strings in the filename so that I can move these files to a different directory. I am using the regex pattern "\d{8}-\d{6}" to locate all of the strings within the filenames.
How would I go about in doing a search in a directory and checking to see if there are 6 files with matching strings in their filenames so that I can store them into an array and then move the files to another directory?
The script I have written so far:
Set objFS = CreateObject("Scripting.FileSystemObject")
strShareDirectory = "in\"
strDumpStorageDir = "out\"
Set objFolder = objFS.GetFolder(strShareDirectory)
Set colFiles = objFolder.Files
Set re = New RegExp
re.Global = True
re.IgnoreCase = False
re.Pattern = "-\d{8}-\d{6}"
Dim curFile, matchValue
Dim i: i = 0
For Each objFile in colFiles
bMatch = re.Test(objFile.Name)
curFile = objFile.Name
If bMatch Then
ReDim preserve matches(i)
Matches(i) = curFile
i = (i + 1)
For Each objFile1 in colFiles
If objFile1.Name <> objFile.Name Then
For each match in re.Execute(objFile1.Name)
matchValue = match.Value
Exit For
Next
If (Instr(curFile, matchValue) > 0) Then
matchCount = 1
For Each match1 in re.Execute(objFile1.Name)
curFile1 = objFile1.Name
matchValue1 = match1.Value
Exit For
'If Then
Next
'msgbox(curFile1)
End If
End If
Next
End If
Next
Here is what my sample directory that I am working with looks like.
As #KekuSemau's proposal does not address the (sub)problem of grouping the files, dweebles does not give the full story (Why the array? Why the insistence on having a full (sub)set of files?), and the numbers (group of 6, 3/4 parts in a file name) aren't really relevant to the basic task - distribute a set files into folders based on parts of the file name - I claim that the way to solve the task is to get rid of all the array, dictionary, and regexp fancies and to keep it simple:
Before:
tree /A /F ..\data
+---in
| B-2
| B-1
| A-3
| A-2
| B-3
| A-1
|
\---out
Code:
Const csSrc = "..\data\in"
Const csDst = "..\data\out"
Dim f, n, d
For Each f In goFS.GetFolder(csSrc).Files
n = Split(f.Name, "-")
If 1 = UBound(n) Then
d = goFS.BuildPath(csDst, n(1))
If Not goFS.FolderExists(d) Then goFS.CreateFolder d
f.Move goFS.BuildPath(d, f.Name)
End If
Next
After:
tree /A /F ..\data
+---in
\---out
+---3
| A-3
| B-3
|
+---1
| B-1
| A-1
|
\---2
B-2
A-2
P.S.
This problem can be solved using the same approach.
Ah, now I understand.
So: you need all file names that match the pattern IF there are at least 6 files with the same matching sub string. Okay. Then, yes, I understand that you can get strangled up in nested for..next loops. If that happens, I would recommend to put some code into extra functions.
In this solution, I use dictionaries to do some work much easier (every call to 'exists' is another nested iteration over all its elements for example, and every assignment as well).
This example would ignore multiple matches within one file name.
option explicit
dim objFS : dim strShareDirectory : dim strDumpStorageDir : dim objFolder : dim colFiles : dim re : dim objFile
dim dictResults ' dictionary of [filename] -> [matching substring]
dim dictResultsCount ' dictionary of [matching substring] -> [count]
dim dictResultsFinal ' only the valid entries from dictResults
dim keyItem
dim strMatch
set dictResultsFinal = CreateObject("Scripting.Dictionary")
set dictResults = CreateObject("Scripting.Dictionary")
set dictResultsCount = CreateObject("Scripting.Dictionary")
Set objFS = CreateObject("Scripting.FileSystemObject")
strShareDirectory = "in\"
strDumpStorageDir = "out\"
Set objFolder = objFS.GetFolder(strShareDirectory)
Set colFiles = objFolder.Files
Set re = New RegExp
re.Global = True
re.IgnoreCase = False
re.Pattern = "-\d{8}-\d{6}"
Dim curFile, matchValue
Dim i: i = 0
For Each objFile in colFiles
' test if the filename matches the pattern
if re.test(objFile.Name) then
' for now, collect all matches without further checks
strMatch = re.execute(objFile.Name)(0)
dictResults(objFile.Name) = strMatch
' and count
if not dictResultsCount.Exists(strMatch) then
dictResultsCount(strMatch) = 1
else
dictResultsCount(strMatch) = dictResultsCount(strMatch) +1
end if
end if
next
' for testing: output all filenames that match the pattern
msgbox join(dictResults.keys(), vblf)
' now copy only the valid entries into a new dictionary
for each keyItem in dictResults.keys()
if dictResultsCount.Exists( dictResults(keyItem) ) then
if dictResultsCount( dictResults(keyItem) ) >= 6 then
dictResultsFinal(keyItem) = 1
end if
end if
next
' test output the final result
msgbox join(dictResultsFinal.keys(), vblf)
--- my first answer
Well I should probably ask what have you tried but... here's your example ^^.
This should give you enough to start from (I ignored that '6' requirements you mentioned). Ask if you need more explanations.
Option explicit
dim a
a = findFiles("G:\", "\d{8}-\d{6}")
msgbox join(a, vblf)
function findFiles(path, pattern)
dim rx
dim fso
dim fsoFolder
dim fsoFiles
dim results
dim item
set rx = new regexp
rx.pattern = pattern
set results = CreateObject("Scripting.Dictionary")
set fso = CreateObject("Scripting.FileSystemObject")
set fsoFolder = fso.GetFolder(path)
set fsoFiles = fsoFolder.Files
for each item in fsoFiles
if rx.test(item.name) then results(item.name) = 1
next
set fso = nothing
set fsoFolder = nothing
set fsoFiles = nothing
findFiles = results.keys()
end function
I would like to remove the unwanted text from each string in a file.
the input string looks like this
username^time stamp^don't need this printed on printer name more useless info pages printed:some number
I want to remove everything else but keep the username,time stamp,printer name and some number.Then write each line to a file so the output looks like this
username timestamp printername some number
This is the code I'm working with
Set fs = CreateObject("Scripting.FileSystemObject")
sf = "C:\test.txt"
Set f = fs.OpenTextFile(sf, 1) ''1=for reading
s = f.ReadAll
segments = Split(s,"^",-1)
s= segments(1,)
f.Close
Set f = fs.OpenTextFile(sf, 2) ''2=ForWriting
f.Write s
f.Close
There's always a moment that somebody asks "Why not use a regular expression?". This is that moment.
Try this:
Dim re, s, match, matches
s = "Chuck Norris^12-12-2012^don't need this printed on HAL9000 more useless info pages printed:42 "
Set re = new regexp
re.pattern = "(.*)\^(.*)\^.*printed on (\w+).*pages printed:(\d+).*"
re.Global = True
Set matches = re.Execute(s)
Set match = matches(0)
msgbox "username=" & match.submatches(0)
msgbox "time stamp=" & match.submatches(1)
msgbox "printer=" & match.submatches(2)
msgbox "pages printed=" & match.submatches(3)
Neat huh? And I bet you'll figure out how to implement it in your existing code.
Code:
Const csSep = "^"
'username^time^(other arbitrary junk)^printer name^(other arbitrary junk)^page count
Dim sJunk : sJunk = "kurt^01:02:03^some junk^nec p7^nix^123"
WScript.Echo sJunk
Dim aParts : aParts = Split(sJunk, csSep)
Dim sNetto : sNetto = Join(Array(aParts(0),aParts(1),aParts(3),aParts(5)), csSep)
WScript.Echo sNetto
output:
kurt^01:02:03^some junk^nec p7^nix^123
kurt^01:02:03^nec p7^123