How to find total size of files in ReadOnlyCollection(Of String) - vb.net

I have the below code that collects certain files in a directory and places them in a collection of string. I would like to find the total size of all files in that collection. I have tried .Length, however it says that Length is not a member of Collection(Of String). I have also tried working with FileInfo, however, it doesn't seem to work for what I need. Ultimately, I want to display the total size in a messagebox.
Dim counter = My.Computer.FileSystem.GetFiles(TextBox1.Text,
FileIO.SearchOption.SearchAllSubDirectories, "*.mp4", "*.jpg", "*.wav", "*.pf_import")
Here is an example of one attempt, however it gives me the value of 42, when the true value is 16.5MB.
Dim fi As String
For Each fi In counter
FileLen += fi.Length
I also tried this example, however, it gives me 0.
Dim fi As String
Dim filesize As Integer
For Each fi In counter
FileLen += filesize
Can someone give me an idea of what I'm doing wrong?

Iterate through each file and call the GetFileInfo function and retrieve the Length property which is the size in bytes:
Dim length As Integer = 0
For Each f In My.Computer.FileSystem.GetFiles(TextBox1.Text,
FileIO.SearchOption.SearchAllSubDirectories, "*.mp4", "*.jpg", "*.wav", "*.pf_import")
length += My.Computer.FileSystem.GetFileInfo(f).Length
Next
Then convert from bytes to megabytes:
Dim totalKB As Double = length/1024.0 ' To Kilobytes
Dim totalMB As Double = totalKB/1000 ' To Megabytes

Related

New to programming language, need help to read a .dat file into array Vs VB, console .net framework

I am trying to read from a data file called TXT.dat and store the circled values into a separate array's using the type of commands I have used. I cannot use streamreader,streamwriter this is what I am learning at university but we were taught to read an array not certain values as I will be tested using a file which has
2 factorial values from a single file and store int two arrays.
I have spent hours trying before going to paid sites who always answer with i/o streamreader which doesnt help
.dat file i created to practice
MY CODE
Sub Main()
Const i_val As Integer = 6
Dim j As Integer = 6 'loop readers
Dim Arayn_Fact(i_val - 1) As Double 'array for 2nd value per line
Dim Aray_Fact2n(j - 1) As Double 'array for 4th value per line
Read_Values(i_val - 1, Arayn_Fact)
End Sub
Sub Read_Values(ByVal i As Integer, ByRef _A() As Double)
Dim fid1 As Integer = FreeFile()
Dim fid2 As Integer = FreeFile() + 1
Dim tmp As Double
FileOpen(fid1, "TXT.dat", OpenMode.Input, OpenAccess.Read)
For i = 0 To 5 Step 1
Input(fid1, tmp)
Input(fid1, _A(i))
Next i
FileClose(fid1)
Console.ReadKey()
End Sub
Without getting too fancy, here's some very basic code to do what you've outlined:
Sub Main()
Dim fileName As String = "txt.dat"
Dim Aray_Fact2() As Integer 'array for 2nd value per line
Dim Aray_Fact4() As Integer 'array for 4th value per line
Dim list2 As New List(Of Integer)
Dim list4 As New List(Of Integer)
Dim lines() As String = System.IO.File.ReadAllLines(fileName)
For Each line As String In lines
Dim values() As String = line.Split(",")
If values.Length >= 3 Then
Dim f2, f4 As Integer
If Integer.TryParse(values(1), f2) AndAlso Integer.TryParse(values(3), f4) Then
list2.Add(f2)
list4.Add(f4)
Else
Console.WriteLine("Invalid value in line: " & line)
End If
Else
Console.WriteLine("Invalid number of entries in line: " & line)
End If
Next
Aray_Fact2 = list2.ToArray
Aray_Fact4 = list4.ToArray
Console.WriteLine("Aray_Fact2: " & String.Join(",", Aray_Fact2))
Console.WriteLine("Aray_Fact4: " & String.Join(",", Aray_Fact4))
Console.Write("Press Enter to Quit...")
Console.ReadLine()
End Sub
If you are completely against the use of Lists and ToArray, then you'd have to read the file TWICE. Once to determine how many lines are in the file so you can dimension the arrays to the correct size. Then another time to actually read the values and populate the arrays.
First, some style things. Passing the array ByRef is incorrect. Arrays are already reference types, and so passing ByVal still passes the reference to the array object. But don't even pass the array in the first place. It's better practice to let the Read_Values() method create and return the array to the calling function.
And no one uses that ancient/ganky FileOpen() API anymore. I mean that. NO. ONE. It's not even appropriate for teaching, as the Stream types are closer to what the low level operating system/file system are doing. If you can't use StreamReader and friends, there are still better options out there.
Sub Main()
Dim Array_Fact() As Double = Read_Values("TXT.dat", 1)
Dim Array_Fact2n() As Double = Read_Values("TXT.dat", 3)
Console.ReadKey(True)
End Sub
Function Read_Values(filePath As String, position As Integer) As Double()
Dim lines() As String = File.ReadAllLines(filePath) 'Look Ma, no StreamReader
Dim result(lines.Length - 1) As Double
For i As Integer = 0 To Lines.Length - 1
result(i) = Double.Parse(lines(i).Split(",")(position).Trim())
Next line
Return result
End Function
But what I'd really do is keep the 2nd and 4th values in the same collection and read everything in one pass through the file. This uses features like Generics, Tuples, Lambdas+Linq, and more that you won't get to for some time, but I feel like it's useful to show you where you're going:
Sub Main()
Dim Facts = Read_Values("TXT.dat")
Console.WriteLine(String.Join(",",Facts.Select(Function(f) $"({f.Item1}:{f.Item2})")))
Console.ReadKey(True)
End Sub
Function Read_Values(filePath As String) As IEnumerable(Of (Double, Double))
Return File.ReadLines(filePath).Select(
Function(line) 'Poor man's CSV. In real code, I'd pull in a CSV parser from NuGet
Dim fields = line.Split(",")
Return (Double.Parse(fields(1).Trim()), Double.Parse(fields(3).Trim()))
End Function
)
End Function

Reading from text files in Visual Basic

This is the first challenge on Day 1 of the 2018 Advent of Code
(link: https://adventofcode.com/2018/day/1)
So I am trying to create a program that reads a long list of positive and negative numbers (e.g +1, -2, +3, etc.) and then add them up to create a total. I have researched some methods of file handling in Visual Basic, and have come up with the below method:
Sub Main()
Dim objStreamReader As StreamReader
Dim strLine As String = ""
Dim total As Double = 0
objStreamReader = New StreamReader(AppDomain.CurrentDomain.BaseDirectory & "frequencies.txt")
strLine = objStreamReader.ReadLine
Do While Not strLine Is Nothing
Console.WriteLine(strLine)
strLine = objStreamReader.ReadLine
total += strLine
Loop
Console.WriteLine(total)
objStreamReader.Close()
Console.ReadLine()
End Sub
Here is a link to the list of numbers: https://adventofcode.com/2018/day/1/input
It is not a syntax error I am getting but a logic error. The answer is somehow wrong, but I cannot seem to figure out where! I have tried to remove the signs from each number but that throws me a NullException error when it compiles.
So far I have come out with the answer 549, which the Advent of Code webiste rejects. Any ideas?
Make your life easier by using File.ReadLines(fileName) instead of dealing with StreamReader. Use Path.Combine instead of string concatenation to create a path. Path.Combine takes care of adding missing \ or removing extra ones etc.
Your file might contain an extra empty line at its end, that does not convert to a number. Use Double.TryParse to make sure you have a valid number before totalizing it. You should have Option Strict On anyway to enforce explicit conversions.
Dim fileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "frequencies.txt")
Dim total As Double = 0
For Each strLine As String In File.ReadLines(fileName)
Console.WriteLine(strLine)
Dim n As Double
If Double.TryParse(strLine, n) Then
total += n
End If
Next
Console.WriteLine(total)
Console.ReadLine()
For appending two string, please use string builder.
Dim test as new stringbuilder()
Test.append("your string")
It will not affect performance.

How to replace bytes in VB.NET?

I have two strings:
Dim Original_Hex_Bytes as string = "616572646E61"
Dim Patched_Hex_Bytes as string = "616E64726561"
Then I have a binary file and I need to search for the Original_Hex_Bytes and replace them with Patched_Hex_Bytes; I don't konw the offset where begin to write new bytes :(
How can I do this?
If needed, I also know how to convert Hex strings in bytes, I use this:
Private Function Hex_To_Bytes(ByVal strinput As String) As Byte()
Dim i As Integer = 0
Dim x As Integer = 0
Dim bytes(strinput.Length / 2) As Byte
Do While (strinput.Length > i + 1)
Dim lngDecimal As Long = Convert.ToInt32(strinput.Substring(i, 2), 16)
bytes(x) = Convert.ToByte(lngDecimal)
i += 2
x += 1
Loop
Return bytes
End Function
You can use BinaryReader and BinaryWriter classes to achieve this.
But in this case, as you do not know the file structure, need to read the entire file and sweep it in search of bytes array and will be easier to use ASCII strings as aerdna and andrea.
When you know the structure of a file is more appropriate to work with data structure to manipulate its contents.

build an array of integers inside a for next loop vb.net

i got this far ... my data string, "num_str" contains a set of ~10 numbers, each separated by a comma. the last part of the string is a blank entry, so i use '.Trim' to avoid an error
Dim i As Integer
Dim m_data() As String
m_data = num_str.Split(",")
For i = 0 To UBound(m_data)
If m_data(i).Trim.Length > 0 Then
MsgBox(Convert.ToInt32(m_data(i).Trim))
End If
Next i
as you can see from the Msgbox, each of the numbers successfully pass through the loop.
where i am stuck is how to place all of the 'Convert.ToInt32(m_data(i).Trim)' numbers, which are now presumably integers, into an array.
how do i build an array of integers inside the For / Next loop so i can find MAX and MIN and LAST
TIA
You just need to initialize the array with the zero-based indexer. You can deduce it's initial size from the size of the string():
Dim m_data = num_str.Split({","c}, StringSplitOptions.RemoveEmptyEntries)
Dim intArray(m_data.Length) As Int32
For i = 0 To m_data.Length - 1
intArray(i) = Int32.Parse(m_data(i).Trim())
Next i
Note that i've also used the overload of String.Split which removes empty strings.
This way is more concise using the Select LINQ Operator.
Dim arrayOfInts = num_str.
Split({","c},
StringSplitOptions.RemoveEmptyEntries).
Select(Function(v) Int32.Parse(v.Trim()))
Dim minInt = arrayOfInts.Min()
Dim maxint = arrayOfInts.Max()

linq submitchanges runs out of memory

I have a database with about 180,000 records. I'm trying to attach a pdf file to each of those records. Each pdf is about 250 kb in size. However, after about a minute my program starts taking about about a GB of memory and I have to stop it. I tried doing it so the reference to each linq object is removed once it's updated but that doesn't seem to help. How can I make it clear the reference?
Thanks for your help
Private Sub uploadPDFs(ByVal args() As String)
Dim indexFiles = (From indexFile In dataContext.IndexFiles
Where indexFile.PDFContent = Nothing
Order By indexFile.PDFFolder).ToList
Dim currentDirectory As IO.DirectoryInfo
Dim currentFile As IO.FileInfo
Dim tempIndexFile As IndexFile
While indexFiles.Count > 0
tempIndexFile = indexFiles(0)
indexFiles = indexFiles.Skip(1).ToList
currentDirectory = 'I set the directory that I need
currentFile = 'I get the file that I need
writePDF(currentDirectory, currentFile, tempIndexFile)
End While
End Sub
Private Sub writePDF(ByVal directory As IO.DirectoryInfo, ByVal file As IO.FileInfo, ByVal indexFile As IndexFile)
Dim bytes() As Byte
bytes = getFileStream(file)
indexFile.PDFContent = bytes
dataContext.SubmitChanges()
counter += 1
If counter Mod 10 = 0 Then Console.WriteLine(" saved file " & file.Name & " at " & directory.Name)
End Sub
Private Function getFileStream(ByVal fileInfo As IO.FileInfo) As Byte()
Dim fileStream = fileInfo.OpenRead()
Dim bytesLength As Long = fileStream.Length
Dim bytes(bytesLength) As Byte
fileStream.Read(bytes, 0, bytesLength)
fileStream.Close()
Return bytes
End Function
I suggest you perform this in batches, using Take (before the call to ToList) to process a particular number of items at a time. Read (say) 10, set the PDFContent on all of them, call SubmitChanges, and then start again. (I'm not sure offhand whether you should start with a new DataContext at that point, but it might be cleanest to do so.)
As an aside, your code to read the contents of a file is broken in at least a couple of ways - but it would be simpler just to use File.ReadAllBytes in the first place.
Also, your way of handling the list gradually shrinking is really inefficient - after fetching 180,000 records, you're then building a new list with 179,999 records, then another with 179,998 records etc.
Does the DataContext have ObjectTrackingEnabled set to true (the default value)? If so, then it will try to keep a record of essentially all the data it touches, thus preventing the garbage collector from being able to collect any of it.
If so, you should be able to fix the situation by periodically disposing the DataContext and creating a new one, or turning object tracking off.
OK. To use the smallest amount of memory we have to update the datacontext in blocks. I've put a sample code below. Might have sytax errors since I'm using notepad to type it in.
Dim DB as YourDataContext = new YourDataContext
Dim BlockSize as integer = 25
Dim AllItems = DB.Items.Where(function(i) i.PDFfile.HasValue=False)
Dim count = 0
Dim tmpDB as YourDataContext = new YourDataContext
While (count < AllITems.Count)
Dim _item = tmpDB.Items.Single(function(i) i.recordID=AllItems.Item(count).recordID)
_item.PDF = GetPDF()
Count +=1
if count mod BlockSize = 0 or count = AllItems.Count then
tmpDB.SubmitChanges()
tmpDB = new YourDataContext
GC.Collect()
end if
End While
To Further optimise the speed you can get the recordID's into an array from allitems as an anonymous type, and set DelayLoading on for that PDF field.