在Apple Swift中parsingExcel数据

我目前的工作stream程涉及使用Applescript实质上分隔Excel数据并将其格式化为纯文本文件。 我们正在推动所有的Swift环境,但是我还没有find任何parsing我的Excel数据到Swift的工具包。

我能想到的唯一的事情就是用C或者什么东西来包装它,但是这并不理想。 任何更好的build议来parsing这个数据在Swift中使用?

目标是消除Applescript,但我不确定这是否仍然可以与Excel文件进​​行交互。 通过Applescript脚本编写Excel似乎是唯一的方法。

编辑:我没有从这个工作stream程中消除Excel的选项。 这就是数据将如何到达应用程序,因此我必须包括它。

能够简化parsing这些数据的过程,然后处理它将是至关重要的。 我知道Applescript在过去一直很好,帮助我处理它; 不过,对我来说,这太过于封闭了。

我一直在寻找在Swift / Cocoa中编写的东西,但是这仍然可能需要用一个Applescript来提取数据,对吧?

推动Swift的一大优点是可读性。 我不太了解Objective-C,而且我觉得,迅捷将是一个更容易的过渡。

我在PC上的工作stream程一直使用COM对象,正如前面所说的,在Mac Excel应用程序中不可用。 我现在只是在寻找数据提取。 一些以前的应用程序在应用程序内进行处理,但我正在寻找这个非常独立的,因此在我正在开发的应用程序内的所有处理。 从.XLS或.XLSX文件中提取数据后,我将通过RegEx进行一些文本编辑,也许还会进行一些数字处理。 没什么太疯狂的 到目前为止,它将在客户端运行,但我期待将其扩展到服务器进程。

在Mac OS X 10.6 Snow Leopard中,Apple引入了AppleScriptObjC框架,这使得Cocoa和AppleScript之间的交互变得非常容易。 AppleScript代码和Objective-C类似的语法可以在同一个源文件中使用。 这比Scripting BridgeNSAppleScript更方便。

AppleScriptObjC不能在Swift中直接使用,因为NSBundle的命令loadAppleScriptObjectiveCScripts没有桥接到Swift。

但是,您可以使用Objective-C桥接类

ASObjC.h

 @import Foundation; @import AppleScriptObjC; @interface NSObject (Excel) - (void)openExcelDocument:(NSString *)filePath; - (NSArray *)valueOfUsedRange; @end @interface ASObjC : NSObject + (ASObjC *)sharedASObjC; @property id Excel; @end 

ASObjC.m

 #import "ASObjC.h" @implementation ASObjC + (void)initialize { if (self == [ASObjC class]) { [[NSBundle mainBundle] loadAppleScriptObjectiveCScripts]; } } + (ASObjC *)sharedASObjC { static id sharedInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedInstance = [[ASObjC alloc] init]; }); return sharedInstance; } - (instancetype)init { self = [super init]; if (self) { _Excel = NSClassFromString(@"ASExcel"); } return self; } @end 

从AppleScriptObjC模板创build一个AppleScript源文件

ASExcel.applescript

 script ASExcel property parent: class "NSObject" on openExcelDocument:filePath set asFilePath to filePath as text tell application "Microsoft Excel" set sourceBook to open workbook workbook file name asFilePath repeat try get workbooks return end try delay 0.5 end repeat end tell end openDocument on valueOfUsedRange() tell application "Microsoft Excel" tell active sheet set activeRange to used range return value of activeRange end tell end tell end valueOfUsedRange end script 

必要时链接到AppleScriptObjC框架。
创build桥接头并导入ASObjC.h

然后你可以用Swift调用AppleScriptObjC

  ASObjC.sharedASObjC().Excel.openExcelDocument("Macintosh HD:Users:MyUser:Path:To:ExcelFile.xlsx") 

要么

 let excelData = ASObjC.sharedASObjC().Excel.valueOfUsedRange() as! Array<[String]> 

如果你试图消除Excel作为依赖(这不是没有道理:花钱而不是每个人都有)或者AppleScript作为一种语言(完全可以理解,但是作为苹果的应用程序自动化的替代品吸)。

有第三方的Excelparsing库可用于其他语言,例如,我已经在我自己的项目中成功地使用了Python的openpyxl (用于.xlsx文件)和xlrd (用于.xsl)库。 我通过Google的魔术师看到有人写了一个ObjC框架, DHlibxls ,它可以直接从Swift中使用,但是我自己并没有使用它,所以不能再告诉你什么。

您可以使用ScriptingBridge或NSAppleScript与Apple Scriptable的东西进行交互

ScriptingBridge可以从Apple脚本字典中生成一个头文件。

NSAppleScript可以通过传递一个String来为你执行任何AppleScript

1.导出为纯文本CSV

如果你所要做的就是从Excel中提取数据以供其他地方使用,而不是捕获Excel公式和格式,那么你可能不应该尝试读取.xls文件。 XLS是一种复杂的格式。 这对Excel来说是好事,而不是一般的数据交换。

同样,如果您只想将数据保存为纯文本,则您可能不需要使用AppleScript或其他任何软件与Excel集成。 Excel已经知道如何将数据保存为纯文本。 只需使用Excel的“另存为”命令。 (这就是Mac上所称的,我不知道PC。)

现在的问题是使用什么明文格式。 一个明显的select是明文逗号分隔值文件(CSV),因为它是一个简单的事实标准(与XML这种复杂的官方标准相对)。 这将使得使用Swift或任何其他语言都容易。

2.如果可能,以UTF-8编码导出,否则以UTF-16导出

那么你怎么做到这一点? 明文非常简单,但需要跟踪的一个细节是文本编码 。 文本编码是在明文文件中表示字符的一种方式。 不幸的是,你不能通过检查文件来可靠地告诉文件的编码,所以当你保存文件时你需要select一个编码,并且在你阅读时要记住使用这个编码。 如果你搞砸了,重音字符,印刷商的引号,破折号和其他非ASCII字符将会被破坏。 那么你应该使用哪种文本编码? 简而言之, 如果可能的话 ,您应该始终使用UTF-8

但是,如果您使用的是旧版本的Excel,那么您可能无法使用UTF-8。 在这种情况下,你应该使用UTF-16。 特别是,我相信UTF-16是Excel 2011 for Mac中的唯一导出选项,它产生了一个可预测的结果,不会以令人惊讶的方式在难懂的语言环境设置或Microsoft特定的编码上依赖。

因此,如果您使用的是Excel 2011 for Mac,请从Excel的“另存为”命令中select“UTF-16 Unicode文本”。

这将导致Excel保存该文件,以便每行都是一行文本,并且每个列由制表符分隔。 (所以在技术上,这是一个制表符分隔的值文件,而不是逗号分隔的值文件。)

3.用Swift导入

现在你有一个纯文本文件,你知道它是以UTF-8(或UTF-16)编码保存的。 所以现在你可以阅读它并在Swift中parsing它。

如果您的Excel数据比较复杂,则可能需要全function的CSVparsing器。 最好的select可能是CHCSVParser

使用CHCSV,您可以使用以下代码parsing文件:

 NSURL * const inputFileURL = [NSURL fileURLWithPath:@"/path/to/exported/file.txt"]; unichar tabCharacter = '\t'; NSArray *rows = [NSArray arrayWithContentsOfCSVFile:inputFilePath options:CHCSVParserOptionsSanitizesFields delimiter:tabCharacter]; 

(当然,你也可以从Swift中调用它。)

另一方面,如果数据相对简单(例如,它没有转义字符),则根本不需要使用外部库。 您可以编写一些Swift代码来parsing制表符分隔的值,只需将文件读入string,在换行符上分割,然后在选项卡上分割

该函数将接收表示TSV数据的String并返回一个字典数组:

 /** Reads a multiline, tab-separated String and returns an Array<NSictionary>, taking column names from the first line or an explicit parameter */ func JSONObjectFromTSV(tsvInputString:String, columnNames optionalColumnNames:[String]? = nil) -> Array<NSDictionary> { let lines = tsvInputString.componentsSeparatedByString("\n") guard lines.isEmpty == false else { return [] } let columnNames = optionalColumnNames ?? lines[0].componentsSeparatedByString("\t") var lineIndex = (optionalColumnNames != nil) ? 0 : 1 let columnCount = columnNames.count var result = Array<NSDictionary>() for line in lines[lineIndex ..< lines.count] { let fieldValues = line.componentsSeparatedByString("\t") if fieldValues.count != columnCount { // NSLog("WARNING: header has %u columns but line %u has %u columns. Ignoring this line", columnCount, lineIndex,fieldValues.count) } else { result.append(NSDictionary(objects: fieldValues, forKeys: columnNames)) } lineIndex = lineIndex + 1 } return result } 

所以你只需要将文件读入一个string并传递给这个函数。 这个片段来自tsv-to-json转换器的要点 。 如果您需要了解更多关于Microsoft产品生成的文本编码以及Cocoa可以自动检测的文本编码 ,那么这个文本编码回购包含了对出口标本的研究,这导致了UTF-16的发展方向对于Mac上的旧Microsoft产品。

(我知道我在这里链接到我自己的回购,道歉?)