Страницы: 1
RSS
VBA. XLSX to CSV, Нужна помощь в доработке скрипта
 
Задача экспортировать определённый лист из книги (`10000 строк) в определенное место с определенным именем файла в формат CSV в кодировке UTF-8 и разделителем "|" разбив результат на 3000 строк в каждом файле.

Скрипт для экспорта вроде работает, но нужно доработать, что бы кодировка UTF-8 и разделитель не запятая а вертикальная черта | и добавить разрезание на 3000 строк в каждом файле
Код
Option Explicit
Public Sub ExportWorksheetAndSaveAsCSV()
Dim wbkExport As Workbook
Dim shtToExport As Worksheet
Set shtToExport = ThisWorkbook.Worksheets("Итоговые цены")     'Sheet to export as CSV
Set wbkExport = Application.Workbooks.Add
shtToExport.Copy Before:=wbkExport.Worksheets(wbkExport.Worksheets.Count)
Application.DisplayAlerts = False                       'Possibly overwrite without asking
wbkExport.SaveAs Filename:="d:\Clouds\Yandex.Disk\Магазин\pricelists\import\Prices_for_marketplaces_new.csv", FileFormat:=xlCSV
Application.DisplayAlerts = True
wbkExport.Close SaveChanges:=False
End Sub

Помогите пожалуйста докрутить скрипт. Спасибо!
 
Доброго времени, попробуйте так:
Код
Sub ExportToCSVUTF8()
    Dim ws As Worksheet, nws As Worksheet
    Dim wb          As Workbook
    Dim delim$, Path$, txt$
    Dim i&, j&, s&
    Dim arr(), arr2()

    Set ws = ActiveSheet
    arr = ws.UsedRange.Value
    delim = "|": s = 3000

    For i = LBound(arr, 1) To UBound(arr, 1)
        ReDim Preserve arr2(i - 1)
        For j = LBound(arr, 2) To UBound(arr, 2)
            If txt = "" Then
                txt = arr(i, j)
            Else
                txt = txt & delim & arr(i, j)
            End If
        Next j
        arr2(i - 1) = txt
        txt = ""
    Next i
    
    For i = LBound(arr2) To UBound(arr2) Step s
        Set wb = Workbooks.Add
        For j = i To i + (s - 1)
            If j > UBound(arr2) Then Exit For
            wb.Worksheets(1).Cells(j - i + 1, 1).Value = arr2(j)
        Next j
        wb.SaveAs "C:\Файлы\" & "File " & i & " до " & i + s & ".csv", xlCSVUTF8
        wb.Close False
    Next i
    MsgBox "Готово", vbInformation, "PlanetaExcel"
End Sub
Изменено: Behruz A.N. - 24.09.2023 18:45:09
Вредить легко, помогать трудно.
 
Behruz A.N., Большое вам спасибо! Но пока, что то не до конца все получается, выдаёт ошибку. И есть пара уточнений. Нужно указать именно нужный лист, а не активный лист, и если честно, то итоговые файлы не нужно открывать, нужно, что бы они просто создались по указанному адресу. А пока, вот, что получается:

 
Замените два вопросительных знака из строки где указан путь к сохраняемой книги, место
Код
Set ws = ActiveSheet
пишите
Код
Set ws = ActiveWorkbook.Worksheets(имялиста)
.
Изменено: Behruz A.N. - 24.09.2023 19:17:41
Вредить легко, помогать трудно.
 
Behruz A.N., Супер! Всё работает отлично и как надо! Низкий вам поклон и большое спасибо!
 
Будьте здоровы, низкий поклон Николаю Павлову за этот сайт.
Вредить легко, помогать трудно.
 
Подскажите пожалуйста, что могло навредить данному скрипту, после того, как я добавил на экспортируемый лист еще один столбец, который, как и все остальные на данном листе состоят из формул, которые забирают данные из соседних листов книги и если до добавления этого столбца скрипт отрабатывает около 30000 строк за 5 секунд, то теперь (с этим новым столбцом) на каждую строку уходит 1-2 секунды... Стоит удалить этот новый столбец - всё снова летает. В этом столбце вот такая формула (помогли в соседней теме) и он спокойно прогружает все эти данные и никаких затыков.
Код
=ЕСЛИОШИБКА(
ВПР([@ID];static_prices;10;0);
ЕСЛИ(И(ABS(ДЕНЬНЕД(ТДАТА();2)+ОСТАТ(ОКРУГЛ(ТДАТА();4);1)-6,625)<=1;[@Поставщик]<>"Pavilion");0;[@Остаток]))
Повторюсь, все данные во всех ячейках уже есть, все ок, а при работе скрипта, такое ощущение, что снова идет работа по всем этим ячейкам и формулам, как бы просто экспортировать этот лист, с теми данными, что есть на этом листе, с условиями в этом скрипте (разделитель "|", UTF8, разрезать на 3000 строк). Ниже привожу рабочий код скрипта:
Код
Sub ExportToCSVUTF8()
    Dim ws As Worksheet, nws As Worksheet
    Dim wb          As Workbook
    Dim delim$, Path$, txt$
    Dim i&, j&, s&
    Dim arr(), arr2()
 
    Set ws = ActiveWorkbook.Worksheets("Итоговые цены")
    arr = ws.UsedRange.Value
    delim = "|": s = 3000
 
    For i = LBound(arr, 1) To UBound(arr, 1)
        ReDim Preserve arr2(i - 1)
        For j = LBound(arr, 2) To UBound(arr, 2)
            If txt = "" Then
                txt = arr(i, j)
            Else
                txt = txt & delim & arr(i, j)
            End If
        Next j
        arr2(i - 1) = txt
        txt = ""
    Next i
     
    For i = LBound(arr2) To UBound(arr2) Step s
        Set wb = Workbooks.Add
        For j = i To i + (s - 1)
            If j > UBound(arr2) Then Exit For
            wb.Worksheets(1).Cells(j - i + 1, 1).Value = arr2(j)
        Next j
        Application.DisplayAlerts = False
        wb.SaveAs "d:\Clouds\Yandex.Disk\Магазин\pricelists\import\" & "Prices_marketplaces_from_" & i & "_to_" & i + s & ".csv", xlCSVUTF8
        wb.Close False
    Next i
    'MsgBox "Готово", vbInformation, "PlanetaExcel"
End Sub
 
Цитата
написал:
такое ощущение, что снова идет работа по всем этим ячейкам и формулам
попробуйте отключить пересчет формул
Код
' Отключение
    Application.Calculation = xlCalculationManual

    For i = LBound(arr, 1) To UBound(arr, 1)
        ReDim Preserve arr2(i - 1)
        For j = LBound(arr, 2) To UBound(arr, 2)
            If txt = "" Then
                txt = arr(i, j)
            Else
                txt = txt & delim & arr(i, j)
            End If
        Next j
        arr2(i - 1) = txt
        txt = ""
    Next i

    ' Включение
    Application.Calculation = xlCalculationAutomatic
Изменено: yalewa - 25.11.2023 21:54:10
 
Цитата
написал:
попробуйте отключить пересчет формул
Волшебство! За 2 секунды стало всё без проблем работать! Огромное вам спасибо!
 
Всё таки, оказалось, есть еще одна проблема в данном скрипте.
В итоговый файл CSV в некоторые строки (в ячейках которых, в исходном XLSX имеются кавычки) добавляются дополнительные кавычки и получается так, что у каких то строк есть кавычки в начале строки и в конце, а в каких то нет и такой CSV при импорте через PQ просто рассыпается. Как можно исправить указанный выше скрипт, что бы при импорте в CSV не добавлялись эти кавычки?


 
попробуйте так

Код
Sub ExportToCSV_UTF8()
    'CSV в кодировке UTF8+BOM
    Dim wb As Workbook
    Dim ws As Worksheet
    Dim sSymbol As String, txt As String
    Dim i As Long, j As Long, RowsLimit As Long
    Dim arr(), arr2()
 
    If MsgBox("Сохранить лист в CSV?", vbQuestion + vbYesNo, "Вопросы") = vbNo Then Exit Sub
    
    sSymbol = "|" 'символ-разделитель
    RowsLimit = 3000 'кол-строк строк в 1 файле
           
    Application.ScreenUpdating = False
    Application.Calculation = xlCalculationManual
    Application.DisplayAlerts = False
    
    Set ws = ActiveWorkbook.Worksheets("Итоговые цены")
    arr = ws.UsedRange.Value
 
    For i = LBound(arr, 1) To UBound(arr, 1)
        ReDim Preserve arr2(i - 1)
        For j = LBound(arr, 2) To UBound(arr, 2)
            If txt = "" Then
                txt = arr(i, j)
            Else
                txt = txt & sSymbol & arr(i, j)
            End If
        Next j
        arr2(i - 1) = Replace$(txt, Chr(34), "")
        txt = ""
    Next i
     
    For i = LBound(arr2) To UBound(arr2) Step RowsLimit
        Set wb = Workbooks.Add
        For j = i To i + (RowsLimit - 1)
            If j > UBound(arr2) Then Exit For
            wb.Worksheets(1).Cells(j - i + 1, 1).Value = arr2(j)
        Next j
        wb.SaveAs "d:\Clouds\Yandex.Disk\Магазин\pricelists\import\" & "Prices_marketplaces_from_" & i & "_to_" & i + RowsLimit & ".csv", xlCSVUTF8
        wb.Close False
    Next i
    
    Application.DisplayAlerts = True
    Application.Calculation = xlCalculationAutomatic
    Application.ScreenUpdating = True
    
    MsgBox "Готово", vbInformation, "Конец"
End Sub
 
New, Да, скрипт работает, но... Теперь, ячейки, содержащие в данных кавычки (и они, конечно мне нужны), после импорта в CSV исчезли. А те самые открывающие и закрывающие кавычки, которые раньше добавлялись если в данных были кавычки больше не появляются, но зато появляются в тех строках, в которых есть "восклицательный знак", "вопросительный знак" :) То есть, снова, не то. Мне бы получить в экспортном CSV "как есть"...


 
Del
Изменено: New - 26.11.2023 20:19:36
 
попробуйте так:
Код
Sub ExportToCSV_UTF8()
    'CSV в кодировке UTF8+BOM
    Dim wb As Workbook
    Dim ws As Worksheet
    Dim sSymbol As String, txt As String
    Dim i As Long, j As Long, RowsLimit As Long
    Dim arr(), arr2()
    Dim s As String
    Dim oStream
 
    If MsgBox("Сохранить лист в CSV?", vbQuestion + vbYesNo, "Вопросы") = vbNo Then Exit Sub
    
    sSymbol = "|" 'символ-разделитель
    RowsLimit = 3000 'кол-строк строк в 1 файле
           
    Application.ScreenUpdating = False
    Application.Calculation = xlCalculationManual
    Application.DisplayAlerts = False
    
    Set ws = ActiveWorkbook.Worksheets("Итоговые цены")
    arr = ws.UsedRange.Value
 
    For i = LBound(arr, 1) To UBound(arr, 1)
        ReDim Preserve arr2(i - 1)
        For j = LBound(arr, 2) To UBound(arr, 2)
            If txt = "" Then
                txt = arr(i, j)
            Else
                txt = txt & sSymbol & arr(i, j)
            End If
        Next j
        arr2(i - 1) = txt
        txt = ""
    Next i
     
    For i = LBound(arr2) To UBound(arr2) Step RowsLimit
        s = ""
        For j = i To i + (RowsLimit - 1)
            If j > UBound(arr2) Then Exit For
            s = s & arr2(j) & vbNewLine
        Next j
        If s <> "" Then
            Set oStream = CreateObject("ADODB.Stream")
            With oStream
                .Open
                .Charset = "utf-8"
                .WriteText s
                .SaveToFile "d:\Clouds\Yandex.Disk\Магазин\pricelists\import\Prices_marketplaces_from_" & i & "_to_" & i + RowsLimit & ".csv", 2
            End With
            Set oStream = Nothing
        End If
    Next i
    
    Application.DisplayAlerts = True
    Application.Calculation = xlCalculationAutomatic
    Application.ScreenUpdating = True
    
    MsgBox "Готово", vbInformation, "Конец"
End Sub
 
Цитата
написал:
попробуйте так:
Безупречно! Работает как часы. Не перестаю вас благодарить! И всех откликнувшихся. Спасибо.
 

Newsky13 Здравствуйте.

Можно еще такой вариант. Возможно будет пошустрей. Основной тормоз функция перекодировки, если применить более скоростную, должно по скорости получится хорошо. Только в переменную Put1 свой путь пропишите.

Код
Sub enstaralfgh()
Dim Arr1, nRow&, nSt&, kRow&, RowP&, Put1$, Str1$, Rz$, i&, j&, DF2 As Byte
    Put1 = ThisWorkbook.Path & "\"
    kRow = 3000: Rz = "|"
    Arr1 = ActiveSheet.UsedRange.Value
    nRow = UBound(Arr1, 1)
    nSt = UBound(Arr1, 2)
    RowP = kRow
DF2 = FreeFile: Open Put1 & "1" & "_to_" & RowP & ".csv" For Binary As #DF2
    For i = 1 To nRow
        For j = 1 To nSt
'Str1 = CStr(Arr1(i, j))
Str1 = EncodeUTF8noBOM(CStr(Arr1(i, j)))
If j <> nSt Then Put #DF2, , Str1 & Rz Else Put #DF2, , Str1 & vbNewLine
        Next j
            If i = RowP Then
            Close #DF2
If RowP + kRow < nRow Then RowP = RowP + kRow Else kRow = nRow - RowP: RowP = nRow
If i <> nRow Then DF2 = FreeFile: Open Put1 & RowP - kRow & "_to_" & RowP & ".csv" For Binary As #DF2
            End If
    Next i
End Sub
Function EncodeUTF8noBOM(ByVal Txt As String) As String
Dim Sim$, N&
    For i = 1 To Len(Txt)
    Sim = Mid(Txt, i, 1): N = AscW(Sim): If N < 0 Then N = N + 65536
Select Case N
Case Is < 128
Case Is < 2048: Sim = Chr(192 + N \ 64) & Chr(128 + N Mod 64)
Case Is < 65536: Sim = Chr(224 + N \ 4096) & Chr(128 + (N Mod 4096) \ 64) & Chr(128 + N Mod 64)
End Select
    EncodeUTF8noBOM = EncodeUTF8noBOM & Sim
    Next
End Function
Изменено: Евгений Смирнов - 27.11.2023 15:57:53
 
Евгений Смирнов, Здравствуйте. Ваш вариант действительно отработал и пошустрей и даже не подвесил систему и результат в CSV как надо! Большое вам спасибо! Если позволите, один вопрос: Как заменить Arr1 что бы прописать в скрипт нужный лист, а не активный?
UPD:
Упс... заметил очень не нужное поведение скрипта, при повторном (и при всех последующих) вызове скрипта, он данные ДОписывает в файл, а не перезаписывает его целиком. Вот такое поведение совсем не соответствует задаче :)

И еще заметил такую странность: экспорт - ок, файл создался. Объёмом 0 байт. Открываю - всё есть. Закрываю - в проводнике вижу ненулевой размер. Снова экспорт - размер файла не изменился. Открываю - новые данные добавились. Закрываю Вижу в проводнике - вес увеличился
Изменено: Newsky13 - 27.11.2023 22:28:33
 

Newsky13 Можно было использовать режим OutPut, там было бы все ок. Я решил поэкспериментировать с режимом Binary, в этом режиме новый файл создается, если его нет. Поэтому если файлов в папке нет, все нормально. Я думал, что при новом открытии файла указатель устанавливается в позицию 1, а он видимо остается на последней записи. Хотя в принципе это логично, надо в своей справке это пометить. Что касается второго вопроса ответа не знаю. Возможно, где то надо запихнуть функцию DoEvents, чтобы передать управление операционной системе. Сейчас времени нет. Сегодня попозже постараюсь поправить.

Код
Arr1 = ThisWorkbook.Worksheets("ИмяЛиста").UsedRange.Value
 

Newsky13 Насчет 0 размера файла. В принципе это никак не влияет на корректность работы макроса. Я у себя сейчас не вижу такого, хотя иногда замечал такую ситуацию при работе с файлами в режиме прямого доступа. Возможно это можно так объяснить. При работе в этом режиме операционка знает только о создании файла, но не знает, что мы в него записали данные, поэтому сначала показывает 0 размер, но после первого обращения к файлу все становиться ок. Процедуру подправил проверяйте.

Код
Sub enstaralfgh()
Dim Arr1, nRow&, nSt&, kRow&, RowP&, Put1$, ImaF$, Str1$, Rz$, i&, j&, DF2 As Byte
    Put1 = ThisWorkbook.Path & "\"
    kRow = 3000: Rz = "|"
    Arr1 = ActiveSheet.UsedRange.Value
    nRow = UBound(Arr1, 1)
    nSt = UBound(Arr1, 2)
    RowP = kRow
ImaF = Put1 & "1" & "_to_" & RowP & ".csv"
If Dir(ImaF, 7) <> vbNullString Then Kill ImaF
DF2 = FreeFile: Open ImaF For Binary As #DF2
    For i = 1 To nRow
        For j = 1 To nSt
'Str1 = CStr(Arr1(i, j))
Str1 = EncodeUTF8noBOM(CStr(Arr1(i, j)))
If j <> nSt Then Put #DF2, , Str1 & Rz Else Put #DF2, , Str1 & vbNewLine
        Next j
            If i = RowP Then
            Close #DF2
If RowP + kRow < nRow Then RowP = RowP + kRow Else kRow = nRow - RowP: RowP = nRow
ImaF = Put1 & RowP - kRow & "_to_" & RowP & ".csv"
If Dir(ImaF, 7) <> vbNullString Then Kill ImaF
If i <> nRow Then DF2 = FreeFile: Open ImaF For Binary As #DF2
            End If
    Next i
End Sub
 
Евгений Смирнов, в таком варианте, можно сказать почти идеально, но... Если теперь, в файл не добавляются новые строки, а файл именно, что перезаписывается, но только после открытия-закрытия итогового файла. А если, скрипт запустить первый раз (снова вижу размер файла 0 байт) и не трогая его (не запуская) снова выполнить скрипт, то получаю ошибку
Код
Run-time error '55' File already open
и Debug отсылает к
Код
If Dir(ImaF, 7) <> vbNullString Then Kill ImaF
(Kill ImaF залит желтым).  Что то все таки немного не то в этом моменте, а так, просто супер! Очень быстро всё и без нагрузки!
Изменено: Newsky13 - 28.11.2023 21:28:08
 

Newsky13 Ну теперь вроде все складывается. Вы хорошо описываете проблему, с вами легко общаться. Все мои предыдущие предположения неверны. Значит проблема в том, что какой то файл остается открытый, видимо тогда и размер его в проводнике равен 0, из за этого оператор Kill не может его удалить, хотя функция Dir его находит. У меня 2 компа программное обеспечение одинаковое. Винда XP и 7, Офис 2002 и 2010. Сегодня проверил во всех вариациях ни одной ошибки и ни одного файла с 0 размером. А вчера вспомнил, что в конце весны менял диск на основном компе, и видимо файлы с 0 размером я встречал при работе с тем диском. Предполагаю, что видимо старый диск был не очень (дешевый китайский SSD), поэтому он и завернул ласты за 3 года, хотя нагрузка на него была практически никакая. Так, что у вас вероятнее всего проблемы с ЖД, или виндой. Попробуйте проверить на других компах, или хотя бы на заведомо исправных съемном диске или флешке.

Ну в принципе можно ещё перед End Sub попробовать  добавить строку

Код
Close

или

Код
Reset

Изменено: Евгений Смирнов - 29.11.2023 14:08:07
 

Newsky13 Если эксперименты не надоели можно так ещё попробовать.

Код
Sub enstaralfgh1()
Dim Arr1, nRow&, nSt&, kRow&, RowP&, Put1$, ImaF$, Str1$, Rz$, i&, j&
    Put1 = ThisWorkbook.Path & "\"
    kRow = 3000: Rz = "|"
    Arr1 = ActiveSheet.UsedRange.Value
    nRow = UBound(Arr1, 1)
    nSt = UBound(Arr1, 2)
    RowP = kRow
ImaF = Put1 & "1" & "_to_" & RowP & ".csv"
With CreateObject("ADODB.Stream")
    .Open: .Charset = "UTF-8"
    For i = 1 To nRow
        For j = 1 To nSt
Str1 = CStr(Arr1(i, j))
If j <> nSt Then .WriteText Str1 & Rz Else .WriteText Str1 & vbNewLine
        Next j
        If i = RowP Then
        .SaveToFile ImaF, 2
        .Close
If RowP + kRow < nRow Then RowP = RowP + kRow Else kRow = nRow - RowP: RowP = nRow
ImaF = Put1 & RowP - kRow & "_to_" & RowP & ".csv"
If i <> nRow Then .Open
        End If
    Next i
End With
End Sub
 
Цитата
написал:
Ну в принципе можно ещё перед End Sub можно добавить строку Close
Вот! Вот, чего не хватало, для полного счастья! Теперь - безупречно! И файл сразу с "размером" и повторный запуск скрипта происходит без ошибок и очень быстро и разделитель нужный и UTF-8 на месте и лишних кавычек нет! Просто замечательно! Большое вам спасибо за помощь и за уделённое время!
 

Newsky13 Хорошо, что все получилось, но я все таки думаю, что у вас есть какие-то проблемы с ЖД. И с вашим диском будет ограничение по кол-ву создаваемых файлов, не более 255. Ну это так к сведению.

 
Евгений Смирнов, если строк будет меньше, чем kRow = 3000 (например, 100), то файл не сохранится и не появится в папке
P.S. Так же в каждый файл в конце добавляется пустая строка (vbNewLine), некоторым системам она будет мешать (строка пустая есть, а данных в ней нет)

P.P.S. С твоего разрешения
Код
Sub ExportToCSV_UTF8()
    Dim arrData As Variant
    Dim sPath As String, sSymbol As String, sFileName As String, sStr As String
    Dim QtyOfRows As Long, QtyOfCols As Long, LimitRows As Long, RowsCounter As Long, i As Long, j As Long
    
    sPath = ThisWorkbook.Path & "\"
    LimitRows = 3000 'ActiveSheet.Rows.Count
    sSymbol = "|"
    
    arrData = ActiveSheet.UsedRange.Value
    QtyOfRows = UBound(arrData, 1)
    QtyOfCols = UBound(arrData, 2)
    RowsCounter = LimitRows
    
    If QtyOfRows >= LimitRows Then
        sFileName = "1" & "_to_" & RowsCounter & ".csv"
    Else
        sFileName = "1" & "_to_" & QtyOfRows & ".csv"
    End If
    
    With CreateObject("ADODB.Stream")
        .Open
        .Charset = "UTF-8"
        For i = 1 To QtyOfRows
            For j = 1 To QtyOfCols
                sStr = CStr(arrData(i, j))
                If j <> QtyOfCols Then
                    .WriteText sStr & sSymbol
                Else
                    If i Mod LimitRows = 0 Or i = QtyOfRows Then
                        .WriteText sStr 'последняя строка в файле: не добавляем vbNewLine
                    Else
                        .WriteText sStr & vbNewLine
                    End If
                End If
            Next j
            If i = RowsCounter Or i = QtyOfRows Then 'сохранить файл, если достигли конца массива
                .SaveToFile sFileName, 2
                .Close
                If RowsCounter + LimitRows < QtyOfRows Then
                    RowsCounter = RowsCounter + LimitRows
                Else
                    LimitRows = QtyOfRows - RowsCounter
                    RowsCounter = QtyOfRows
                End If
                sFileName = RowsCounter - LimitRows + 1 & "_to_" & RowsCounter & ".csv"
                If i <> QtyOfRows Then .Open
            End If
        Next i
    End With
    
    MsgBox "Файлы сохранены в папку: " & sPath, vbInformation, "CSV"
End Sub
Изменено: New - 02.12.2023 23:32:00
 
New Здравствуйте. Про пустую строку знал. По первому вопросу, что то не подумал.
Павел ну зачем вы лишили удовольствия форумчан самим поковыряться в коде и  исправить эти недочеты. Они будут теперь обижаться на вас :D  
Изменено: Евгений Смирнов - 03.12.2023 11:31:57
 
Евгений Смирнов, Евгений, давай на "ты" )  Я воспринимаю всех постоянных участников нашего форума как друзей
Изменено: New - 03.12.2023 12:20:20
 
Павел, я не против. без проблем.
Изменено: Евгений Смирнов - 03.12.2023 13:05:53
Страницы: 1
Наверх