4

I have 20 csv files. Each are unrelated. How do I combine them together into one xlsx file with 20 sheets, each named after the csv files.

$root = "C:\Users\abc\Desktop\testcsv"
$CSVfiles = Get-ChildItem -Path $root -Filter *.csv

$xlsx = "C:\Users\abc\Desktop\testxl.xlsx" #output location
$delimiter = "," #delimiter

#Create a excel
$xl=New-Object -ComObject Excel.Application
$xl.Visible=$true
#add a workbook
$wb=$xl.WorkBooks.add(1)

ForEach ($csv in $CSVfiles){

    #name  the worksheet
    $ws=$wb.WorkSheets.item(1)
    $ws.Name = [io.path]::GetFileNameWithoutExtension($csv)

    $TxtConnector = ("TEXT;" + $csv)
    $Connector = $ws.QueryTables.add($TxtConnector,$ws.Range("A1"))
    $query = $ws.QueryTables.item($Connector.name)
    $query.TextFileOtherDelimiter = $delimiter
    $query.TextFileParseType  = 1
    $query.TextFileColumnDataTypes = ,1 * $ws.Cells.Columns.Count
    $query.AdjustColumnWidth = 1

    # Execute & delete the import query
    $query.Refresh()
    $query.Delete()
    $wb.SaveAs($xlsx,51)
}
# Save & close the Workbook as XLSX.
$xl.Quit()
Deepak Tiwari
  • 155
  • 1
  • 2
  • 8
  • Can you provide what you've tried? – Thom Schumacher Mar 16 '18 at 15:42
  • 1
    stackoverflow is not a script writing service. What have you written so far? – EBGreen Mar 16 '18 at 15:42
  • 3
    MS does not provide good APIs for creating office documents via software other than actually running Office (the official interop APIs actually run full Office apps in the background). What you might try is [SpreadsheetML](https://msdn.microsoft.com/en-us/library/office/gg278316.aspx), which is a fairly simple xml schema Excel understands and allows for multiple sheets in a single document. – Joel Coehoorn Mar 16 '18 at 15:44
  • You want to use ImportExcel module, it does not use com methods and does not require office installation. https://github.com/dfinke/ImportExcel here's the link – Tomek Mar 16 '18 at 16:33

3 Answers3

4

This way, change the first line to the folder where you store those 20 CSV files and then

$path="c:\path\to\folder" #target folder
cd $path;

$csvs = Get-ChildItem .\* -Include *.csv
$y=$csvs.Count
Write-Host "Detected the following CSV files: ($y)"
foreach ($csv in $csvs)
{
Write-Host " "$csv.Name
}
$outputfilename = $(get-date -f yyyyMMdd) + "_" + $env:USERNAME + "_combined-data.xlsx" #creates file name with date/username
Write-Host Creating: $outputfilename
$excelapp = new-object -comobject Excel.Application
$excelapp.sheetsInNewWorkbook = $csvs.Count
$xlsx = $excelapp.Workbooks.Add()
$sheet=1

foreach ($csv in $csvs)
{
$row=1
$column=1
$worksheet = $xlsx.Worksheets.Item($sheet)
$worksheet.Name = $csv.Name
$file = (Get-Content $csv)
foreach($line in $file)
{
$linecontents=$line -split ',(?!\s*\w+")'
foreach($cell in $linecontents)
{
$worksheet.Cells.Item($row,$column) = $cell
$column++
}
$column=1
$row++
}
$sheet++
}
$output = $path + "\" + $outputfilename
$xlsx.SaveAs($output)
$excelapp.quit()
cd \ #returns to drive root
Francesco Mantovani
  • 10,216
  • 13
  • 73
  • 113
  • Thank you Francesco, this solution works for me. I have a trouble with the regex rules to split xml. E.g. for now I see two cases when it goes wrong, coma in next text is treated as a separator: char,..." "IDE builds every time, even I'm not the expert with regex, however Excel opens it perfectly (I mean xml), while the script adds some extra cells. Does anybody have a solution in the pocket? – m.zhelieznov Jan 26 '21 at 15:09
  • @m.zhelieznov, post your question in detail, but not here, in a new post. Paste your or my code and explain where you are blocked. Tag the right technology and someone will help you – Francesco Mantovani Jan 26 '21 at 16:36
  • I got the solution from here and updated your script a bit https://stackoverflow.com/a/40672956/3516022 :
    `#Good regex, but leaves some comas as cells
    $linecontents=$line -split '(\s*"[^"]+"\s*|\s*[^,]+|,)(?=,|$)' foreach($cell in $linecontents) #Don't add cells with coma if ($cell -ne ",") { $worksheet.Cells.Item($row,$column) = $cell $column++`
    – m.zhelieznov Jan 28 '21 at 13:09
3

https://stackoverflow.com/a/51094040/5995160 answer is too slow when dealing with csv's with a ton of data, I modified this solution to use https://github.com/dfinke/ImportExcel. This has greatly improved the performance of this task, at least for me.

Install-Module ImportExcel -scope CurrentUser
$csvs = Get-ChildItem .\* -Include *.csv
$csvCount = $csvs.Count
Write-Host "Detected the following CSV files: ($csvCount)"
foreach ($csv in $csvs) {
    Write-Host " -"$csv.Name
}

$excelFileName = $(get-date -f yyyyMMdd) + "_" + $env:USERNAME + "_combined-data.xlsx"
Write-Host "Creating: $excelFileName"

foreach ($csv in $csvs) {
    $csvPath = ".\" + $csv.Name
    $worksheetName = $csv.Name.Replace(".csv","")
    Write-Host " - Adding $worksheetName to $excelFileName"
    Import-Csv -Path $csvPath | Export-Excel -Path $excelFileName -WorkSheetname $worksheetName
}

This solution assumes that the user has already changed directories to where all the csv's live.

0

See below for a solution with uses the OpenText method.

At least two things to note:

  • I'm assuming your workbook creates a single sheet by default. if creates more than that, you will need to modify the script so that these additional sheets are deleted from the end result.

  • The way you specify TextFileColumnDataTypes is quite clever. You will need to modify it and feed the array to the FieldInfo argument below. See the documentation linked above for the kind of array it is expecting.


$CSVfiles = Get-ChildItem -Path $root -Filter *.csv

$xlsx = "C:\Users\abc\Desktop\testxl.xlsx" #output location

#Create a excel
$xl = New-Object -ComObject Excel.Application

$xl.Visible=$true

#add a workbook
$wb = $xl.WorkBooks.add(1)

# how many worksheets do you have in your original workbook? Assuming one:
$ws = $wb.Worksheets.Item(1)

ForEach ($csv in $CSVfiles){

    # OpenText method does not work well with csv files
    Copy-Item -Path $csv.FullName -Destination ($csv.FullName).Replace(".csv",".txt") -Force

    # Use OpenText method. FieldInfo will need to be amended to suit your needs
    $xl.WorkBooks.OpenText(`
                    ($file.FullName).Replace(".csv",".txt"),    # Filename
                    2,                 # Origin
                    1,                 # StartRow
                    1,                 # DataType
                    1,                 # TextQualifier
                    $false,            # ConsecutiveDelimiter
                    $false,            # Tab
                    $false,            # Semicolon
                    $true,             # Comma
                    $false,            # Space
                    $false,            # Other
                    $false,            # OtherChar
                    @()                # FieldInfo
    )

    $tempBook   = $xl.ActiveWorkbook

    $tempBook.worksheets.Item(1).Range("A1").Select()         | Out-Null
    $tempBook.worksheets.Item(1).Move($wb.Worksheets.Item(1)) | Out-Null

    # name  the worksheet
    $xl.ActiveSheet.Name = $csv.BaseName

    Remove-Item -Path ($csv.FullName).Replace(".csv",".txt")  -Force

}

$ws.Delete()

# Save & close the Workbook as XLSX.
$wb.SaveAs($xlsx,51)
$wb.Close()

$xl.Quit()
G42
  • 9,791
  • 2
  • 19
  • 34