使用Python中的openpyxl将行插入Excel电子表格

时间:2013-06-25 14:00:20

标签: python excel xlrd xlwt openpyxl

我正在寻找使用openpyxl在电子表格中插入行的最佳方法。

实际上,我有一个电子表格(Excel 2007),它有一个标题行,后面是(最多)几千行数据。我想将行插入第一行实际数据,所以在标题之后。我的理解是append函数适合于向文件的结尾添加内容。

阅读openpyxl和xlrd(以及xlwt)的文档,除了手动循环内容并插入新工作表(插入所需行之后)之外,我找不到任何明确的方法。

鉴于我迄今为止使用Python的经验有限,我试图了解这是否确实是最好的选择(最pythonic!),如果是这样,有人可以提供一个明确的例子。具体来说,我可以使用openpyxl读取和写入行,还是必须访问单元格?另外我可以(过)写同一个文件(名字)吗?

11 个答案:

答案 0 :(得分:17)

==根据此处的反馈更新为功能齐全的版本:groups.google.com/forum/#!topic/openpyxl-users/wHGecdQg3Iw。 ==

正如其他人所指出的,func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! UICollectionViewCell cell.backgroundColor = UIColor.orangeColor() let selectionView = UIView() selectionView.backgroundColor = UIColor.blackColor() cell.selectedBackgroundView = selectionView return cell } 没有提供此功能,但我已按如下方式扩展Parse.Cloud.beforeSave("Musics", function(request, response) { var newEntrySong = request.object; var querySongs = new Parse.Query("Musics"); querySongs.equalTo("title", newEntrySong.get("title")); querySongs.equalTo("description", newEntrySong.get("description")); // this could be a sort of signature for your song, to make more unique (skipping spaces and new lines for example) querySongs.equalTo("md5Title", newEntrySong.get("md5Title")); querySongs.first({ success: function(temp) { response.error({errorCode:123,errorMsg:"Song already exist!"}); }, error: function(error) { response.success(); } }); }); 类以实现插入行。希望这对其他人有用。

openpyxl

答案 1 :(得分:9)

使用我现在用来实现所需结果的代码来回答这个问题。请注意,我手动在第1位插入行,但这应该很容易根据特定需要进行调整。您也可以轻松调整此项以插入多行,并从相关位置开始填充其余数据。

另请注意,由于下游依赖性,我们手动指定“Sheet1”中的数据,并且数据将被复制到插入工作簿开头的新工作表,同时将原始工作表重命名为“Sheet1” 0.5' 。

编辑:我还添加了(稍后)对format_code的更改,以修复此处默认复制操作删除所有格式的问题:new_cell.style.number_format.format_code = 'mm/dd/yyyy'。我找不到任何可以设置的文档,这更像是一个反复试验的案例!

最后,不要忘记这个例子是保存原文。您可以更改适用的保存路径以避免这种情况。

    import openpyxl

    wb = openpyxl.load_workbook(file)
    old_sheet = wb.get_sheet_by_name('Sheet1')
    old_sheet.title = 'Sheet1.5'
    max_row = old_sheet.get_highest_row()
    max_col = old_sheet.get_highest_column()
    wb.create_sheet(0, 'Sheet1')

    new_sheet = wb.get_sheet_by_name('Sheet1')

    # Do the header.
    for col_num in range(0, max_col):
        new_sheet.cell(row=0, column=col_num).value = old_sheet.cell(row=0, column=col_num).value

    # The row to be inserted. We're manually populating each cell.
    new_sheet.cell(row=1, column=0).value = 'DUMMY'
    new_sheet.cell(row=1, column=1).value = 'DUMMY'

    # Now do the rest of it. Note the row offset.
    for row_num in range(1, max_row):
        for col_num in range (0, max_col):
            new_sheet.cell(row = (row_num + 1), column = col_num).value = old_sheet.cell(row = row_num, column = col_num).value

    wb.save(file)

答案 2 :(得分:6)

添加适用于openpyxl的最新版本v2.5 +的答案:

现在有insert_rows()insert_cols()

  

insert_rows(idx, amount=1)

     

在行== idx

之前插入一行或多行

答案 3 :(得分:5)

Openpyxl工作表在执行行级或列级操作时功能有限。工作表与行/列相关的唯一属性是属性row_dimensionscolumn_dimensions,它们分别为每个行和列存储“RowDimensions”和“ColumnDimensions”对象。这些词典也用于get_highest_row()get_highest_column()等功能。

其他所有操作都在单元格级别上运行,在字典中跟踪Cell对象_cells(并在字典_styles中跟踪它们的样式)。大多数在行或列级别上执行任何操作的函数实际上都在一系列单元格上运行(例如前面提到的append())。

最简单的做法是建议:创建新工作表,追加标题行,追加新数据行,追加旧数据行,删除旧工作表,然后将新工作表重命名为旧工作表。使用此方法可能出现的问题是行/列维度属性和单元格样式的丢失,除非您专门复制它们。

或者,您可以创建自己的插入行或列的函数。

我有大量非常简单的工作表,我需要从中删除列。既然您要求提供明确的示例,我将提供快速汇总的功能:

from openpyxl.cell import get_column_letter

def ws_delete_column(sheet, del_column):

    for row_num in range(1, sheet.get_highest_row()+1):
        for col_num in range(del_column, sheet.get_highest_column()+1):

            coordinate = '%s%s' % (get_column_letter(col_num),
                                   row_num)
            adj_coordinate = '%s%s' % (get_column_letter(col_num + 1),
                                       row_num)

            # Handle Styles.
            # This is important to do if you have any differing
            # 'types' of data being stored, as you may otherwise get
            # an output Worksheet that's got improperly formatted cells.
            # Or worse, an error gets thrown because you tried to copy
            # a string value into a cell that's styled as a date.

            if adj_coordinate in sheet._styles:
                sheet._styles[coordinate] = sheet._styles[adj_coordinate]
                sheet._styles.pop(adj_coordinate, None)
            else:
                sheet._styles.pop(coordinate, None)

            if adj_coordinate in sheet._cells:
                sheet._cells[coordinate] = sheet._cells[adj_coordinate]
                sheet._cells[coordinate].column = get_column_letter(col_num)
                sheet._cells[coordinate].row = row_num
                sheet._cells[coordinate].coordinate = coordinate

                sheet._cells.pop(adj_coordinate, None)
            else:
                sheet._cells.pop(coordinate, None)

        # sheet.garbage_collect()

我传递了我正在使用的工作表,以及我要删除的列号,然后就可以了。我知道这不是你想要的,但我希望这些信息有所帮助!

编辑:注意到有人给了另一张投票,并认为我应该更新它。 Openpyxl中的坐标系统在过去的几年中经历了一些变化,为coordinate中的项目引入了_cell属性。这也需要编辑,或者行将留空(而不是删除),Excel将抛出有关文件问题的错误。这适用于Openpyxl 2.2.3(未经测试的更高版本)

答案 4 :(得分:2)

从openpyxl 1.5开始,您现在可以使用.insert_rows(idx,row_qty)

from openpyxl import load_workbook
wb = load_workbook('excel_template.xlsx')
ws = wb.active
ws.insert_rows(14, 10)

如果您在Excel中手动进行操作,它将不会获取idx行的格式。您之后将应用正确的格式,即单元格颜色。

答案 5 :(得分:2)

我编写了一个函数,可以使用openpyxl在电子表格中的任何位置插入整个行,或者在整个2D表中插入。

函数的每一行都带有注释,但如果您只想插入一行,则使该行等于[row]。也就是说,如果row = [1,2,3,4,5],则将您的输入设置为[[1,2,3,4,5]]。如果希望将此行插入电子表格的第一行(A1),则开始= [1,1]。

您确实可以覆盖文件名,就像下面的示例一样。

def InputList(Start, List): #This function is to input an array/list from a input start point; len(Start) must equal 2, where Start = [1,1] is cell 1A. List must be a two dimensional array; if you wish to input a single row then this can be done where len(List) == 1, e.g. List = [[1,2,3,4]]
    x = 0 #Sets up a veriable to go through List columns
    y = 0 #Sets up a veriable to go through List rows
    l = 0 #Sets up a veriable to count addional columns against Start[1] to allow for column reset on each new row
    for row in List: #For every row in List
        l = 0 #Set additonal columns to zero
        for cell in row: #For every cell in row
            ws.cell(row=Start[0], column=Start[1]).value = List[y][x] #Set value for current cell
            x = x + 1 #Move to next data input (List) column
            Start[1] = Start[1] + 1 #Move to next Excel column
            l = l + 1 #Count addional row length
        y = y + 1 #Move to next Excel row
        Start[0] = Start[0] + 1 #Move to next Excel row
        x = 0 #Move back to first column of input data (ready for next row)
        Start[1] = Start[1] - l #Reset Excel column back to orignal start column, ready to write next row

在第7行的开头插入一行的示例:

from openpyxl import load_workbook
wb = load_workbook('New3.xlsx')
ws = wb.active

def InputList(Start, List): #This function is to input an array/list from a input start point; len(Start) must equal 2, where Start = [1,1] is cell 1A. List must be a two dimensional array; if you wish to input a single row then this can be done where len(List) == 1, e.g. List = [[1,2,3,4]]
    x = 0 #Sets up a veriable to go through List columns
    y = 0 #Sets up a veriable to go through List rows
    l = 0 #Sets up a veriable to count addional columns against Start[1] to allow for column reset on each new row
    for row in List: #For every row in List
        l = 0 #Set additonal columns to zero
        for cell in row: #For every cell in row
            ws.cell(row=Start[0], column=Start[1]).value = List[y][x] #Set value for current cell
            x = x + 1 #Move to next data input (List) column
            Start[1] = Start[1] + 1 #Move to next Excel column
            l = l + 1 #Count addional row length
        y = y + 1 #Move to next Excel row
        Start[0] = Start[0] + 1 #Move to next Excel row
        x = 0 #Move back to first column of input data (ready for next row)
        Start[1] = Start[1] - l #Reset Excel column back to orignal start column, ready to write next row

test = [[1,2,3,4]]
InputList([7,1], test)

wb.save('New3.xlsx')

答案 6 :(得分:1)

我采用了达拉斯解决方案并增加了对合并单元格的支持:

    def insert_rows(self, row_idx, cnt, above=False, copy_style=True, fill_formulae=True):
        skip_list = []
        try:
            idx = row_idx - 1 if above else row_idx
            for (new, old) in zip(range(self.max_row+cnt,idx+cnt,-1),range(self.max_row,idx,-1)):
                for c_idx in range(1,self.max_column):
                  col = self.cell(row=1, column=c_idx).column #get_column_letter(c_idx)
                  print("Copying %s%d to %s%d."%(col,old,col,new))
                  source = self["%s%d"%(col,old)]
                  target = self["%s%d"%(col,new)]
                  if source.coordinate in skip_list:
                      continue

                  if source.coordinate in self.merged_cells:
                      # This is a merged cell
                      for _range in self.merged_cell_ranges:
                          merged_cells_list = [x for x in cells_from_range(_range)][0]
                          if source.coordinate in merged_cells_list:
                              skip_list = merged_cells_list
                              self.unmerge_cells(_range)
                              new_range = re.sub(str(old),str(new),_range)
                              self.merge_cells(new_range)
                              break

                  if source.data_type == Cell.TYPE_FORMULA:
                    target.value = re.sub(
                      "(\$?[A-Z]{1,3})%d"%(old),
                      lambda m: m.group(1) + str(new),
                      source.value
                    )
                  else:
                    target.value = source.value
                  target.number_format = source.number_format
                  target.font   = source.font.copy()
                  target.alignment = source.alignment.copy()
                  target.border = source.border.copy()
                  target.fill   = source.fill.copy()
            idx = idx + 1
            for row in range(idx,idx+cnt):
                for c_idx in range(1,self.max_column):
                  col = self.cell(row=1, column=c_idx).column #get_column_letter(c_idx)
                  #print("Clearing value in cell %s%d"%(col,row))
                  cell = self["%s%d"%(col,row)]
                  cell.value = None
                  source = self["%s%d"%(col,row-1)]
                  if copy_style:
                    cell.number_format = source.number_format
                    cell.font      = source.font.copy()
                    cell.alignment = source.alignment.copy()
                    cell.border    = source.border.copy()
                    cell.fill      = source.fill.copy()
                  if fill_formulae and source.data_type == Cell.TYPE_FORMULA:
                    #print("Copying formula from cell %s%d to %s%d"%(col,row-1,col,row))
                    cell.value = re.sub(
                      "(\$?[A-Z]{1,3})%d"%(row - 1),
                      lambda m: m.group(1) + str(row),
                      source.value
                    )

答案 7 :(得分:0)

编辑Nick的解决方案,此版本采用起始行,要插入的行数和文件名,并插入必要数量的空行。

#! python 3

import openpyxl, sys

my_start = int(sys.argv[1])
my_rows = int(sys.argv[2])
str_wb = str(sys.argv[3])

wb = openpyxl.load_workbook(str_wb)
old_sheet = wb.get_sheet_by_name('Sheet')
mcol = old_sheet.max_column
mrow = old_sheet.max_row
old_sheet.title = 'Sheet1.5'
wb.create_sheet(index=0, title='Sheet')

new_sheet = wb.get_sheet_by_name('Sheet')

for row_num in range(1, my_start):
    for col_num in range(1, mcol + 1):
        new_sheet.cell(row = row_num, column = col_num).value = old_sheet.cell(row = row_num, column = col_num).value

for row_num in range(my_start + my_rows, mrow + my_rows):
    for col_num in range(1, mcol + 1):
        new_sheet.cell(row = (row_num + my_rows), column = col_num).value = old_sheet.cell(row = row_num, column = col_num).value

wb.save(str_wb)

答案 8 :(得分:0)

这对我有用:

    openpyxl.worksheet.worksheet.Worksheet.insert_rows(wbs,idx=row,amount=2)

在行== idx

之前插入2行

请参阅:http://openpyxl.readthedocs.io/en/stable/api/openpyxl.worksheet.worksheet.html

答案 9 :(得分:0)

要在Python中使用openpyxl将行插入Excel电子表格中

下面的代码可以帮助您:-

import openpyxl

file = "xyz.xlsx"
#loading XL sheet bassed on file name provided by user
book = openpyxl.load_workbook(file)
#opening sheet whose index no is 0
sheet = book.worksheets[0]

#insert_rows(idx, amount=1) Insert row or rows before row==idx, amount will be no of 
#rows you want to add and it's optional
sheet.insert_rows(13)

对于插入列,openpyxl也具有类似的功能,即insert_cols(idx,amount = 1)

答案 10 :(得分:-1)

不幸的是,在文件读取中没有更好的方法,并使用像xlwt这样的库来写出一个新的excel文件(在顶部插入新行)。 Excel不像您可以阅读和附加的数据库那样工作。遗憾的是,您只需要阅读信息并在内存中操作并写出基本上是新文件的内容。