Counting character position after two or more spaces
I am trying to return a value equal to the position of the first letter following two or more spaces.
I have a tool that extracts variable column length tables into TXT documents. I need to get these tables into an Excel sheet without having to set a fixed width for each column in each table (this is a lot of coding to do). I'm trying to find something more dynamic to do this based on the position of the first character after two or more spaces.
Whereas not all rows are completely filled, but the first row will make a perfect candidate for getting the column width.
To give an example, lines of text would look like this:
John & ensp; & EnSP; & ensp; & ensp; Robert & ensp; & EnSP; & EnSP; Eric & ensp; & EnSP; & EnSP; & EnSP; Tom
10 & ensp; & EnSP; & EnSP; & EnSP; & ensp; 11 & ensp; & EnSP; & EnSP; & EnSP; & EnSP; & EnSP; 143 & ensp; & ensp; & EnSP; & EnSP; 43
21 & EnSP; & EnSP; & EnSP; & EnSP; & EnSP; & EnSP; & EnSP; & EnSP; & EnSP; & EnSP; & EnSP; & EnSP; & ensp; & ensh; 265 & ensp; & EnSP; & EnSP; & EnSP; 56
99 & EnSP; & EnSP; & EnSP; & EnSP; & EnSP; 241 & EnSP; & EnSP; & EnSP; & EnSP; & EnSP; & EnSP; & EnSP; & EnSP; & EnSP; & EnSP; & EnSP; & EnSP; & EnSP; 76
All I have gotten so far is to get it to work with a fixed width according to the code below
Sub exporttosheet()
Dim fPath As String
fPath = "C:\test.txt"
Const fsoForReading = 1
Const F_LEN_A As Integer = 10
Const F_LEN_B As Integer = 23
Const F_LEN_C As Integer = 7
Const F_LEN_D As Integer = 10
Dim objFSO As Object, objTextStream As Object, txt, f1, f2, f3, f4
Dim start As Integer
Dim fLen As Integer
Dim rw As Long
Set objFSO = CreateObject("scripting.filesystemobject")
Set objTextStream = objFSO.OpenTextFile(fPath, fsoForReading)
rw = 1
Do Until objTextStream.AtEndOfStream
txt = objTextStream.Readline
f1 = Trim(Left(txt, F_LEN_A))
start = F_LEN_A + 1
f2 = Trim(Mid(txt, start, F_LEN_B))
start = start + F_LEN_B + 1
f3 = Trim(Mid(txt, start, F_LEN_C))
start = start + F_LEN_C + 1
f4 = Trim(Mid(txt, start, F_LEN_D))
With ThisWorkbook.Sheets("data").Cells(rw, 1).Resize(1, 4)
.NumberFormat = "@" 'format cells as text
.Value = Array(f1, f2, f3, f4)
End With
rw = rw + 1
Loop
objTextStream.Close
End Sub
source to share
Instead of any confirmation from you I guess I really have unicode characters in your evidence.
Option Explicit
Sub Split_My_Data()
Dim s As Long, str As String, tmp As Variant, varFieldInfo As Variant
ReDim tmp(0 To 0)
With Worksheets("Sheet3")
str = .Cells(1, 1).Value2
s = Application.Max(InStrRev(str, Chr(32)), _
InStrRev(str, ChrW(8194)))
Do While CBool(s)
tmp(UBound(tmp)) = Array(s, 1)
str = Left(str, s)
Do While Right(str, 1) = Chr(32) Or Right(str, 1) = ChrW(8194): str = Left(str, Len(str) - 1): Loop
s = Application.Max(InStrRev(str, Chr(32)), _
InStrRev(str, ChrW(8194)))
ReDim Preserve tmp(LBound(tmp) To UBound(tmp) + 1)
If Not CBool(s) Then Exit Do
Loop
'make the last (first) fieldinfo element
tmp(UBound(tmp)) = Array(0, 1)
'make room for the reversed fieldinfo
ReDim varFieldInfo(LBound(tmp) To UBound(tmp))
'reverse the fieldinfo array
For s = UBound(tmp) To LBound(tmp) Step -1
varFieldInfo(UBound(tmp) - s) = tmp(s)
Next s
'run TextToColumns with the new array of arrays for FieldInfo
.Columns("A:A").TextToColumns Destination:=.Cells(1, "A"), DataType:=xlFixedWidth, FieldInfo:=varFieldInfo
For s = 1 To .Cells(1, .Columns.Count).End(xlToLeft).Column
With Intersect(.Columns(s), .UsedRange).Cells
'get rid of unicode
.Replace what:=ChrW(8194), replacement:=vbNullString, lookat:=xlPart
'use another texttocolumns as a fast Trim
.TextToColumns Destination:=.Cells(1, "A"), DataType:=xlFixedWidth, FieldInfo:=Array(Array(0, 1))
'shrink/expand the column
.EntireColumn.AutoFit
.EntireColumn.ColumnWidth = Application.Max(.ColumnWidth, 9)
End With
Next s
End With
End Sub
Results with text as truncated text and numbers as real numbers (no unicode):
source to share
you can use the following function to get the length of the columns from the header row:
Function GetF_LENs(txt As Variant, nCols As Long) As Variant
Dim t As Variant
Dim iFLEN As Long
t = Split(WorksheetFunction.Trim(txt), " ")
nCols = UBound(t) + 1 '<--| the number of columns equals the number of found values
ReDim FLENs(1 To nCols - 1) '<--| we need the width of columns till the one before the last column
For iFLEN = 1 To nCols - 1
FLENs(iFLEN) = InStr(txt, t(iFLEN))
Next
GetF_LENs = FLENs
End Function
and you can use it in your code like this:
Sub exporttosheet()
Const fsoForReading = 1
Dim fPath As String
fPath = "C:\test.txt"
Dim F_LENs As Variant, txt As Variant
Dim objFSO As Object, objTextStream As Object
Dim rw As Long, nCols As Long
Set objFSO = CreateObject("scripting.filesystemobject")
Set objTextStream = objFSO.OpenTextFile(fPath, fsoForReading)
txt = objTextStream.Readline '<--| read the first "header" line
F_LENs = GetF_LENs(txt, nCols) '<--| get 'F_LENs' array out of "header" line: it stores the widths of all columns
ReDim values(1 To nCols) '<--| resize the array that will hold each row values accordingly to the number of columns encountered
rw = 1
Do Until objTextStream.AtEndOfStream
ReadValuesAndWriteCells txt, F_LENs, values, nCols, rw
txt = objTextStream.Readline '<--| read the first "header" line
Loop
ReadValuesAndWriteCells txt, F_LENs, values, nCols, rw
objTextStream.Close
End Sub
where i typed the current line, reading and writing the next sub
Sub ReadValuesAndWriteCells(txt As Variant, F_LENs As Variant, values As Variant, nCols As Long, rw As Long)
Dim start As Integer
Dim fLen As Integer
start = 1
For fLen = 1 To nCols - 1 '<--| loop through 'F_LENs' array, i.e.: through current line columns
values(fLen) = Trim(Mid(txt, start, F_LENs(fLen) - start)) '<-- store current line current column value in corresponding 'Values' index
start = F_LENs(fLen)
Next
values(fLen) = Trim(Mid(txt, start)) '<-- store current line last column value
With ThisWorkbook.Sheets("data").Cells(rw, 1).Resize(1, nCols)
.NumberFormat = "@" 'format cells as text
.Value = values '<--| write current line array values
End With
rw = rw + 1
End Sub
source to share
You can try this function below:
Public Function InterpretLine(strLine As String) As Variant
Dim rgxCell As RegExp: Set rgxCell = New RegExp
rgxCell.Pattern = "([^ ]+([ ]?[^ ]+)*)"
rgxCell.Global = True
Dim mtcResult As MatchCollection: Set mtcResult = rgxCell.Execute(strLine)
Dim strResult() As String: ReDim strResult(0 To mtcResult.Count - 1)
Dim i As Long: For i = 0 To mtcResult.Count - 1
strResult(i) = mtcResult.Item(i)
Next i
InterpretLine = strResult
End Function
It takes a string as a string value and returns an array of strings (each element is a cell from a string). My guess is that none of the cells contain 2 consecutive space characters, and there are always at least two spaces between the cells. (Here, whitespace only means the one that is entered via the long key on the keyboard, inserts of pill rows, etc. Not included.)
To use Regex in VBA you need the following link (in the VBA editor choose Tools> Links) and check the following option:
source to share