Utils.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. # see the xlwt.license module for details of licensing.
  2. # Utilities for work with reference to cells and with sheetnames
  3. import re
  4. from .ExcelMagic import MAX_ROW, MAX_COL
  5. from .compat import xrange
  6. _re_cell_ex = re.compile(r"(\$?)([A-I]?[A-Z])(\$?)(\d+)", re.IGNORECASE)
  7. _re_row_range = re.compile(r"\$?(\d+):\$?(\d+)")
  8. _re_col_range = re.compile(r"\$?([A-I]?[A-Z]):\$?([A-I]?[A-Z])", re.IGNORECASE)
  9. _re_cell_range = re.compile(r"\$?([A-I]?[A-Z]\$?\d+):\$?([A-I]?[A-Z]\$?\d+)", re.IGNORECASE)
  10. _re_cell_ref = re.compile(r"\$?([A-I]?[A-Z]\$?\d+)", re.IGNORECASE)
  11. def col_by_name(colname):
  12. """'A' -> 0, 'Z' -> 25, 'AA' -> 26, etc
  13. """
  14. col = 0
  15. power = 1
  16. for i in xrange(len(colname)-1, -1, -1):
  17. ch = colname[i]
  18. col += (ord(ch) - ord('A') + 1) * power
  19. power *= 26
  20. return col - 1
  21. def cell_to_rowcol(cell):
  22. """Convert an Excel cell reference string in A1 notation
  23. to numeric row/col notation.
  24. Returns: row, col, row_abs, col_abs
  25. """
  26. m = _re_cell_ex.match(cell)
  27. if not m:
  28. raise Exception("Ill-formed single_cell reference: %s" % cell)
  29. col_abs, col, row_abs, row = m.groups()
  30. row_abs = bool(row_abs)
  31. col_abs = bool(col_abs)
  32. row = int(row) - 1
  33. col = col_by_name(col.upper())
  34. return row, col, row_abs, col_abs
  35. def cell_to_rowcol2(cell):
  36. """Convert an Excel cell reference string in A1 notation
  37. to numeric row/col notation.
  38. Returns: row, col
  39. """
  40. m = _re_cell_ex.match(cell)
  41. if not m:
  42. raise Exception("Error in cell format")
  43. col_abs, col, row_abs, row = m.groups()
  44. # Convert base26 column string to number
  45. # All your Base are belong to us.
  46. row = int(row) - 1
  47. col = col_by_name(col.upper())
  48. return row, col
  49. def rowcol_to_cell(row, col, row_abs=False, col_abs=False):
  50. """Convert numeric row/col notation to an Excel cell reference string in
  51. A1 notation.
  52. """
  53. assert 0 <= row < MAX_ROW # MAX_ROW counts from 1
  54. assert 0 <= col < MAX_COL # MAX_COL counts from 1
  55. d = col // 26
  56. m = col % 26
  57. chr1 = "" # Most significant character in AA1
  58. if row_abs:
  59. row_abs = '$'
  60. else:
  61. row_abs = ''
  62. if col_abs:
  63. col_abs = '$'
  64. else:
  65. col_abs = ''
  66. if d > 0:
  67. chr1 = chr(ord('A') + d - 1)
  68. chr2 = chr(ord('A') + m)
  69. # Zero index to 1-index
  70. return col_abs + chr1 + chr2 + row_abs + str(row + 1)
  71. def rowcol_pair_to_cellrange(row1, col1, row2, col2,
  72. row1_abs=False, col1_abs=False, row2_abs=False, col2_abs=False):
  73. """Convert two (row,column) pairs
  74. into a cell range string in A1:B2 notation.
  75. Returns: cell range string
  76. """
  77. assert row1 <= row2
  78. assert col1 <= col2
  79. return (
  80. rowcol_to_cell(row1, col1, row1_abs, col1_abs)
  81. + ":"
  82. + rowcol_to_cell(row2, col2, row2_abs, col2_abs)
  83. )
  84. def cellrange_to_rowcol_pair(cellrange):
  85. """Convert cell range string in A1 notation to numeric row/col
  86. pair.
  87. Returns: row1, col1, row2, col2
  88. """
  89. cellrange = cellrange.upper()
  90. # Convert a row range: '1:3'
  91. res = _re_row_range.match(cellrange)
  92. if res:
  93. row1 = int(res.group(1)) - 1
  94. col1 = 0
  95. row2 = int(res.group(2)) - 1
  96. col2 = -1
  97. return row1, col1, row2, col2
  98. # Convert a column range: 'A:A' or 'B:G'.
  99. # A range such as A:A is equivalent to A1:A16384, so add rows as required
  100. res = _re_col_range.match(cellrange)
  101. if res:
  102. col1 = col_by_name(res.group(1).upper())
  103. row1 = 0
  104. col2 = col_by_name(res.group(2).upper())
  105. row2 = -1
  106. return row1, col1, row2, col2
  107. # Convert a cell range: 'A1:B7'
  108. res = _re_cell_range.match(cellrange)
  109. if res:
  110. row1, col1 = cell_to_rowcol2(res.group(1))
  111. row2, col2 = cell_to_rowcol2(res.group(2))
  112. return row1, col1, row2, col2
  113. # Convert a cell reference: 'A1' or 'AD2000'
  114. res = _re_cell_ref.match(cellrange)
  115. if res:
  116. row1, col1 = cell_to_rowcol2(res.group(1))
  117. return row1, col1, row1, col1
  118. raise Exception("Unknown cell reference %s" % (cellrange))
  119. def cell_to_packed_rowcol(cell):
  120. """ pack row and column into the required 4 byte format """
  121. row, col, row_abs, col_abs = cell_to_rowcol(cell)
  122. if col >= MAX_COL:
  123. raise Exception("Column %s greater than IV in formula" % cell)
  124. if row >= MAX_ROW: # this for BIFF8. for BIFF7 available 2^14
  125. raise Exception("Row %s greater than %d in formula" % (cell, MAX_ROW))
  126. col |= int(not row_abs) << 15
  127. col |= int(not col_abs) << 14
  128. return row, col
  129. # === sheetname functions ===
  130. def valid_sheet_name(sheet_name):
  131. if sheet_name == u"" or sheet_name[0] == u"'" or len(sheet_name) > 31:
  132. return False
  133. for c in sheet_name:
  134. if c in u"[]:\\?/*\x00":
  135. return False
  136. return True
  137. def quote_sheet_name(unquoted_sheet_name):
  138. if not valid_sheet_name(unquoted_sheet_name):
  139. raise Exception(
  140. 'attempt to quote an invalid worksheet name %r' % unquoted_sheet_name)
  141. return u"'" + unquoted_sheet_name.replace(u"'", u"''") + u"'"