内容简介:在垃圾短信过滤应用在官方文档 “也就是不能直接导入 C++ 代码,但是可以使用 Objective-C 或者 C 对 C++ 进行封装。所以项目中使用 Objective-C 做封装,然后在 Swift 中调用,下面就是这个过程的实践,Demo 代码见
在垃圾短信过滤应用 SMSFilters 中,需要使用 Jieba 分词库来対短信进行分词,然后使用 TF-IDF 来进行处理` 分词库是 C++ 写的,这就意味着需要在Swift中集成 C++ 库。
在官方文档 “ Using Swift with Cocoa and Objective-C “ 中,Apple只是介绍了怎么将 Swift 代码跟 Objective-C 代码做整合,但是没有提C++,后来在官方文档中看到了这样一段话:
You cannot import C++ code directly into Swift. Instead, create an Objective-C or C wrapper for C++ code.
也就是不能直接导入 C++ 代码,但是可以使用 Objective-C 或者 C 对 C++ 进行封装。所以项目中使用 Objective-C 做封装,然后在 Swift 中调用,下面就是这个过程的实践,Demo 代码见 SwiftJiebaDemo 。
整合过程
分成三步:
- 引入C++文件;
- 用 Objective-C 封装;
- 在 Swift 中 调用 Objective-C;
引入C++文件
Demo中使用的是”结巴”中文分词的 C++ 版本 yanyiwu/cppjieba 。将其中的 include/cppjieba 和依赖 limonp 合并,并加入 dict 中的 hmm_model 和 jiaba.dict 作为基础数据,并暴露 JiebaInit 和 JiebaCut 接口:
//
// Segmentor.cpp
// iosjieba
//
// Created by yanyiwu on 14/12/24.
// Copyright (c) 2014年 yanyiwu. All rights reserved.
//
#include "Segmentor.h"
#include <iostream>
using namespace cppjieba;
cppjieba::MixSegment * globalSegmentor;
void JiebaInit(const string& dictPath, const string& hmmPath, const string& userDictPath)
{
if(globalSegmentor == NULL) {
globalSegmentor = new MixSegment(dictPath, hmmPath, userDictPath);
}
cout << __FILE__ << __LINE__ << endl;
}
void JiebaCut(const string& sentence, vector<string>& words)
{
assert(globalSegmentor);
globalSegmentor->Cut(sentence, words);
cout << __FILE__ << __LINE__ << endl;
cout << words << endl;
}
以及
// // Segmentor.h // iosjieba // // Created by yanyiwu on 14/12/24. // Copyright (c) 2014年 yanyiwu. All rights reserved. // #ifndef __iosjieba__Segmentor__ #define __iosjieba__Segmentor__ #include <stdio.h> #include "cppjieba/MixSegment.hpp" #include <string> #include <vector> extern cppjieba::MixSegment * globalSegmentor; void JiebaInit(const std::string& dictPath, const std::string& hmmPath, const std::string& userDictPath); void JiebaCut(const std::string& sentence, std::vector<std::string>& words); #endif /* defined(__iosjieba__Segmentor__) */
目录如下:
$ tree iosjieba
iosjieba
├── Segmentor.cpp
├── Segmentor.h
├── cppjieba
│ ├── DictTrie.hpp
│ ├── FullSegment.hpp
│ ├── HMMModel.hpp
│ ├── HMMSegment.hpp
│ ├── Jieba.hpp
│ ├── KeywordExtractor.hpp
│ ├── MPSegment.hpp
│ ├── MixSegment.hpp
│ ├── PosTagger.hpp
│ ├── PreFilter.hpp
│ ├── QuerySegment.hpp
│ ├── SegmentBase.hpp
│ ├── SegmentTagged.hpp
│ ├── TextRankExtractor.hpp
│ ├── Trie.hpp
│ ├── Unicode.hpp
│ └── limonp
│ ├── ArgvContext.hpp
│ ├── BlockingQueue.hpp
│ ├── BoundedBlockingQueue.hpp
│ ├── BoundedQueue.hpp
│ ├── Closure.hpp
│ ├── Colors.hpp
│ ├── Condition.hpp
│ ├── Config.hpp
│ ├── FileLock.hpp
│ ├── ForcePublic.hpp
│ ├── LocalVector.hpp
│ ├── Logging.hpp
│ ├── Md5.hpp
│ ├── MutexLock.hpp
│ ├── NonCopyable.hpp
│ ├── StdExtension.hpp
│ ├── StringUtil.hpp
│ ├── Thread.hpp
│ └── ThreadPool.hpp
└── iosjieba.bundle
└── dict
├── hmm_model.utf8
├── jieba.dict.small.utf8
└── user.dict.utf8
接下来开始在项目中集成。首先创建一个空项目 iOSJiebaDemo ,将 iosjieba 加入项目中。
| 单页应用 | SwiftJiebaDemo | 添加 SwiftJiebaDemo |
|---|---|---|
| |
|
|
添加 iosjieba:
见代码: https://github.com/qiwihui/SwiftJiebaDemo/commit/caeb6c2f9fb005a9bc518ee67890814481676807
C++ 到 Objective-C 封装
这个过程是将 C++ 的接口进行 Objective-C 封装,向 Swift 暴露。这个封装只暴露了 objcJiebaInit 和 objcJiebaCut 两个接口。
// // iosjiebaWrapper.h // SMSFilters // // Created by Qiwihui on 1/14/19. // Copyright © 2019 qiwihui. All rights reserved. // #import <Foundation/Foundation.h> @interface JiebaWrapper : NSObject - (void) objcJiebaInit: (NSString *) dictPath forPath: (NSString *) hmmPath forDictPath: (NSString *) userDictPath; - (void) objcJiebaCut: (NSString *) sentence toWords: (NSMutableArray *) words; @end
//
// iosjiebaWrapper.mm
// iOSJiebaTest
//
// Created by Qiwihui on 1/14/19.
// Copyright © 2019 Qiwihui. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "iosjiebaWrapper.h"
#include "Segmentor.h"
@implementation JiebaWrapper
- (void) objcJiebaInit: (NSString *) dictPath forPath: (NSString *) hmmPath forDictPath: (NSString *) userDictPath {
const char *cDictPath = [dictPath UTF8String];
const char *cHmmPath = [hmmPath UTF8String];
const char *cUserDictPath = [userDictPath UTF8String];
JiebaInit(cDictPath, cHmmPath, cUserDictPath);
}
- (void) objcJiebaCut: (NSString *) sentence toWords: (NSMutableArray *) words {
const char* cSentence = [sentence UTF8String];
std::vector<std::string> wordsList;
for (int i = 0; i < [words count];i++)
{
wordsList.push_back(wordsList[i]);
}
JiebaCut(cSentence, wordsList);
[words removeAllObjects];
std::for_each(wordsList.begin(), wordsList.end(), [&words](std::string str) {
id nsstr = [NSString stringWithUTF8String:str.c_str()];
[words addObject:nsstr];
});
}
@end
见代码: https://github.com/qiwihui/SwiftJiebaDemo/commit/7d196bb2c33280a4f419be21b47961a521618221
Objective-C 到 Swift
在 Swift 中调用 Objecttive-C 的接口,这个在官方文档和许多博客中都有详细介绍。
- 加入
{project_name}-Bridging-Header.h头文件,即SwiftJiebaDemo_Bridging_Header_h,引入之前封装的头文件,并在Targets -> Build Settings -> Objective-C Bridging Header中设置头文件路径SwiftJiebaDemo/SwiftJiebaDemo_Bridging_Header_h。
// // SwiftJiebaDemo-Bridging-Header.h // SwiftJiebaDemo // // Created by Qiwihui on 1/15/19. // Copyright © 2019 Qiwihui. All rights reserved. // #ifndef SwiftJiebaDemo_Bridging_Header_h #define SwiftJiebaDemo_Bridging_Header_h #import "iosjiebaWrapper.h" #endif /* SwiftJiebaDemo_Bridging_Header_h */
- 将使用到 C++ 的 Objective-C 文件修改为 Objective-C++ 文件,即 将
.m改为.mm:iosjiebaWrapper.m改为iosjiebaWrapper.mm。
见代码: https://github.com/qiwihui/SwiftJiebaDemo/commit/94852b1357b0a0a4b2e8b92384fbdb1b16c80ed8
使用
使用时需要先初始化 Jiaba 分词,然后再进行分词。
class Classifier {
init() {
let dictPath = Bundle.main.resourcePath!+"/iosjieba.bundle/dict/jieba.dict.small.utf8"
let hmmPath = Bundle.main.resourcePath!+"/iosjieba.bundle/dict/hmm_model.utf8"
let userDictPath = Bundle.main.resourcePath!+"/iosjieba.bundle/dict/user.dict.utf8"
JiebaWrapper().objcJiebaInit(dictPath, forPath: hmmPath, forDictPath: userDictPath);
}
func tokenize(_ message:String) -> [String] {
print("tokenize...")
let words = NSMutableArray()
JiebaWrapper().objcJiebaCut(message, toWords: words)
return words as! [String]
}
}
控制台输出结果:
可以看到,测试用例 小明硕士毕业于中国科学院计算所,后在日本京都大学深造 经过分词后为
〔拼音〕["小明", "硕士", "毕业", "于", "中国科学院", "计算所", ",", "后", "在", "日本", "京都大学", "深造"] ,完成集成。
见代码: https://github.com/qiwihui/SwiftJiebaDemo/commit/bc42e1312dff6a9f7171cc69403136bc8a82204c
遇到的问题
由于自己对于编译链接原理不了解,以及是 iOS 开发初学,因此上面的这个过程中遇到了很多问题,耗时两周才解决,故将遇到的一些问题记录于此,以便日后。
-
"cassert" file not found
将 .m 改为 .mm 即可。
-
compiler not finding <tr1/unordered_map>
设置 C++ Standard Library 为 LLVM libc++
参考: mac c++ compiler not finding <tr1/unordered_map>
-
warning: include path for stdlibc++ headers not found; pass '-std=libc++' on the command line to use the libc++ standard library instead [-Wstdlibcxx-not-found]
Build Setting -> C++ Standard Library -> libstdc++ 修改为 Build Setting -> C++ Standard Library -> libc++
-
use of unresolved identifier
这个问题在于向项目中加入文件时, Target Membership 设置不正确导致。需要将对于使用到的 Target 都勾上。
相关参考: Understanding The “Use of Unresolved Identifier” Error In Xcode
参考
- SwiftArchitect 对问题 “Can I have Swift, Objective-C, C and C++ files in the same Xcode project?
” 的 回答 - SwiftArchitect 对问题 “Can I mix Swift with C++? Like the Objective - C .mm files” 的 回答
- 在Swift代码中整合C++类库
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 基于海量公司分词ES中文分词插件
- 北大开源全新中文分词工具包:准确率远超THULAC、结巴分词
- 复旦大学提出中文分词新方法,Transformer连有歧义的分词也能学
- 分词,难在哪里?
- 隐马尔可夫分词
- 【NLP】分词算法综述
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Mobilizing Web Sites
Layon, Kristofer / 2011-12 / 266.00元
Everyone has been talking about the mobile web in recent years, and more of us are browsing the web on smartphones and similar devices than ever before. But most of what we are viewing has not yet bee......一起来看看 《Mobilizing Web Sites》 这本书的介绍吧!