csv2ods 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # Copyright (C) 2008 Agustin Henze -> agustinhenze at gmail.com
  4. #
  5. # This is free software. You may redistribute it under the terms
  6. # of the Apache license and the GNU General Public License Version
  7. # 2 or at your option any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public
  15. # License along with this program; if not, write to the Free Software
  16. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  17. #
  18. # Contributor(s):
  19. #
  20. # Søren Roug
  21. #
  22. # Oct 2014: Georges Khaznadar <georgesk@debian.org>
  23. # - ported to Python3
  24. # - imlemented the missing switch -c / --encoding, with an extra
  25. # feature for POSIX platforms which can guess encoding.
  26. from odf.opendocument import OpenDocumentSpreadsheet
  27. from odf.style import Style, TextProperties, ParagraphProperties, TableColumnProperties
  28. from odf.text import P
  29. from odf.table import Table, TableColumn, TableRow, TableCell
  30. from optparse import OptionParser
  31. import sys,csv,re, os, codecs
  32. if sys.version_info[0]==3: unicode=str
  33. if sys.version_info[0]==2:
  34. class UTF8Recoder:
  35. """
  36. Iterator that reads an encoded stream and reencodes the input to UTF-8
  37. """
  38. def __init__(self, f, encoding):
  39. self.reader = codecs.getreader(encoding)(f)
  40. def __iter__(self):
  41. return self
  42. def next(self):
  43. return self.reader.next().encode("utf-8")
  44. class UnicodeReader:
  45. """
  46. A CSV reader which will iterate over lines in the CSV file "f",
  47. which is encoded in the given encoding.
  48. """
  49. def __init__(self, f, dialect=csv.excel, encoding="utf-8", **kwds):
  50. f = UTF8Recoder(f, encoding)
  51. self.reader = csv.reader(f, dialect=dialect, **kwds)
  52. def next(self):
  53. row = self.reader.next()
  54. return [unicode(s, "utf-8") for s in row]
  55. def __iter__(self):
  56. return self
  57. def csvToOds( pathFileCSV, pathFileODS, tableName='table',
  58. delimiter=',', quoting=csv.QUOTE_MINIMAL,
  59. quotechar = '"', escapechar = None,
  60. skipinitialspace = False, lineterminator = '\r\n',
  61. encoding="utf-8"):
  62. textdoc = OpenDocumentSpreadsheet()
  63. # Create a style for the table content. One we can modify
  64. # later in the word processor.
  65. tablecontents = Style(name="Table Contents", family="paragraph")
  66. tablecontents.addElement(ParagraphProperties(numberlines="false", linenumber="0"))
  67. tablecontents.addElement(TextProperties(fontweight="bold"))
  68. textdoc.styles.addElement(tablecontents)
  69. # Start the table
  70. table = Table( name=tableName )
  71. if sys.version_info[0]==3:
  72. reader = csv.reader(open(pathFileCSV, encoding=encoding),
  73. delimiter=delimiter,
  74. quoting=quoting,
  75. quotechar=quotechar,
  76. escapechar=escapechar,
  77. skipinitialspace=skipinitialspace,
  78. lineterminator=lineterminator)
  79. else:
  80. reader = UnicodeReader(open(pathFileCSV),
  81. encoding=encoding,
  82. delimiter=delimiter,
  83. quoting=quoting,
  84. quotechar=quotechar,
  85. escapechar=escapechar,
  86. skipinitialspace=skipinitialspace,
  87. lineterminator=lineterminator)
  88. fltExp = re.compile('^\s*[-+]?\d+(\.\d+)?\s*$')
  89. for row in reader:
  90. tr = TableRow()
  91. table.addElement(tr)
  92. for val in row:
  93. if fltExp.match(val):
  94. tc = TableCell(valuetype="float", value=val.strip())
  95. else:
  96. tc = TableCell(valuetype="string")
  97. tr.addElement(tc)
  98. p = P(stylename=tablecontents,text=val)
  99. tc.addElement(p)
  100. textdoc.spreadsheet.addElement(table)
  101. textdoc.save( pathFileODS )
  102. if __name__ == "__main__":
  103. usage = "%prog -i file.csv -o file.ods -d"
  104. parser = OptionParser(usage=usage, version="%prog 0.1")
  105. parser.add_option('-i','--input', action='store',
  106. dest='input', help='File input in csv')
  107. parser.add_option('-o','--output', action='store',
  108. dest='output', help='File output in ods')
  109. parser.add_option('-d','--delimiter', action='store',
  110. dest='delimiter', help='specifies a one-character string to use as the field separator. It defaults to ",".')
  111. parser.add_option('-c','--encoding', action='store',
  112. dest='encoding', help='specifies the encoding the file csv. It defaults to utf-8')
  113. parser.add_option('-t','--table', action='store',
  114. dest='tableName', help='The table name in the output file')
  115. parser.add_option('-s','--skipinitialspace',
  116. dest='skipinitialspace', help='''specifies how to interpret whitespace which
  117. immediately follows a delimiter. It defaults to False, which
  118. means that whitespace immediately following a delimiter is part
  119. of the following field.''')
  120. parser.add_option('-l','--lineterminator', action='store',
  121. dest='lineterminator', help='''specifies the character sequence which should
  122. terminate rows.''')
  123. parser.add_option('-q','--quoting', action='store',
  124. dest='quoting', help='''It can take on any of the following module constants:
  125. 0 = QUOTE_MINIMAL means only when required, for example, when a field contains either the quotechar or the delimiter
  126. 1 = QUOTE_ALL means that quotes are always placed around fields.
  127. 2 = QUOTE_NONNUMERIC means that quotes are always placed around fields which do not parse as integers or floating point numbers.
  128. 3 = QUOTE_NONE means that quotes are never placed around fields.
  129. It defaults is QUOTE_MINIMAL''')
  130. parser.add_option('-e','--escapechar', action='store',
  131. dest='escapechar', help='''specifies a one-character string used to escape the delimiter when quoting is set to QUOTE_NONE.''')
  132. parser.add_option('-r','--quotechar', action='store',
  133. dest='quotechar', help='''specifies a one-character string to use as the quoting character. It defaults to ".''')
  134. (options, args) = parser.parse_args()
  135. if options.input:
  136. pathFileCSV = options.input
  137. else:
  138. parser.print_help()
  139. exit( 0 )
  140. if options.output:
  141. pathFileODS = options.output
  142. else:
  143. parser.print_help()
  144. exit( 0 )
  145. if options.delimiter:
  146. delimiter = options.delimiter
  147. else:
  148. delimiter = ","
  149. if options.skipinitialspace:
  150. skipinitialspace = True
  151. else:
  152. skipinitialspace=False
  153. if options.lineterminator:
  154. lineterminator = options.lineterminator
  155. else:
  156. lineterminator ="\r\n"
  157. if options.escapechar:
  158. escapechar = options.escapechar
  159. else:
  160. escapechar=None
  161. if options.tableName:
  162. tableName = options.tableName
  163. else:
  164. tableName = "table"
  165. if options.quotechar:
  166. quotechar = options.quotechar
  167. else:
  168. quotechar = "\""
  169. encoding = "utf-8" # default setting
  170. ###########################################################
  171. ## try to guess the encoding; this is implemented only with
  172. ## POSIX platforms. Can it be improved?
  173. output = os.popen('/usr/bin/file ' + pathFileCSV).read()
  174. m=re.match(r'^.*: ([-a-zA-Z0-9]+) text$', output)
  175. if m:
  176. encoding=m.group(1)
  177. if 'ISO-8859' in encoding:
  178. encoding="latin-1"
  179. else:
  180. encoding="utf-8"
  181. ############################################################
  182. # when the -c or --coding switch is used, it takes precedence
  183. if options.encoding:
  184. encoding = options.encoding
  185. csvToOds( pathFileCSV=unicode(pathFileCSV),
  186. pathFileODS=unicode(pathFileODS),
  187. delimiter=delimiter, skipinitialspace=skipinitialspace,
  188. escapechar=escapechar,
  189. lineterminator=unicode(lineterminator),
  190. tableName=tableName, quotechar=quotechar,
  191. encoding=encoding)
  192. # Local Variables: ***
  193. # mode: python ***
  194. # End: ***