Wh1Te Пользователь Сообщений: 4 |
Через разные формулы, получаю строку с данными текст и числа. Всего около 5-6 ячеек занято, мне нужно взять данные из всей строки и сохранить в текстовом файле, но так чтобы в текстовом файле данные разделялись точками, обычное разделение сохраняет через пробелы. И вторая строчка, там большое количество пробелов между данными. Первую строку можно сделать через «Сцепить», вторую аналогично только пробелов добавить ? Файл Excel и, образец файла Txt который нужен прикрепил к сообщению. Изменено: Wh1Te — 03.09.2015 15:13:37 |
JayBhagavan Пользователь Сообщений: 11833 ПОЛ: МУЖСКОЙ | Win10x64, MSO2019x64 |
Wh1Te, и Вам здравствовать. В файле экселя только одна строка. <#0> |
TSN Пользователь Сообщений: 217 |
#3 04.09.2015 09:10:02 Пример записи в файл тхт
Поэтому как получить все данные из файла думайте самостоятельно Прикрепленные файлы
Изменено: TSN — 04.09.2015 09:12:01 |
||||
Whitee Пользователь Сообщений: 4 |
Большое спасибо за написанный скрипт. Очень облегчает задачу вывода в txt. Чуть чуть донастрою уже сам) Изменено: Whitee — 08.09.2015 23:07:52 |
Юрий М Модератор Сообщений: 60580 Контакты см. в профиле |
Whitee, и вот зачем Вы процитировали всё целиком? Кнопка цитирования не для ответа. Исправьте свой #4. |
Whitee Пользователь Сообщений: 4 |
Столкнулся с одним недостатком. В текстовом файле все строки идут друг за другом одной строчкой. А нужно чтобы в txt было так же как и в Excele осуществлялся перенос на следующую строчку Текстовый файл — в каком виде нужно получить данные. (пробел, запятая, точка не важно. Потом сам изменю). |
ikki Пользователь Сообщений: 9709 |
#7 08.09.2015 23:46:45
Вы всегда стремитесь устранять свои ошибки за чужой счёт?
перенос на следующую строчку — это как раз «чуть-чуть» фрилансер Excel, VBA — контакты в профиле |
||||||||
kalbasiatka Пользователь Сообщений: 684 |
#8 09.09.2015 06:51:08
|
||
TSN Пользователь Сообщений: 217 |
#9 09.09.2015 08:45:57
Для разнообразия вариантов еще один.
Изменено: TSN — 09.09.2015 08:48:08 |
||||
Whitee Пользователь Сообщений: 4 |
#10 09.09.2015 14:14:28
Работает превосходно, спасибо |
||
jeka-irbis Пользователь Сообщений: 137 |
#11 17.09.2019 22:27:02 вопрос решен самостоятельно, всем спасибо за «помощь»)) Изменено: jeka-irbis — 19.09.2019 22:31:41 |
Retroman 1 / 1 / 1 Регистрация: 27.01.2015 Сообщений: 73 |
||||||
1 |
||||||
09.12.2018, 14:33. Показов 19178. Ответов 12 Метки excel, txt, txtfile, vba, данные, экспорт (Все метки)
Мне нужно из xmlx файла (Microsoft excel 2010) выполнить экспорт данных в ячейке в txt файл. Однако программа выдаёт ошибку «invalid outside procedure», ругаясь на строчку 2. Из-за этого я не могу автоматизировать процесс экспорта, чтобы 2000 ячеек руками не перетаскивать по схеме — 1 ячейка = 1 txt файл. Я не понимаю, что мне нужно сделать, чтобы исправить данную ошибку?
Вложения
0 |
Hugo121 6875 / 2807 / 533 Регистрация: 19.10.2012 Сообщений: 8,562 |
||||
09.12.2018, 15:30 |
2 |
|||
1. Селекты — это лишнее. Добавлено через 2 минуты
0 |
Казанский 15136 / 6410 / 1730 Регистрация: 24.09.2011 Сообщений: 9,999 |
||||
09.12.2018, 16:53 |
3 |
|||
Retroman, или так
Кстати, если файлу дать расширение htm, то он вполне открывается браузером, и даже с картинками
0 |
Retroman 1 / 1 / 1 Регистрация: 27.01.2015 Сообщений: 73 |
||||
09.12.2018, 18:30 [ТС] |
4 |
|||
Hugo121, тогда, если мне надо каждую ячейку в отдельный файл, нужно будет просто открытие файла воткнуть в своеобразный цикл, вроде такого? Или подобный цикл в Basic по-другому реализуется?
0 |
6875 / 2807 / 533 Регистрация: 19.10.2012 Сообщений: 8,562 |
|
09.12.2018, 18:45 |
5 |
по схеме — 1 ячейка = 1 txt файл — тут я такого вообще не вижу, поэтому пока вопрос ответа не имеет
0 |
1 / 1 / 1 Регистрация: 27.01.2015 Сообщений: 73 |
|
09.12.2018, 19:12 [ТС] |
6 |
Hugo121, А можно сделать в VB так, чтобы именем открываемого файла была итерация в цикле, то есть i=1 — файл называется 1.txt, i=2 — 2.txt и так далее?
0 |
Hugo121 6875 / 2807 / 533 Регистрация: 19.10.2012 Сообщений: 8,562 |
||||
09.12.2018, 19:16 |
7 |
|||
Можно.
Но советую сразу продумать что делать если такой файл уже есть.
0 |
Retroman 1 / 1 / 1 Регистрация: 27.01.2015 Сообщений: 73 |
||||
09.12.2018, 19:34 [ТС] |
8 |
|||
Тогда, если я правильно всё понимаю, макрос с распределением по одной ячейке на файл будет выглядеть так, или я снова напортачил?
0 |
Hugo121 6875 / 2807 / 533 Регистрация: 19.10.2012 Сообщений: 8,562 |
||||
09.12.2018, 19:44 |
9 |
|||
Сообщение было отмечено Retroman как решение РешениеНе, напортачили.
1 |
1 / 1 / 1 Регистрация: 27.01.2015 Сообщений: 73 |
|
09.12.2018, 19:52 [ТС] |
10 |
Hugo121, благодарю за помощь Теперь с этой задачей проблем нет
0 |
0 / 0 / 0 Регистрация: 06.11.2019 Сообщений: 2 |
|
06.11.2019, 12:17 |
11 |
А как модифицировать чтобы txt создавались сразу в UTF-8?
0 |
Meccup1 3 / 2 / 1 Регистрация: 22.10.2019 Сообщений: 21 |
||||
06.11.2019, 12:45 |
12 |
|||
А как модифицировать чтобы txt создавались сразу в UTF-8? Какие древние темы поднимаются из-за лени гуглить. Кликните здесь для просмотра всего текста
Ключ к решению в команде .Charset = «utf-8»
0 |
0 / 0 / 0 Регистрация: 06.11.2019 Сообщений: 2 |
|
06.11.2019, 13:16 |
13 |
Спасибо, но не пойму куда воткнуть .Charset = «utf-8″…
0 |
This VBA Program reads an Excel Range (Sales Data) and write to a Text file (Sales.txt)
Excel VBA code to read data from an Excel file (Sales Data – Range “A1:E26”). Need two “For loop” for rows and columns. Write each value with a comma in the text file till the end of columns (write without comma only the last column value). Do the above step until reach the end of rows.
Sales Data in Excel: 5 columns and 25 rows
Sales Data
VBA code to create a text file as below
VBA Code:
- Declaring Variables :
Variable | Data Type | Comments |
---|---|---|
myFileName | String | Output text file (Full path with file name) |
rng | Range | Excel range to read |
cellVal | Variant | Variable to assign each cell value |
row | Integer | Iterate rows |
col | Integer | Iterate columns |
'Variable declarations Dim myFileName As String, rng As Range, cellVal As Variant, row As Integer, col As Integer
- Initialize variables:
- myFileName: The file name with the full path of the output text file
- rng: Excel range to read data from an excel.
'Full path of the text file myFileName = "D:ExcelWriteTextsales.txt" 'Data range need to write on text file Set rng = ActiveSheet.Range("A1:E26")
Open the output text file and assign a variable “#1”
'Open text file Open myFileName For Output As #1
‘Nested loop to iterate both rows and columns of a given range eg: “A1:E26” [5 columns and 26 rows]
'Number of Rows For row = 1 To rng.Rows.Count 'Number of Columns For col = 1 To rng.Columns.Count
Assign the value to variable cellVal
cellVal = rng.Cells(row, col).Value
Write cellVal with comma. If the col is equal to the last column of a row. write-only value without the comma.
'write cellVal on text file If col = rng.Columns.Count Then Write #1, cellVal Else Write #1, cellVal, End If
Close both for loops
Next col Next row
Close the file
Close #1
Approach:
Step 1: Add a shape (Create Text File) to your worksheet
Step 2: Right-click on “Create a Text file” and “Assign Macro..”
Step 3: Select MacroToCreateTextFile
Step 4: Save your excel file as “Excel Macro-Enabled Workbook” *.xlsm
Step 5: Click “Create Text file”
Pandas можно использовать для чтения и записи файлов Excel с помощью Python. Это работает по аналогии с другими форматами. В этом материале рассмотрим, как это делается с помощью DataFrame.
Помимо чтения и записи рассмотрим, как записывать несколько DataFrame в Excel-файл, как считывать определенные строки и колонки из таблицы и как задавать имена для одной или нескольких таблиц в файле.
Установка Pandas
Для начала Pandas нужно установить. Проще всего это сделать с помощью pip
.
Если у вас Windows, Linux или macOS:
pip install pandas # или pip3
В процессе можно столкнуться с ошибками ModuleNotFoundError
или ImportError
при попытке запустить этот код. Например:
ModuleNotFoundError: No module named 'openpyxl'
В таком случае нужно установить недостающие модули:
pip install openpyxl xlsxwriter xlrd # или pip3
Будем хранить информацию, которую нужно записать в файл Excel, в DataFrame
. А с помощью встроенной функции to_excel()
ее можно будет записать в Excel.
Сначала импортируем модуль pandas
. Потом используем словарь для заполнения DataFrame
:
import pandas as pd
df = pd.DataFrame({'Name': ['Manchester City', 'Real Madrid', 'Liverpool',
'FC Bayern München', 'FC Barcelona', 'Juventus'],
'League': ['English Premier League (1)', 'Spain Primera Division (1)',
'English Premier League (1)', 'German 1. Bundesliga (1)',
'Spain Primera Division (1)', 'Italian Serie A (1)'],
'TransferBudget': [176000000, 188500000, 90000000,
100000000, 180500000, 105000000]})
Ключи в словаре — это названия колонок. А значения станут строками с информацией.
Теперь можно использовать функцию to_excel()
для записи содержимого в файл. Единственный аргумент — это путь к файлу:
df.to_excel('./teams.xlsx')
А вот и созданный файл Excel:
Стоит обратить внимание на то, что в этом примере не использовались параметры. Таким образом название листа в файле останется по умолчанию — «Sheet1». В файле может быть и дополнительная колонка с числами. Эти числа представляют собой индексы, которые взяты напрямую из DataFrame.
Поменять название листа можно, добавив параметр sheet_name
в вызов to_excel()
:
df.to_excel('./teams.xlsx', sheet_name='Budgets', index=False)
Также можно добавили параметр index
со значением False
, чтобы избавиться от колонки с индексами. Теперь файл Excel будет выглядеть следующим образом:
Запись нескольких DataFrame в файл Excel
Также есть возможность записать несколько DataFrame в файл Excel. Для этого можно указать отдельный лист для каждого объекта:
salaries1 = pd.DataFrame({'Name': ['L. Messi', 'Cristiano Ronaldo', 'J. Oblak'],
'Salary': [560000, 220000, 125000]})
salaries2 = pd.DataFrame({'Name': ['K. De Bruyne', 'Neymar Jr', 'R. Lewandowski'],
'Salary': [370000, 270000, 240000]})
salaries3 = pd.DataFrame({'Name': ['Alisson', 'M. ter Stegen', 'M. Salah'],
'Salary': [160000, 260000, 250000]})
salary_sheets = {'Group1': salaries1, 'Group2': salaries2, 'Group3': salaries3}
writer = pd.ExcelWriter('./salaries.xlsx', engine='xlsxwriter')
for sheet_name in salary_sheets.keys():
salary_sheets[sheet_name].to_excel(writer, sheet_name=sheet_name, index=False)
writer.save()
Здесь создаются 3 разных DataFrame с разными названиями, которые включают имена сотрудников, а также размер их зарплаты. Каждый объект заполняется соответствующим словарем.
Объединим все три в переменной salary_sheets
, где каждый ключ будет названием листа, а значение — объектом DataFrame
.
Дальше используем движок xlsxwriter
для создания объекта writer
. Он и передается функции to_excel()
.
Перед записью пройдемся по ключам salary_sheets
и для каждого ключа запишем содержимое в лист с соответствующим именем. Вот сгенерированный файл:
Можно увидеть, что в этом файле Excel есть три листа: Group1, Group2 и Group3. Каждый из этих листов содержит имена сотрудников и их зарплаты в соответствии с данными в трех DataFrame
из кода.
Параметр движка в функции to_excel()
используется для определения модуля, который задействуется библиотекой Pandas для создания файла Excel. В этом случае использовался xslswriter
, который нужен для работы с классом ExcelWriter
. Разные движка можно определять в соответствии с их функциями.
В зависимости от установленных в системе модулей Python другими параметрами для движка могут быть openpyxl
(для xlsx или xlsm) и xlwt (для xls). Подробности о модуле xlswriter
можно найти в официальной документации.
Наконец, в коде была строка writer.save()
, которая нужна для сохранения файла на диске.
Чтение файлов Excel с python
По аналогии с записью объектов DataFrame
в файл Excel, эти файлы можно и читать, сохраняя данные в объект DataFrame
. Для этого достаточно воспользоваться функцией read_excel()
:
top_players = pd.read_excel('./top_players.xlsx')
top_players.head()
Содержимое финального объекта можно посмотреть с помощью функции head()
.
Примечание:
Этот способ самый простой, но он и способен прочесть лишь содержимое первого листа.
Посмотрим на вывод функции head()
:
Name | Age | Overall | Potential | Positions | Club | |
---|---|---|---|---|---|---|
0 | L. Messi | 33 | 93 | 93 | RW,ST,CF | FC Barcelona |
1 | Cristiano Ronaldo | 35 | 92 | 92 | ST,LW | Juventus |
2 | J. Oblak | 27 | 91 | 93 | GK | Atlético Madrid |
3 | K. De Bruyne | 29 | 91 | 91 | CAM,CM | Manchester City |
4 | Neymar Jr | 28 | 91 | 91 | LW,CAM | Paris Saint-Germain |
Pandas присваивает метку строки или числовой индекс объекту DataFrame
по умолчанию при использовании функции read_excel()
.
Это поведение можно переписать, передав одну из колонок из файла в качестве параметра index_col
:
top_players = pd.read_excel('./top_players.xlsx', index_col='Name')
top_players.head()
Результат будет следующим:
Name | Age | Overall | Potential | Positions | Club |
---|---|---|---|---|---|
L. Messi | 33 | 93 | 93 | RW,ST,CF | FC Barcelona |
Cristiano Ronaldo | 35 | 92 | 92 | ST,LW | Juventus |
J. Oblak | 27 | 91 | 93 | GK | Atlético Madrid |
K. De Bruyne | 29 | 91 | 91 | CAM,CM | Manchester City |
Neymar Jr | 28 | 91 | 91 | LW,CAM | Paris Saint-Germain |
В этом примере индекс по умолчанию был заменен на колонку «Name» из файла. Однако этот способ стоит использовать только при наличии колонки со значениями, которые могут стать заменой для индексов.
Чтение определенных колонок из файла Excel
Иногда удобно прочитать содержимое файла целиком, но бывают случаи, когда требуется получить доступ к определенному элементу. Например, нужно считать значение элемента и присвоить его полю объекта.
Это делается с помощью функции read_excel()
и параметра usecols
. Например, можно ограничить функцию, чтобы она читала только определенные колонки. Добавим параметр, чтобы он читал колонки, которые соответствуют значениям «Name», «Overall» и «Potential».
Для этого укажем числовой индекс каждой колонки:
cols = [0, 2, 3]
top_players = pd.read_excel('./top_players.xlsx', usecols=cols)
top_players.head()
Вот что выдаст этот код:
Name | Overall | Potential | |
---|---|---|---|
0 | L. Messi | 93 | 93 |
1 | Cristiano Ronaldo | 92 | 92 |
2 | J. Oblak | 91 | 93 |
3 | K. De Bruyne | 91 | 91 |
4 | Neymar Jr | 91 | 91 |
Таким образом возвращаются лишь колонки из списка cols
.
В DataFrame
много встроенных возможностей. Легко изменять, добавлять и агрегировать данные. Даже можно строить сводные таблицы. И все это сохраняется в Excel одной строкой кода.
Рекомендую изучить DataFrame в моих уроках по Pandas.
Выводы
В этом материале были рассмотрены функции read_excel()
и to_excel()
из библиотеки Pandas. С их помощью можно считывать данные из файлов Excel и выполнять запись в них. С помощью различных параметров есть возможность менять поведение функций, создавая нужные файлы, не просто копируя содержимое из объекта DataFrame
.
В этом примере показано, как записывать данные Excel в рабочую книгу. Вы можете сделать это, открыв приложение Excel и используя его в качестве сервера для управления книгами Excel.
Сначала откройте диалоговое окно «Добавить ссылки». На вкладке COM выберите «Библиотека объектов Microsoft Excel 14.0» (или любую другую версию, которую вы установили в своей системе).
Затем добавьте следующую инструкцию using, чтобы упростить работу с пространством имен Excel. Часть Excel = означает, что вы можете использовать Excel как псевдоним для пространства имен.
using Excel = Microsoft.Office.Interop.Excel;
В этом примере используется следующий код, чтобы открыть книгу, добавить в нее новый лист, записать на рабочий лист, сохранить изменения и закрыть все.
// Запись в книгу Excel. private void btnWrite_Click(object sender, EventArgs e) { // Получить объект приложения Excel. Excel.Application excel_app = new Excel.ApplicationClass(); // Сделать Excel видимым (необязательно). excel_app.Visible = true; // Откройте книгу. Excel.Workbook workbook = excel_app.Workbooks.Open( txtFile.Text, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing); // Посмотрим, существует ли рабочий лист. string sheet_name = DateTime.Now.ToString("MM-dd-yy"); Excel.Worksheet sheet = FindSheet(workbook, sheet_name); if (sheet == null) { // Добавить лист в конце. sheet = (Excel.Worksheet)workbook.Sheets.Add( Type.Missing, workbook.Sheets[workbook.Sheets.Count], 1, Excel.XlSheetType.xlWorksheet); sheet.Name = DateTime.Now.ToString("MM-dd-yy"); } // Добавить некоторые данные в отдельные ячейки. sheet.Cells[1, 1] = "A"; sheet.Cells[1, 2] = "B"; sheet.Cells[1, 3] = "C"; // Делаем этот диапазон ячеек жирным и красным. Excel.Range header_range = sheet.get_Range("A1", "C1"); header_range.Font.Bold = true; header_range.Font.Color = System.Drawing.ColorTranslator.ToOle( System.Drawing.Color.Red); header_range.Interior.Color = System.Drawing.ColorTranslator.ToOle( System.Drawing.Color.Pink); // Добавьте некоторые данные в диапазон ячеек. int[,] values = { { 2, 4, 6}, { 3, 6, 9}, { 4, 8, 12}, { 5, 10, 15}, }; Excel.Range value_range = sheet.get_Range("A2", "C5"); value_range.Value2 = values; // Сохраните изменения и закройте книгу. workbook.Close(true, Type.Missing, Type.Missing); // Закройте сервер Excel. excel_app.Quit(); MessageBox.Show("Done"); }
Сначала код создает объект Excel.Application для управления Excel. Это делает этот объект видимым, чтобы вы могли наблюдать за его работой. Часто вы не хотите, чтобы сервер был видимым.
Далее код открывает рабочую книгу. Большинство параметров метода Open являются необязательными (например, пароли и флаги, указывающие на то, что вы хотите открыть файл только для чтения), поэтому им присваивается значение Type.Missing .
Затем код вызывает метод FindSheet, описанный в ближайшее время, чтобы узнать, содержит ли рабочая тетрадь рабочий лист с именем текущей даты. (Извините за жесткое кодирование в формате даты. Обычно я бы использовал DateTime.Now.ToShortDate (), чтобы получить подходящую дату, но, по крайней мере, для меня он будет содержать / символы, которые не являются разрешено в именах рабочих листов.)
Если нет рабочего листа с этим именем, код создает его, добавляет его после последнего рабочего листа и устанавливает имя листа на текущую дату.
Далее код устанавливает значения трех ячеек отдельно. Он создает Range, представляющий эти три ячейки, и устанавливает их значения Font.Bold, Color и Interior.Color .
Затем код показывает, как установить значения листа из массива. Он создает 2-мерный массив, делает Range того же размера и устанавливает свойство Range объекта Value2 в массив, чтобы установить значения клеток.
Наконец, код закрывает книгу, сохраняет изменения и закрывает сервер Excel.
В следующем коде показан метод FindSheet.
// Вернуть рабочий лист с заданным именем. private Excel.Worksheet FindSheet(Excel.Workbook workbook, string sheet_name) { foreach (Excel.Worksheet sheet in workbook.Sheets) { if (sheet.Name == sheet_name) return sheet; } return null; }
Этот метод просто просматривает рабочие листы книги, чтобы увидеть, содержит ли он имя с указанным именем.
Источник: http://csharphelper.com/blog/2014/11/write-excel-data-in-c/
Как сохранить выделенный диапазон в отдельный файл?
Могут возникать ситуации, при которых необходимо отправить кому-либо определенную информацию, например, небольшую часть большого отчета и сделать это таким образом, чтобы не посвящать получателя в лишние детали – расчеты, итоги, показатели и так далее.
Обычно, у пользователей не возникает проблем с тем, чтобы скопировать диапазон ячеек и вставить либо в отдельную рабочую книгу при помощи специальной вставки значений, чтобы заменить формулы результатами вычислений, либо в документ приложения Word. Даже такая простая операция требует от пользователя выполнения целого ряда определенных действий: скопировать диапазон, создать новый файл в нужном формате, вставить диапазон, сохранить новый файл. Если возникает необходимость проделывать такую операцию по несколько раз в день, стоит задуматься об автоматизации этого процесса.
Экспорт диапазона ячеек в файл
При помощи надстройки для Excel можно сохранять выделенные диапазоны ячеек в отдельные файлы выбранного формата, причем делать это быстро и удобно.
Надстройка – это дополнительные функциональные возможности для приложения. Чтобы воспользоваться такими возможностями, надстройку необходимо установить. При этом, как правило, создается новая вкладка на ленте Excel и создается кнопка вызова диалогового окна.
При вызове диалогового окна все поля автоматически заполняются значениями, а пользователю оставляется возможность внести в них свои изменения, после чего запустить программу.
В поле «Выбор диапазона» автоматически подставляется адрес выделенного на листе диапазона ячеек, либо несколько адресов через знак-разделитель «;» (точка с запятой), если выделено несколько диапазонов.
В поле «Выбор папки» автоматически подставляется путь к папке, в которой находится рабочая книга с копируемым диапазоном. При помощи кнопки рядом с полем можно выбрать любую другую папку.
В поле «Выбор имени» автоматически подтягивается имя активной рабочей книги, то есть той, которая открыта в момент вызова диалогового окна. Имя также можно изменить на любое другое.
Пользователю остается только выбрать формат файла, в который будет вставлен выделенный диапазон и запустить программу.
Как сохранить выделенные диапазоны изображениями?
Обычно, делается скриншот, то есть снимается изображение с экрана кнопкой «Print Screen» на клавиатуре, после этого изображение из буфера обмена вставляется в графический редактор и уже там вырезается нужный фрагмент изображения. При помощи надстройки всего этого можно не делать, достаточно выделить диапазон ячеек, выбрать формат «JPG» и запустить программу. Изображение выделенного диапазона будет сохранено в заданную папку с заданным именем в формате JPG.
Как сохранить выделенный диапазон отдельной книгой?
Исходный диапазон ячеек может быть определенным образом отформатирован, иметь определенные шрифты, ширины столбцов и так далее, кроме этого в ячейках диапазона могут содержаться формулы со ссылками на ячейки в других листах и других рабочих книгах. Чтобы скопировать такой диапазон в другую книгу, необходимо проделать ряд операций: скопировать диапазон, вставить диапазон, для вставки ширин столбцов вставить диапазон еще раз при помощи специальной вставки, для переноса форматов вставить диапазон еще раз при помощи специальной вставки форматов, заменить формулы значениями ячеек. При помощи надстройки все эти операции выполняются автоматически.
Как сохранить диапазон документом Word?
Выделенный диапазон можно сохранить документом Word, при этом, при копировании и вставки диапазона из Excel в Word, таблица может не поместиться на странице по ширине. В надстройке для таких случаев предусмотрено автоматическое изменение ориентации страницы с книжной на альбомную. Разворот страницы тоже не всегда может помочь, поэтому после вставки диапазона в документ Word необходимо убедиться полностью ли помещается вставленная таблица в размеры страницы.
Как сохранить диапазон в pdf-файл
При сохранении диапазонов ячеек в pdf-формат, также есть возможность изменения ориентации страницы, но все-таки после создания файла лучше убедиться, что область выделения из Excel помещается на одну страницу в горизонтальном измерении, иначе часть данных из выделенного диапазона в pdf-файле будет не видна.
Добрый день, есть список, его сотрудники пополняют и в конце рабочего дня они выделяют все записи за день и нажимаю кнопку, запись выделенного (на нем макрос)
суть макроса копирование выделенной области на др лист (уже сделал) и копирование выделенных данных в отдельный файл и его пополнение (не сделал) и хочу еще отчищать раб область (лист куда сотрудники в течение дня вводят записи). Очень нужна ваша помощь Макросы эксель только начал изучать.
вот код макроса, копия на др лист
Добрый день, есть список, его сотрудники пополняют и в конце рабочего дня они выделяют все записи за день и нажимаю кнопку, запись выделенного (на нем макрос)
суть макроса копирование выделенной области на др лист (уже сделал) и копирование выделенных данных в отдельный файл и его пополнение (не сделал) и хочу еще отчищать раб область (лист куда сотрудники в течение дня вводят записи). Очень нужна ваша помощь Макросы эксель только начал изучать.
вот код макроса, копия на др лист
Сообщение Добрый день, есть список, его сотрудники пополняют и в конце рабочего дня они выделяют все записи за день и нажимаю кнопку, запись выделенного (на нем макрос)
суть макроса копирование выделенной области на др лист (уже сделал) и копирование выделенных данных в отдельный файл и его пополнение (не сделал) и хочу еще отчищать раб область (лист куда сотрудники в течение дня вводят записи). Очень нужна ваша помощь Макросы эксель только начал изучать.
вот код макроса, копия на др лист
понял вас, значит так по скрипту вашему
копирует замечательно, но копирует всю строку а не выделенную область.
хотел объединить мое копирование в лист и ваше в файл не получилось
Sub ЗаписьВОбщийЖурнал()
‘
‘ Макрос2 Макрос
‘ Макрос записан 15.10.2007 (VladimirovAV)
‘
Selection.Copy
Sheets(«База данных»).Select
Range(«A1″).Select
Selection.End(xlDown).Select
ActiveCell.Offset(1, 0).Select
ActiveSheet.Paste
ActiveWorkbook.Save
Sheets(«Исходные данные»).Select
Selection.ClearContents
Range(«A3″).Select
If Not TypeName(Selection) = «Range» Then Exit Sub
Dim lr&, wb As Workbook, lb As Workbook
With Application: .ScreenUpdating = False: .DisplayAlerts = False: .EnableEvents = False: End With
Set wb = GetObject(«c:test.xls») ‘путь к файлу-накопителю
Set lb = ThisWorkbook
lr = wb.Sheets(1).Cells(Rows.Count, 1).End(xlUp).Row
Selection.EntireRow.Copy wb.Sheets(1).Cells(lr + 1, 1)
wb.Close (True) ‘ закрыть с сохранением
With Application: .EnableEvents = True: .DisplayAlerts = True: .ScreenUpdating = True: End With
Set wb = Nothing: Set lb = Nothing
понял вас, значит так по скрипту вашему
копирует замечательно, но копирует всю строку а не выделенную область.
хотел объединить мое копирование в лист и ваше в файл не получилось
Sub ЗаписьВОбщийЖурнал()
‘
‘ Макрос2 Макрос
‘ Макрос записан 15.10.2007 (VladimirovAV)
‘
Selection.Copy
Sheets(«База данных»).Select
Range(«A1″).Select
Selection.End(xlDown).Select
ActiveCell.Offset(1, 0).Select
ActiveSheet.Paste
ActiveWorkbook.Save
Sheets(«Исходные данные»).Select
Selection.ClearContents
Range(«A3″).Select
If Not TypeName(Selection) = «Range» Then Exit Sub
Dim lr&, wb As Workbook, lb As Workbook
With Application: .ScreenUpdating = False: .DisplayAlerts = False: .EnableEvents = False: End With
Set wb = GetObject(«c:test.xls») ‘путь к файлу-накопителю
Set lb = ThisWorkbook
lr = wb.Sheets(1).Cells(Rows.Count, 1).End(xlUp).Row
Selection.EntireRow.Copy wb.Sheets(1).Cells(lr + 1, 1)
wb.Close (True) ‘ закрыть с сохранением
With Application: .EnableEvents = True: .DisplayAlerts = True: .ScreenUpdating = True: End With
Set wb = Nothing: Set lb = Nothing
Сообщение понял вас, значит так по скрипту вашему
копирует замечательно, но копирует всю строку а не выделенную область.
хотел объединить мое копирование в лист и ваше в файл не получилось
Sub ЗаписьВОбщийЖурнал()
‘
‘ Макрос2 Макрос
‘ Макрос записан 15.10.2007 (VladimirovAV)
‘
Selection.Copy
Sheets(«База данных»).Select
Range(«A1″).Select
Selection.End(xlDown).Select
ActiveCell.Offset(1, 0).Select
ActiveSheet.Paste
ActiveWorkbook.Save
Sheets(«Исходные данные»).Select
Selection.ClearContents
Range(«A3″).Select
If Not TypeName(Selection) = «Range» Then Exit Sub
Dim lr&, wb As Workbook, lb As Workbook
With Application: .ScreenUpdating = False: .DisplayAlerts = False: .EnableEvents = False: End With
Set wb = GetObject(«c:test.xls») ‘путь к файлу-накопителю
Set lb = ThisWorkbook
lr = wb.Sheets(1).Cells(Rows.Count, 1).End(xlUp).Row
Selection.EntireRow.Copy wb.Sheets(1).Cells(lr + 1, 1)
wb.Close (True) ‘ закрыть с сохранением
With Application: .EnableEvents = True: .DisplayAlerts = True: .ScreenUpdating = True: End With
Set wb = Nothing: Set lb = Nothing
отказывается копировать Автор — aydar
Дата добавления — 22.03.2013 в 13:43
убрал, не получилось
убрал, не получилось
Сообщение убрал, не получилось
Sub ЗаписьВОбщийЖурнал()
‘
‘ Макрос2 Макрос
Selection.Copy
Sheets(«База данных»).Select
Range(«A1»).Select
Selection.End(xlDown).Select
ActiveCell.Offset(1, 0).Select
ActiveSheet.Paste
ActiveWorkbook.Save
Sheets(«Исходные данные»).Select
Selection.ClearContents
Range(«A3»).Select
‘это часть работает замечательно
‘ это копирования выделения в другой файл
If Not TypeName(Selection) = «Range» Then Exit Sub
Dim lr&, wb As Workbook, lb As Workbook
With Application: .ScreenUpdating = False: .DisplayAlerts = False: .EnableEvents = False: End With
Set wb = GetObject(«c:test.xls») ‘путь к файлу-накопителю
Set lb = ThisWorkbook
lr = wb.Sheets(1).Cells(Rows.Count, 1).End(xlUp).Row
Selection.Copy wb.Sheets(1).Cells(lr + 1, 1)
wb.Close (True) ‘ закрыть с сохранением
With Application: .EnableEvents = True: .DisplayAlerts = True: .ScreenUpdating = True: End With
Set wb = Nothing: Set lb = Nothing
копирование в другой файл работает криво, копируется вся строка а не выделение,иногда не все данные копируются
Sub ЗаписьВОбщийЖурнал()
‘
‘ Макрос2 Макрос
Selection.Copy
Sheets(«База данных»).Select
Range(«A1»).Select
Selection.End(xlDown).Select
ActiveCell.Offset(1, 0).Select
ActiveSheet.Paste
ActiveWorkbook.Save
Sheets(«Исходные данные»).Select
Selection.ClearContents
Range(«A3»).Select
‘это часть работает замечательно
‘ это копирования выделения в другой файл
If Not TypeName(Selection) = «Range» Then Exit Sub
Dim lr&, wb As Workbook, lb As Workbook
With Application: .ScreenUpdating = False: .DisplayAlerts = False: .EnableEvents = False: End With
Set wb = GetObject(«c:test.xls») ‘путь к файлу-накопителю
Set lb = ThisWorkbook
lr = wb.Sheets(1).Cells(Rows.Count, 1).End(xlUp).Row
Selection.Copy wb.Sheets(1).Cells(lr + 1, 1)
wb.Close (True) ‘ закрыть с сохранением
With Application: .EnableEvents = True: .DisplayAlerts = True: .ScreenUpdating = True: End With
Set wb = Nothing: Set lb = Nothing
копирование в другой файл работает криво, копируется вся строка а не выделение,иногда не все данные копируются aydar
Sub ЗаписьВОбщийЖурнал()
‘
‘ Макрос2 Макрос
Selection.Copy
Sheets(«База данных»).Select
Range(«A1»).Select
Selection.End(xlDown).Select
ActiveCell.Offset(1, 0).Select
ActiveSheet.Paste
ActiveWorkbook.Save
Sheets(«Исходные данные»).Select
Selection.ClearContents
Range(«A3»).Select
‘это часть работает замечательно
‘ это копирования выделения в другой файл
If Not TypeName(Selection) = «Range» Then Exit Sub
Dim lr&, wb As Workbook, lb As Workbook
With Application: .ScreenUpdating = False: .DisplayAlerts = False: .EnableEvents = False: End With
Set wb = GetObject(«c:test.xls») ‘путь к файлу-накопителю
Set lb = ThisWorkbook
lr = wb.Sheets(1).Cells(Rows.Count, 1).End(xlUp).Row
Selection.Copy wb.Sheets(1).Cells(lr + 1, 1)
wb.Close (True) ‘ закрыть с сохранением
With Application: .EnableEvents = True: .DisplayAlerts = True: .ScreenUpdating = True: End With
Set wb = Nothing: Set lb = Nothing
копирование в другой файл работает криво, копируется вся строка а не выделение,иногда не все данные копируются Автор — aydar
Дата добавления — 26.03.2013 в 11:36
Ну вот, зарегистрировались, теперь можно и попытаться разобраться.
У меня на моих файлах работает. Но у меня макрос уже «допиленный»
Проблема может быть в структуре данных.
Например, в приведённом Вами фрагменте кода
lr = wb.Sheets(1).Cells(Rows.Count, 1).End(xlUp).Row определяет ряд с первой пустой ячейкой В ПЕРВОМ СТОЛБЦЕ.
Поэтому если Вы копируете диапазон, где в ячейках первого столбца есть пустые, то lr определится не правильно и следующая запись наложится на предыдущую. Об этом, к стати, всё расписано в том топике, откуда Вы брали фрагмент. Вы дальше первого топика читали? Ну, например, ЭТО.
Приложите пример. Тогда кто-нибудь попробует разобраться почему у Вас не работает. (Я не смогу — на работе завал, да ещё и сисадмины закрыли скачивание файлов с макросами, собаки!)
Ну вот, зарегистрировались, теперь можно и попытаться разобраться.
У меня на моих файлах работает. Но у меня макрос уже «допиленный»
Проблема может быть в структуре данных.
Например, в приведённом Вами фрагменте кода
lr = wb.Sheets(1).Cells(Rows.Count, 1).End(xlUp).Row определяет ряд с первой пустой ячейкой В ПЕРВОМ СТОЛБЦЕ.
Поэтому если Вы копируете диапазон, где в ячейках первого столбца есть пустые, то lr определится не правильно и следующая запись наложится на предыдущую. Об этом, к стати, всё расписано в том топике, откуда Вы брали фрагмент. Вы дальше первого топика читали? Ну, например, ЭТО.
Приложите пример. Тогда кто-нибудь попробует разобраться почему у Вас не работает. (Я не смогу — на работе завал, да ещё и сисадмины закрыли скачивание файлов с макросами, собаки!) Alex_ST
Сообщение Ну вот, зарегистрировались, теперь можно и попытаться разобраться.
У меня на моих файлах работает. Но у меня макрос уже «допиленный»
Проблема может быть в структуре данных.
Например, в приведённом Вами фрагменте кода
lr = wb.Sheets(1).Cells(Rows.Count, 1).End(xlUp).Row определяет ряд с первой пустой ячейкой В ПЕРВОМ СТОЛБЦЕ.
Поэтому если Вы копируете диапазон, где в ячейках первого столбца есть пустые, то lr определится не правильно и следующая запись наложится на предыдущую. Об этом, к стати, всё расписано в том топике, откуда Вы брали фрагмент. Вы дальше первого топика читали? Ну, например, ЭТО.
Приложите пример. Тогда кто-нибудь попробует разобраться почему у Вас не работает. (Я не смогу — на работе завал, да ещё и сисадмины закрыли скачивание файлов с макросами, собаки!) Автор — Alex_ST
Дата добавления — 26.03.2013 в 12:39
Как файл из Эксель сохранить в ПДФ: 4 основных способа
Способов, как сохранить Эксель в ПДФ, так много, что воспользоваться ими может и владелец компьютера с современным программным обеспечением, и пользователь старого ПК, на котором даже нет пакета MS Office.
Программа Excel предназначена для работы с электронными таблицами, с помощью которых можно проводить расчеты, создавать и редактировать графики или просто хранить информацию. Однако структура получаемых документов получается достаточно сложной, а размеры файлов — большими.
Для упрощения документооборота и ускорения их отправки по электронной почте таблицы Эксель можно сохранить в формате PDF. Полученный документ не только уменьшится в размере, но и будет одинаково отображаться в разных программах и на всех устройствах, включая телефоны.
Конвертация в программе Excel
Разработчики офисного пакета приложений MS Office постоянно добавляют в него новые функции. И в современных версиях, начиная с 2010 года, с помощью редактора Excel можно сразу переводить документы в множество различных форматов, без применения дополнительных приложений.
Все привет, в этой статье опишу исчерпывающие примеры работы с excel на языке C#.
Для начала работы нам необходимо подключить библиотеку COM как на рисунке ниже:
Для этого добавляем ссылку в проект, надеюсь вы знаете как это делается) Выбираем пункт COM ищем библиотеку Microsoft Excel 16.0 Object Library ставим галочку и жмем Ок.
Далее нам не обходимо для сокращения записи и удобства создать алиас.
using Excel = Microsoft.Office.Interop.Excel; |
Теперь нам нужно объявить объект Excel задать параметры и приступать к работе.
//Объявляем приложение Excel.Application app = new Excel.Application { //Отобразить Excel Visible = true, //Количество листов в рабочей книге SheetsInNewWorkbook = 2 }; //Добавить рабочую книгу Excel.Workbook workBook = app.Workbooks.Add(Type.Missing); //Отключить отображение окон с сообщениями app.DisplayAlerts = false; //Получаем первый лист документа (счет начинается с 1) Excel.Worksheet sheet = (Excel.Worksheet)app.Worksheets.get_Item(1); //Название листа (вкладки снизу) sheet.Name = «Имя должно быть не больше 32сим»; |
Пример заполнения ячейки:
//Пример заполнения ячеек №1 for (int i = 1; i <= 9; i++) { for (int j = 1; j < 9; j++) sheet.Cells[i, j] = String.Format(«nookery {0} {1}», i, j); } //Пример №2 sheet.Range[«A1»].Value = «Пример №2»; //Пример №3 sheet.get_Range(«A2»).Value2 = «Пример №3»; |
Захват диапазона ячеек:
//Захватываем диапазон ячеек Вариант №1 Excel.Range r1 = sheet.Cells[1, 1]; Excel.Range r2 = sheet.Cells[9, 9]; Excel.Range range1 = sheet.get_Range(r1, r2); //Захватываем диапазон ячеек Вариант №2 Excel.Range range2 = sheet.get_Range(«A1»,«H9» ); |
Оформление, шрифт, размер, цвет, толщина.
//Шрифт для диапазона range.Cells.Font.Name = «Tahoma»; range2.Cells.Font.Name = «Times New Roman»; //Размер шрифта для диапазона range.Cells.Font.Size = 10; //Жирный текст range.Font.Bold = true; //Цвет текста range.Font.Color = ColorTranslator.ToOle(Color.Blue); |
Объединение ячеек в одну
//Объединение ячеек с F2 по K2 Excel.Range range3 = sheet.get_Range(«F2», «K2»); range3.Merge(Type.Missing); |
Изменяем размеры ячеек по ширине и высоте
//увеличиваем размер по ширине диапазон ячеек Excel.Range range2 = sheet.get_Range(«D1», «S1»); range2.EntireColumn.ColumnWidth = 10; //увеличиваем размер по высоте диапазон ячеек Excel.Range rowHeight = sheet.get_Range(«A4», «S4»); rowHeight.EntireRow.RowHeight = 50;range.EntireColumn.AutoFit();range.EntireColumn.AutoFit(); //авторазмер |
Создаем обводку диапазона ячеек
Excel.Range r1 = sheet.Cells[countRow, 2]; Excel.Range r2 = sheet.Cells[countRow, 19]; Excel.Range rangeColor = sheet.get_Range(r1, r2); rangeColor.Borders.Color = ColorTranslator.ToOle(Color.Black); |
Производим выравнивания содержимого диапазона ячеек.
Excel.Range r = sheet.get_Range(«A1», «S40»); //Оформления r.Font.Name = «Calibri»; r.Cells.Font.Size = 10; r.VerticalAlignment = Excel.XlVAlign.xlVAlignCenter; r.HorizontalAlignment = Excel.XlHAlign.xlHAlignCenter; |
Примеры вычисления формул, все вставки формул были скопированы из самой Excel без изменений. Позиция ячейки взята из счетчика переменно и подставлен к букве ячейки
sheet.Cells[countRow, countColumn] = $«=G{countRow}-F{countRow}»; sheet.Cells[countRow, countColumn].FormulaLocal = $«=ЕСЛИ((H{countRow}*O{countRow})+(I{countRow}*P{countRow})/100<=0;J{countRow}*O{countRow}/100;((H{countRow}*O{countRow})+(I{countRow}*P{countRow}))/100)»; sheet.Cells[countRow, countColumn] = $«=K{countRow}+N{countRow}-R{countRow}»; sheet.Cells[33, 22].FormulaLocal = «=СУММ(V3:V32)»; |
Добавляем разрыв страницы.
//Ячейка, с которой будет разрыв Excel.Range razr = sheet.Cells[n, m] as Excel.Range; //Добавить горизонтальный разрыв (sheet — текущий лист) sheet.HPageBreaks.Add(razr); //VPageBreaks — Добавить вертикальный разрыв |
Как открыть фаил Excel
app.Workbooks.Open(@»C:UsersUserDocumentsExcel.xlsx», Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing); |
Сохраняем документ Excel
app.Application.ActiveWorkbook.SaveAs(«MyFile.xlsx», Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Excel.XlSaveAsAccessMode.xlNoChange, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing); |
Завершение работы с объектом Excel.Application
app.Quit(); System.Runtime.InteropServices.Marshal.ReleaseComObject(app); |
Пример как выбрать фаил и загрузив его и узнать количество заполненных строк и колонок в одном конкретном листе по имени.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
//поиск файла Excel OpenFileDialog ofd = new OpenFileDialog(); ofd.Multiselect =false; ofd.DefaultExt = «*.xls;*.xlsx»; ofd.Filter = «Microsoft Excel (*.xls*)|*.xls*»; ofd.Title = «Выберите документ Excel»; if (ofd.ShowDialog() != DialogResult.OK) { MessageBox.Show(«Вы не выбрали файл для открытия», «Внимание», MessageBoxButtons.OK, MessageBoxIcon.Information); return; } string xlFileName = ofd.FileName; //имя нашего Excel файла //рабоата с Excel Excel.Range Rng; Excel.Workbook xlWB; Excel.Worksheet xlSht; int iLastRow, iLastCol; Excel.Application xlApp = new Excel.Application(); //создаём приложение Excel xlWB = xlApp.Workbooks.Open(xlFileName); //открываем наш файл xlSht = xlWB.Worksheets[«Лист1»]; //или так xlSht = xlWB.ActiveSheet //активный лист iLastRow = xlSht.Cells[xlSht.Rows.Count, «A»].End[Excel.XlDirection.xlUp].Row; //последняя заполненная строка в столбце А iLastCol = xlSht.Cells[1, xlSht.Columns.Count].End[Excel.XlDirection.xlToLeft].Column; //последний заполненный столбец в 1-й строке |
Получаем список всех загруженных книг «листов» из файла
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
//поиск файла Excel OpenFileDialog ofd = new OpenFileDialog(); ofd.Multiselect = false; ofd.DefaultExt = «*.xls;*.xlsx»; ofd.Filter = «Microsoft Excel (*.xls*)|*.xls*»; ofd.Title = «Выберите документ Excel»; if (ofd.ShowDialog() != DialogResult.OK) { MessageBox.Show(«Вы не выбрали файл для открытия», «Внимание», MessageBoxButtons.OK, MessageBoxIcon.Information); return; } string xlFileName = ofd.FileName; //имя нашего Excel файла Excel.Workbook xlWB = ex.Workbooks.Open(xlFileName); ///загружаем список всех книг foreach (object item in xlWB.Sheets) { Excel.Worksheet sheet = (Excel.Worksheet)item; } |
Время на прочтение
5 мин
Количество просмотров 43K
В продолжение темы, начатой в предыдущей статье, хочу поделиться своим опытом экспорта данных, в частности, в формате XLSX.
Итак, кому интересно, как заполнить XLSX без больших и сложных библиотек, прошу под кат.
Недавно передо мной возникла задача экспортировать непредсказуемый по размеру объем табличных данных в формате XLSX. Как любой здравомыслящий программист, первым делом полез искать готовые решения.
Почти сразу наткнулся на библиотеку PHPExcel. Мощное решение, с кучей разных функций и возможностей. Порывшись еще немного нашел отзывы программистов о ней. В частности, на форумах встречаются жалобы на скорость работы и отказ работать с большим объемом данных. Отметил библиотеку как один из вариантов решения и начал искать дальше.
Находил еще несколько библиотек для работы с XLSX, но все они были или забытыми, т.к. не обновлялись по 2-3 года, или обязательно тянули за собой сторонние библиотеки, или использовали DOM для работы с файлами, что мне не очень нравилось. Каждый раз, натыкаясь на очередную библиотеку и изучая механизмы ее работы, ловил себя на мысли, что все это «из пушки по воробьям». Не нужно мне такое сложное решение!
Признаюсь честно, изучив поверхностно каждое из найденных решений, не стал ставить и тестировать ни одного. Мне нужно было более простое и надежное, как танк, решение.
Задача
В общем, раз не нашел ничего подходящего, значит надо сформулировать технические требования к тому, что нужно. Требования, как и следовало ожидать, оказались тривиальными:
- Оформить экспортирующий механизм в виде автономного класса
- Реализовать в классе набор функций для записи значений ячеек и ряда
- Возможность работы с неограниченным объемом данных
- Распаковка и упаковка XLSX.
Отдельно остановлюсь только на последнем пункте. Как известно, XLSX представляет собой обычный zip-архив, который можно распаковать и увидеть, что он состоит из нескольких файлов и каталогов. Обратным образом его можно упаковать и переименовать в XLSX. Если все изменения правильные, то Microsoft Excel откроет файл без проблем.
Реализация
Изначально очень хотел создавать все файлы, из которых состоит XLSX, кодом, но, к счастью, быстро понял бессмысленность своей идеи. И родилось иное, более правильно и простое решение. Надо с помощью Microsoft Excel создать файл XLSX в таком виде, в каком он нужен в итоге, но без данных, иными словами — шаблон, а потом, с помощью кода, только добавить данные!
В таком случае, класс должен будет распаковывать шаблон в отдельный каталог, вносить изменения в /xl/worksheets/sheet1.xml и упаковывать содержимое каталога обратно в XLSX.
В объявлении класса присутствуют публичные переменные:
$templateFile – имя файла шаблона
$exportDir – папка, в которую будет распакован шаблон, разумеется с необходимыми правами доступа.
Конструктор класса принимает имя будущего файла, количество колонок и рядов. Потом проверяет, что имя файла корректно, папка для распаковки шаблона существует и формирует полное имя конечной папки для распаковки шаблона.
После создания класса можно распаковать шаблон и открыть на запись sheet1.xml. На самом деле я не просто дописываю в файл, а полностью его перезаписываю. Однажды взяв из него начальную строку, вношу в нее изменение в тэге dimension, который отражает размер экспортируемого диапазона, и записываю в файл.
public function openWriter()
{
if (is_dir($this->baseDir))
CFileHelper::removeDirectory($this->baseDir);
mkdir($this->baseDir);
exec("unzip $this->templateFullFilename -d "$this->baseDir"");
$this->workSheetHandler = fopen($this->baseDir.'/xl/worksheets/sheet1.xml', 'w+');
fwrite($this->workSheetHandler, '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"><dimension ref="A1:'.chr(64+$this->colCount).$this->rowCount.'"/><sheetData>');
}
Обеспечить скорость работы и возможность работы с большим объемом данных позволяют функции resetRow и flushRow. Они отвечают за очистку текущего ряда в памяти и запись текущего ряда на диск.
А вот сохранение значений ячеек с разными типами оказалось не такой простой задачей.
Запись строки
Казалось бы, что сложного записать строковое значение в файл. Однако, в XLSX все не так просто. Все строки внутри XLSX хранятся в отдельном файле /xl/sharedStrings.xml. В ячейки записываются не строковые значения, а их порядковые номера — индексы. Разумное решение с точки зрения сокращения размера файла.
Но такое решение неудобно с точки зрения программного заполнения шаблона. Если выполнять указанное требование, то мне бы пришлось выполнять отдельный проход по всем строковым значениям в массиве данных, исключать повторяющиеся, сохранять их в sharedStrings.xml, проиндексировать и вместо значений в исходном массиве вписать их индексы. Медленно и неудобно.
Оказывается, можно обойти требование и сохранять строковые значения ячеек прямо в ячейках. Но в этом случае формат записи будет иной:
public function appendCellString($value)
{
$this->curCel++;
if (!empty($value)) {
$value = htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
$value = preg_replace( '/[x00-x13]/', '', $value );
$this->currentRow[] = '<c r="'.chr(64+$this->curCel).$this->numRows.'" t="inlineStr"'.($this->isBold ? ' s="7"' : '').'><is><t>'.$value.'</t></is></c>';
$this->numStrings++;
}
}
Запись числа
Никаких сложностей с записью целых или дробных чисел не возникло. Все просто:
public function appendCellNum($value)
{
$this->curCel++;
$this->currentRow[] = '<c r="'.chr(64+$this->curCel).$this->numRows.'"><v>'.$value.'</v></c>';
}
Запись даты и времени
Дата и время хранятся в виде количества секунд прошедших с 01.01.1970 поделенных на количество секунд в сутках. Причем, в вычислении допущена ошибка с определением високосного года. В общем, не вдаваясь в подробности, которые несложно найти в сети, чтобы корректно вычислять дату пришлось объявить в классе две константы:
ZERO_TIMESTAMP – смещение даты в формате Excel от UNIX_TIMESTAMP
SEC_IN_DAY – секунд в сутках.
После вычисления значения даты и времени, целая часть дроби – это дата, дробная часть – время:
const ZERO_TIMESTAMP = 2209161600;
const SEC_IN_DAY = 86400;
public function appendCellDateTime($value)
{
$this->curCel++;
if (empty($value))
$this->appendCellString('');
else
{
$dt = new DateTime($value);
$ts = $dt->getTimestamp() + self::ZERO_TIMESTAMP;
$this->currentRow[] = '<c r="'.chr(64+$this->curCel).$this->numRows.'" s="1"><v>'.$ts/self::SEC_IN_DAY.'</v></c>';
}
}
После записи всех данных остается закрыть рабочий лист и рабочую книгу.
Применение
Как и раньше, использование описанного класса основано на экспорте данных с помощью провайдера CArrayDataProvider. Предполагая, что объем экспортируемых данных может оказаться очень большим, применен специальный итератор CDataProviderIterator, который перебирает возвращаемые данные по 100 записей (можно указать иное число записей).
public function exportXLSX($organization, $user, &$filename)
{
$this->_provider = new CArrayDataProvider(/*query*/);
Yii::import('ext.AlxdExportXLSX.AlxdExportXLSX');
$export = new AlxdExportXLSX($filename, count($this->_attributes), $this->_provider->getTotalItemCount() + 1);
$export->openWriter();
$export->resetRow();
$export->openRow(true);
foreach ($this->_attributes as $code => $format)
$export->appendCellString($this->_objectref->getAttributeLabel($code));
$export->closeRow();
$export->flushRow();
$rows = new CDataProviderIterator($this->_provider, 100);
foreach ($rows as $row)
{
$export->resetRow();
$export->openRow();
foreach ($this->_attributes as $code => $format)
{
switch ($format->type)
{
case 'Num':
$export->appendCellNum($row[$code]);
/*other types*/
default:
$export->appendCellString('');
}
}
$export->closeRow();
$export->flushRow();
}
$export->closeWriter();
$export->zip();
$filename = $export->getZipFullFileName();
}
Кому интересно, может получить исходный код моего класса AlxdExportXLSX совершенно безвозмездно.