I wrote VBA code that opens all of the .xls files in a folder, and copies and pastes desired information out of those files into a new workbook.
Each row in the new workbook is associated with a file from the folder.
Column1, Column2, Column3
FileName1, ABC, XYZ
FileName2, DEF, TUV
The info in Column3 has the formatting of
ArbitraryString1(Very_Important_Info)ArbitraryString2
Because I wanted Column3 to look nice, I iterated over every row and used
Range("C"&X).TextToColumns DataType:=xlDelimited, Other:=True _
OtherChar:="("
Columns("E:E").Insert Shift:=xlToRight *
Range("D"&X).TextToColumn DataType:=xlDelimited, Other:=True _
OtherChar:=")"
Range("C"&X).TextToColumns DataType:=xlDelimited, Other:=True _
OtherChar:="(" **
Columns("E:Z").Delete
Columns("C:C").Delete
*This is needed so when I call TextToColumn the second time I do not get a message asking if I want to overwrite what is already in that column.
I end up with
Column1, Column2, Column3
FileName1, ABC, Very_Important_Info_1
FileName2, DEF, Very_Important_Info_2
After calling OtherChar:="(" the first time I end up seeing
(Very_Important_Info)ArbitraryString2
with the left bracket still attached.
I call the method a second time or ColumnC would look like
(Very_Important_Info
Might work better with SPLIT
Sub TextToCols()
Dim rng As Range
Dim r As Range
Dim l As Long
Dim arr As Variant
With ActiveSheet
l = .Cells(.Rows.Count, "C").End(xlUp).Row
Set rng = Range("C2:C" & l)
For Each r In rng
r.Value = Application.WorksheetFunction.Substitute(r.Value, ")", "(")
arr = Split(r.Value, "(")
Cells(r.Row, 3).Value = arr(1)
Next r
End With
End Sub
Or text-to-columns:
Sub TextToCols()
Dim rng As Range
Dim r As Range
Dim l As Long
With ActiveSheet
l = .Cells(.Rows.Count, "C").End(xlUp).Row
Set rng = Range("C2:C" & l)
For Each r In rng
r.Value = Application.WorksheetFunction.Substitute(r.Value, ")", "(")
Next r
With .UsedRange.Columns("C").Cells
.TextToColumns Destination:=Range("C1"), _
DataType:=xlDelimited, _
OtherChar:="("
End With
End With
End Sub
Here is a simplified version of Michal Rosa's code:
Sub BeautifyIt()
With Worksheets("Sheet1")
With .Range("C2", .Range("C" & .Rows.Count).End(xlUp))
.Replace ")", "("
.TextToColumns Destination:=.Cells(1, 1), DataType:=xlDelimited, _
TextQualifier:=xlDoubleQuote, ConsecutiveDelimiter:=False, Tab:=True, _
Semicolon:=False, Comma:=False, Space:=False, Other:=True, _
OtherChar:="(", FieldInfo:=Array(Array(1, 1), Array(2, 1)), TrailingMinusNumbers:=True
End With
End With
End Sub
Related
I'm using text to columns as part of a VBA macro to separate timestamps into 2 other columns. When I format Column B to dd/mm/yyyy it uses the current year 2020 instead of 2019. Is there a way to adjust my macro to pull the year from the original timestamp or alternatively, pull the year from Column C once Text to Columns has completed?
Range("A5").Select
Range(Selection, Selection.End(xlDown)).Select
Selection.TextToColumns Destination:=Range("A5"), DataType:=xlDelimited, _
TextQualifier:=xlDoubleQuote, ConsecutiveDelimiter:=False, Tab:=False, _
Semicolon:=False, Comma:=True, Space:=False, Other:=False, FieldInfo _
:=Array(Array(1, 3), Array(2, 3), Array(3, 3)), TrailingMinusNumbers:=True
Range("B5").Select
Range(Selection, Selection.End(xlDown)).Select
Selection.NumberFormat = "dd/mm/yyyy;#"
Selection.TextToColumns Destination:=Range("B5"), DataType:=xlDelimited, _
TextQualifier:=xlDoubleQuote, ConsecutiveDelimiter:=False, Tab:=False, _
Semicolon:=False, Comma:=True, Space:=False, Other:=False, FieldInfo _
:=Array(1, 8), TrailingMinusNumbers:=True
This uses arrays:
Sub mydatesplit()
With ActiveSheet
Dim arr As Variant
arr = .Range("A5", .Cells(.Rows.Count, 1).End(xlUp)).Value
Dim outArr() As Variant
ReDim outArr(1 To UBound(arr, 1), 1 To 3)
Dim i As Long
For i = 1 To UBound(arr, 1)
Dim spltStr() As String
spltStr = Split(Replace(arr(i, 1), ",", ""), " ")
If UBound(spltStr) >= 5 Then
outArr(i, 1) = spltStr(0)
outArr(i, 2) = DateValue(spltStr(2) & " " & spltStr(1) & " " & spltStr(3))
outArr(i, 3) = TimeValue(spltStr(4) & " " & spltStr(5))
End If
Next i
.Range("B5").Resize(UBound(outArr, 1), UBound(outArr, 2)).Value = outArr
End With
End Sub
After Running:
BTW with Dynamic Array formulas newly introduced into Excel with the latest subscription one can use fairly simple formula:
Date:
=--TEXTJOIN(" ",TRUE,INDEX(TRIM(MID(SUBSTITUTE(SUBSTITUTE(A5,",","")," ",REPT(" ",999)),(ROW($1:$7)-1)*999+1,999)),{3,2,4}))
Time
=--TEXTJOIN(" ",TRUE,INDEX(TRIM(MID(SUBSTITUTE(SUBSTITUTE(A5,",","")," ",REPT(" ",999)),(ROW($1:$7)-1)*999+1,999)),{5,6}))
Based on data in the screenshot you could use the following function to split the timestamp
Function convertTimestamp(ByVal inp As String) As Variant
Dim sDate As String, sTime As String, sTimezone As String, sDay As String
Dim v As Variant
v = Split(Replace(inp, ",", ""), " ")
sDate = DateValue(v(2) & " " & v(1) & " " & v(3))
sTime = TimeValue(v(4) & " " & v(5))
sTimezone = v(6)
sDay = v(0)
ReDim v(1 To 4)
v(1) = sDay
v(2) = CDate(sDate)
v(3) = sTime
v(4) = sTimezone
convertTimestamp = v
End Function
PS Adjusted the function based on Scott's excellent approach to split the string.
Either you use this function in the worksheet itself (as an array function!) or you use the following code to split the row 5 to 8 as in your screenshot
Sub TimeStampToCol()
Dim rg As Range
Set rg = Range("A5:A8")
Dim vDat As Variant
vDat = WorksheetFunction.Transpose(rg)
Dim rDat As Variant
ReDim rDat(1 To 4, 1 To 4)
Dim i As Long, v As Variant, j As Long
For i = LBound(vDat) To UBound(vDat)
v = convertTimestamp(vDat(i))
For j = 1 To 4
rDat(i, j) = v(j)
Next j
Next i
Set rg = Range("B5:E8")
rg.Value = rDat
End Sub
Usage as an array formula
So, in a workbook I have a lot of sheets from which I want to use texttocolumns for a Date, the date is usually something like "11/22/2018 10:59:59 AM" and I only want it to do a MDY with a delimited text to columns. The delimiter must be false.
Sub LoopCertain() 'Excel VBA to exclude sheets
'SP Edit, error handler
On Error Goto errHandler
If False Then
errHandler:
MsgBox Err.Description
'This will cause the routine to carry on where it left off
Resume Next
End If
'SP End of Edit
Dim ws As Worksheet
Dim objRange1 As Range
Dim objRange2 As Range
For Each ws In ActiveWorkbook.Worksheets
Select Case ws.Name
Case Is = "General", "Verification", "OEM Plant Summary"
'No Code here if excluded
Case Else
'Set up the ranges
Set objRange1 = Range("C:C")
Set objRange2 = Range("I:I")
Set ws = ActiveSheet
'Do the first parse
objRange1.TextToColumns _
Destination:=Range("C1"), _
DataType:=xlDelimited, _
Tab:=False, _
Semicolon:=False, _
Comma:=False, _
Space:=False, _
Other:=False, _
OtherChar:="-"
'Do the second parse
objRange2.TextToColumns _
Destination:=Range("I1"), _
DataType:=xlDelimited, _
Tab:=False, _
Semicolon:=False, _
Comma:=False, _
Space:=False, _
Other:=False, _
OtherChar:="-"
End Select
Next ws
End Sub
I get errors when trying to run this. I need to change Columns "I" and "C" and results must be in the same column. Appreaciate if you can help since I have been like the whole week trying different types of loops and nothing works. This one work if I remove the Loop though...
Here is the sample data:
A fixed width would probably be more appropriate. Your sample data leaves some unanswered questions but I'll assume a mmddyyyy (not mdyyyy) date format and data that either starts in row 1 or has a header label in row 1 with 10 or less characters.
For Each ws In ActiveWorkbook.Worksheets
Select Case ws.Name
Case "General", "Verification", "OEM Plant Summary"
'No Code here if excluded
Case Else
With ws.Range("C:C")
.TextToColumns Destination:=.Cells(1), DataType:=xlFixedWidth, _
FieldInfo:=Array(Array(0, xlMDYFormat), Array(10, xlSkipColumn))
End With
With ws.Range("I:I")
.TextToColumns Destination:=.Cells(1), DataType:=xlFixedWidth, _
FieldInfo:=Array(Array(0, xlMDYFormat), Array(10, xlSkipColumn))
End With
End Select
Next ws
Why do you need to use TextToColumns? I would be tempted to convert the text to a proper date (meaning a date serial number) and then format the column in whichever way you want to display the date. The advantage of this method would be that you can actually retain the time value contained in the date if case you want to know it later but without displaying it.
However, since you are intending to manipulate the text I think the code below is more efficient. Please try it.
Sub ReformatDate()
Const FirstDataRow As Long = 1 ' change as appropriate
Dim Ws As Worksheet
Dim Rng As Range
Dim Arr As Variant
Dim C As Long
Dim i As Long
For Each Ws In Worksheets
If InStr(1, ",General,Verification,OEM Plant Summary", "," & Ws.Name, _
vbTextCompare) = 0 Then
With Ws
For C = 3 To 9 Step (9 - 3) ' columns C & I
Set Rng = .Range(.Cells(FirstDataRow, C), _
.Cells(.Rows.Count, C).End(xlUp))
Arr = Rng.Value
For i = 1 To UBound(Arr)
If Len(Arr(i, 1)) Then Arr(i, 1) = Split(Arr(i, 1))(0)
Next i
Rng.Value = Arr
.Columns(C).AutoFit
Next C
End With
Next Ws
End Sub
I have a csv with a "|"-delimeter, so I open the workbook:
Set wb = Workbooks.Open(xl_newest_export)
Set ws = wb.Sheets(1)
and use texttocolumns:
ws.Range("A1:A" & CStr(lastRow)).TextToColumns Destination:=Range("A1:A" & CStr(lastRow)), DataType:=xlDelimited, _
TextQualifier:=xlNone, ConsecutiveDelimiter:=False, Tab:=False, _
Semicolon:=False, Comma:=False, Space:=False, DecimalSeparator:=".", Other:=True, OtherChar _
:="|"
However this fails as the text gets evaluated by Excel - a few doubles are getting converted to date. e.g.
"1" -> "01.01.1900"
How can I achieve the above with no Evaluation?
Edit:
As suggested in the comments - Fieldinfo is the solution. However I tried to build a Array-string... e.g. "Array(Array(3,1)...)" but this fails too..
Once you have filled column A with data, use this instead:
Sub PiedPipper()
Dim rng As Range, r As Range, pipe As String
Cells.NumberFormat = "#"
Set rng = Range("A1:A" & Cells(Rows.Count, "A").End(xlUp).Row)
pipe = "|"
For Each r In rng
ary = Split(r.Value, pipe)
r.Resize(1, UBound(ary) + 1) = ary
Next r
End Sub
EDIT#1:
Use this to get the .csv data into column A:
Sub GetCVSData()
Dim FilesToOpen
Dim wf As WorksheetFunction
Set wf = Application.WorksheetFunction
Dim ary, bry, DQ As String
DQ = Chr(34)
FilesToOpen = Application.GetOpenFilename _
(FileFilter:="Text Files (*.csv), *.csv", Title:="Text Files to Open")
Close #1
Open FilesToOpen For Input As #1
j = 1
Do While Not EOF(1)
Line Input #1, TextLine
With Cells(j, 1)
.NumberFormat = "#"
.Value = TextLine
End With
j = j + 1
Loop
Close #1
End Sub
I have columns of numbers that, for whatever reason, are formatted as text. This prevents me from using arithmetic functions such as the subtotal function. What is the best way to convert these "text numbers" to true numbers?
Here is a screenshot of the specific issue:
I've tried these snippets to no avail:
Columns(5).NumberFormat = "0"
and
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Use the below function (changing [E:E] to the appropriate range for your needs) to circumvent this issue (or change to any other format such as "mm/dd/yyyy"):
[E:E].Select
With Selection
.NumberFormat = "General"
.Value = .Value
End With
P.S. In my experience, this VBA solution works SIGNIFICANTLY faster on large data sets and is less likely to crash Excel than using the 'warning box' method.
I had this problem earlier and this was my solution.
With Worksheets("Sheet1").Columns(5)
.NumberFormat = "0"
.Value = .Value
End With
This can be used to find all the numeric values (even those formatted as text) in a sheet and convert them to single (CSng function).
For Each r In Sheets("Sheet1").UsedRange.SpecialCells(xlCellTypeConstants)
If IsNumeric(r) Then
r.Value = CSng(r.Value)
r.NumberFormat = "0.00"
End If
Next
This converts all text in columns of an Excel Workbook to numbers.
Sub ConvertTextToNumbers()
Dim wBook As Workbook
Dim LastRow As Long, LastCol As Long
Dim Rangetemp As Range
'Enter here the path of your workbook
Set wBook = Workbooks.Open("yourWorkbook")
LastRow = Cells.Find(What:="*", After:=Range("A1"), SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
LastCol = Cells.Find(What:="*", After:=Range("A1"), SearchOrder:=xlByColumns, SearchDirection:=xlPrevious).Column
For c = 1 To LastCol
Set Rangetemp = Cells(c).EntireColumn
Rangetemp.TextToColumns DataType:=xlDelimited, _
TextQualifier:=xlDoubleQuote, ConsecutiveDelimiter:=False, Tab:=True, _
Semicolon:=False, Comma:=False, Space:=False, Other:=False, FieldInfo _
:=Array(1, 1), TrailingMinusNumbers:=True
Next c
End Sub
''Convert text to Number with ZERO Digits and Number convert ZERO Digits
Sub ZERO_DIGIT()
On Error Resume Next
Dim rSelection As Range
Set rSelection = rSelection
rSelection.Select
With Selection
Selection.NumberFormat = "General"
.Value = .Value
End With
rSelection.Select
Selection.NumberFormat = "0"
Set rSelection = Nothing
End Sub
''Convert text to Number with TWO Digits and Number convert TWO Digits
Sub TWO_DIGIT()
On Error Resume Next
Dim rSelection As Range
Set rSelection = rSelection
rSelection.Select
With Selection
Selection.NumberFormat = "General"
.Value = .Value
End With
rSelection.Select
Selection.NumberFormat = "0.00"
Set rSelection = Nothing
End Sub
''Convert text to Number with SIX Digits and Number convert SIX Digits
Sub SIX_DIGIT()
On Error Resume Next
Dim rSelection As Range
Set rSelection = rSelection
rSelection.Select
With Selection
Selection.NumberFormat = "General"
.Value = .Value
End With
rSelection.Select
Selection.NumberFormat = "0.000000"
Set rSelection = Nothing
End Sub
The solution that for me works is:
For Each xCell In Selection
xCell.Value = CDec(xCell.Value)
Next xCell
Using aLearningLady's answer above, you can make your selection range dynamic by looking for the last row with data in it instead of just selecting the entire column.
The below code worked for me.
Dim lastrow as Integer
lastrow = Cells(Rows.Count, 2).End(xlUp).Row
Range("C2:C" & lastrow).Select
With Selection
.NumberFormat = "General"
.Value = .Value
End With
The solution that worked for me many times is:
Sub ConvertTextToNumber()
With Range("A1:CX500") 'you can change the range
.NumberFormat = "General"
.Value = .Value
End With
End Sub
For large datasets a faster solution is required.
Making use of 'Text to Columns' functionality provides a fast solution.
Example based on column F, starting range at 25 to LastRow
Sub ConvTxt2Nr()
Dim SelectR As Range
Dim sht As Worksheet
Dim LastRow As Long
Set sht = ThisWorkbook.Sheets("DumpDB")
LastRow = sht.Cells(sht.Rows.Count, "F").End(xlUp).Row
Set SelectR = ThisWorkbook.Sheets("DumpDB").Range("F25:F" & LastRow)
SelectR.TextToColumns Destination:=Range("F25"), DataType:=xlDelimited, _
TextQualifier:=xlDoubleQuote, ConsecutiveDelimiter:=False, Tab:=True, _
Semicolon:=False, Comma:=False, Space:=False, Other:=False, FieldInfo _
:=Array(1, 1), TrailingMinusNumbers:=True
End Sub
From the recorded macro one gets the code below; for a new application you just need to update selection and range:
Sub num()
Columns("B:B").Select
Selection.TextToColumns Destination:=Range("B1"), DataType:=xlDelimited, _
TextQualifier:=xlDoubleQuote, ConsecutiveDelimiter:=False, Tab:=True, _
Semicolon:=False, Comma:=False, Space:=False, Other:=False, FieldInfo _
:=Array(1, 1), TrailingMinusNumbers:=True
End Sub
I had problems making above codes work. To me multiplying with 1 did the trick:-)
Cells(1, 1).Select
Cells(1, 1) = ActiveCell * 1
basically i am using the TextToColumns function to separate out data separated by a semi colon in the same cell. problem is there are 2 columns of data that needs this function done and i have to insert the data without overwriting.
(go easy, first time with VBA and excel) this is what i currently have:
Sub Button1_Click()
Dim rng As Range
Dim sh As Worksheet
Set sh = Worksheets("Sheet1")
With sh
Set rng = .[Q1]
Set rng = .Range(rng, .Cells(.Rows.Count, rng.Column).End(xlUp))
num = Application.WorksheetFunction.Max(Columns("P"))
rng.Offset(0, 1).Resize(1, num).EntireColumn.Insert
rng.TextToColumns Destination:=rng, DataType:=xlDelimited, _
TextQualifier:=xlDoubleQuote, ConsecutiveDelimiter:=False, Tab:=False, _
Semicolon:=True, Comma:=False, Space:=False, Other:=False, _
FieldInfo:=Array(Array(1, 1), Array(2, 1), Array(3, 1)), TrailingMinusNumbers:=True
End With
Set sh2 = Worksheets("Sheet1")
With sh2
num2 = Application.WorksheetFunction.Max(Columns("P"))
Dim lastColumn As Integer
lastColumn = ActiveSheet.UsedRange.Column - 1 + ActiveSheet.UsedRange.Columns.Count
MsgBox Replace(Cells(1, lastColumn).Address(False, False), "1", "")
MsgBox lastColumn
Set rng = .[W1]
Set rng = .Range(rng, .Cells(.Rows.Count, rng.Column).End(xlUp))
rng.TextToColumns Destination:=rng, DataType:=xlDelimited, _
TextQualifier:=xlDoubleQuote, ConsecutiveDelimiter:=False, Tab:=False, _
Semicolon:=True, Comma:=False, Space:=False, Other:=False, _
FieldInfo:=Array(Array(1, 1), Array(2, 1), Array(3, 1)), TrailingMinusNumbers:=True
End With
End Sub
ok so what i am trying to fix is setting the rng manually in the second part to W1. the rng needs to be the next empty column. so lastColumn using the MsgBox DOES return the column "W" but i cannot set that to rng (type mismatch). oh and the num variable is set to the amount of columns i need to insert. my data looks like this:
count | column with ; data need separating | column with ; data need separating
5 | 5 sets of data separated by semi colons | don't care because this is the end of sheet
is there any easier way to do this? am i even close?
... Easiest solution if I understand your question correctly... Why don't you just make a THIRD column that equals Column1 & ";" & Column2, then just do text-to-columns on that single column??
Or am I missing something??
Assuming I'm understanding your setup correctly, the following code should work.
If you process the data columns from right to left, you don't have to worry about the change in address references when the columns are inserted. That means you can use a single block of insertion/conversion code, iterated over the two data address references, which I've assumed are Q1 and R1.
Note also that I added an offset to the TextToColumns destination to avoid overwriting the original data.
Option Explicit
Sub myTextToColumns()
Dim sh As Worksheet
Dim rng As Range
Dim num As Long
Dim arr As Variant
Dim i As Long
Set sh = Worksheets("Sheet1")
arr = Array("R1", "Q1")
num = Application.WorksheetFunction.Max(Columns("P"))
With sh
For i = LBound(arr) To UBound(arr)
Set rng = .Range(arr(i))
Set rng = .Range(rng, .Cells(.Rows.Count, rng.Column).End(xlUp))
rng.Offset(0, 1).Resize(1, num).EntireColumn.Insert
rng.TextToColumns Destination:=rng.Offset(0, 1), DataType:=xlDelimited, _
TextQualifier:=xlDoubleQuote, ConsecutiveDelimiter:=False, Tab:=False, _
Semicolon:=True, Comma:=False, Space:=False, Other:=False, _
FieldInfo:=Array(Array(1, 1), Array(2, 1), Array(3, 1)), TrailingMinusNumbers:=True
Next
End With
End Sub