概述:MacOS10.14,使用freetds库的C语言代码连接sql server远程数据库,插入中文数据,编码不匹配。
freetds库官网http://www.freetds.org,下载freetds库=freetds-0.91.tar.gz
进入目录usr/local下新建文件夹用于存放freetds。
解压安装包,在解压后的目录里改配置
root# ./configure --prefix=/usr/local/freetds --enable-msdblib --with-tdsver=7.1
在解压后的目录里安装
make
然后
make install
,查看配置文件如下
sudo vi /usr/local/freetds/etc/freetds.conf
正文:
[global]
# TDS protocol version
; tds version = 4.2
# Whether to write a TDSDUMP file for diagnostic purposes
# (setting this to /tmp is insecure on a multi-user system)
; dump file = /tmp/freetds.log
; debug flags = 0xffff
# Command and connection timeouts
; timeout = 10
; connect timeout = 10
# If you get out-of-memory errors, it may mean that your client
# is trying to allocate a huge buffer for a TEXT field.
# Try setting 'text size' to a more reasonable limit
text size = 64512
#client charest = GBK
# A typical Sybase server
[egServer50]
host = symachine.domain.com
port = 5000
tds version = 5.0
# A typical Microsoft server
[egServer70]
host = ntmachine.domain.com
port = 1433
tds version = 7.0
#client charest = GREEK
[v70]
host = cd.vtede.com
port = 1433
tds version = 7.0
# charset = ISO-8859-1
———————————————————————————
数据库表:
create table Temp(
id bigint not null,
flag bit,
ChatMsg ntext,
ChatTime datetime,
IpAddress varchar(50),
UserName nVarchar(50),
);
数据库排序规则:
使用sql语句查询
SELECT
name,
collation_name
FROM sys.databases
WHERE name = N’interview0710’;
查询结果:
interview0710,SQL_Latin1_General_CP1_CI_AS
使用sql语句
SELECT SERVERPROPERTY(N'Collation’)
查询结果:
1
———————————————————————————
终端使用tsql连接远程sql server 数据库,已知该数据库地址,端口号,一个可登陆的用户名和对应密码
正常插入:
在终端输入命令tsql -H xxx.com -p 1433 -U username -P password
(1433是该远程数据库默认端口号)
连接到数据库
locale is "C/UTF-8/C/C/C/C"
locale charset is "UTF-8"
using default charset "UTF-8"
1>
结果说明freetds正常连接到sql server,然后使用sql语句插入数据
1> insert into [interview0710].[dbo].[Temp](id,flag,ChatMsg,ChatTime,IpAddress,UserName) values(139,NULL,N'中文','2018-12-03 08:47:52','192.168.1.212',N'中文')
2> go
在数据库中查看结果
139,NULL,中文,2018-12-03 08:47:52:000,192.168.1.212,中文
结果正确。
———————————————————————————
在Xcode中新建工程,
TARGETS-BuildSettings添加头文件路径
HEADER_SEARCH_PATHS = /Users/wc.zeng/Downloads/freetds-0.91\(jb51.net\)/freetds-0.91/include
添加库文件路径
LIBRARY_SEARCH_PATHS = /usr/local/lib /Users/wc.zeng/Downloads/freetds-0.91\(jb51.net\)/freetds-0.91/src/dblib
添加静态库连接
OTHER_LDFLAGS = -l sybdb
freetds库的API文档https://sites.ualberta.ca/dept/aict/uts/software/openbsd/ports/4.6/i386/obj/freetds-0.82.20110223/freetds-0.82/doc/reference/index.html
代码参考网友博客
https://www.cnblogs.com/fnlingnzb-learner/p/5970556.html
在AppDelegate.m文件中
需要包含头文件和定义一些宏
#include
#import
#import
#import
#import
#import /* sqlfront.h always comes first */
#import "sybdb.h" /* sybdb.h is the only other file you need */
#define SQLDBIP "xxx.com" //SQL数据库服务器IP
#define SQLDBPORT "1433" //SQL数据库服务器端口
#define SQLDBNAME "username" //SQL数据库服务器数据库名
#define SQLDBUSER "username" //SQL数据库服务器数据库用户名
#define SQLDBPASSWD "password" //SQL数据库服务器用户密码
#define SQLDBSERVER SQLDBIP":"SQLDBPORT
为一个按钮添加动作,使它可以连接sql server数据库并写入数据
在-(IBAction)Send:(id)sender里添加以下C代码
-(IBAction)Send:(id)sender
{
//写入远程数据库
int i, ch;
LOGINREC *login;//描述客户端的结构体,在连接时被传递到服务器.
DBPROCESS *dbproc;
RETCODE erc; //库函数中最普遍的返回类型.
NSString *sendchatmsg=[sendmessage stringValue];//从插座变量里获取字符串
NSString *myip=[ip_address stringValue];
/*************************************************************/
//在开始调用本库函数前常常要先调用dbinit()函数
if (dbinit() == FAIL) {
exit(1);
}
//dblogin()函数申请 LOGINREC 结构体,此结构体被传递给dbopen()函数,用来创建一个连接。
//虽然基本上不会调用失败,但是检查它!
if ((login = dblogin()) == NULL) {
exit(1);
}
//LOGINREC结构体不能被直接访问,要通过以下宏设置,下面设置两个必不可少的域
DBSETLUSER(login, SQLDBUSER);
DBSETLPWD(login, SQLDBPASSWD);
/*************************************************************/
//dbopen()与服务器建立一个连接. 传递 LOGINREC 指针和服务器名字
if ((dbproc = dbopen(login, SQLDBSERVER)) == NULL) {
exit(1);
}
// 可以调用dbuser()函数选择我们使用的数据库名,可以省略,省略后使用用户默认数据库.
if (SQLDBNAME && (erc = dbuse(dbproc, SQLDBNAME)) == FAIL) {
exit(1);
}
//获取当前时间
NSDate *datenow = [NSDate date];//现在时间
NSString *timeSp = [NSString stringWithFormat:@"%d", (long)[datenow timeIntervalSince1970]];
//NSLog(@"timeSp:%@",timeSp); //时间戳的值时间戳转时间的方法
NSDateFormatter * dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
NSString*strDate = [dateFormatter stringFromDate:[NSDate date]];
//NSLog(@"%@",strDate);
NSStringEncoding enc=CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingISOLatin1);
//NSString转换成ISOLatin1编码
//sendchatmsg=[NSString stringWithCString:[sendchatmsg UTF8String] encoding:enc];//能把NSString转换成IOSLatin1编码格式
NSLog(@"sendchatmsg:%@",sendchatmsg);
//拼接sql插入语句
char str1[256];
strcpy(str1,"insert into [interview0710].[dbo].[Temp](id,flag,ChatMsg,ChatTime,IpAddress,UserName) values(3,NULL,N'");
//strcat(str1, [sendchatmsg cStringUsingEncoding:NSUTF8StringEncoding]);
strcat(str1, [sendchatmsg cStringUsingEncoding:NSUTF8StringEncoding]);
printf("now str1=%s\n",str1);
strcat(str1,"','");
strcat(str1, [strDate cStringUsingEncoding:NSUTF8StringEncoding]);
strcat(str1,"','");
strcat(str1, [myip cStringUsingEncoding:NSUTF8StringEncoding]);
strcat(str1,"',N'");
strcat(str1,[sendchatmsg cStringUsingEncoding:NSUTF8StringEncoding]);
strcat(str1,"')");
printf("final str1=%s\n",str1);
const char *resultCString = strcat(str1,"");
/*
if ([stringOBJ canBeConvertedToEncoding:NSUTF8StringEncoding]) {
resultCString = [stringOBJ cStringUsingEncoding:NSUTF8StringEncoding];
}*/
printf("c string is:%s",resultCString);
//[testrecv setString:stringOBJ];//textfield cell显示聊天记录失败
dbcmd(dbproc,resultCString);//装载sql语句
printf("\n");
if ((dbsqlexec(dbproc)) == FAIL) {
exit(1); //等待服务器执行SQL语句,等待时间取决于查询的复杂度。
}
//在调用dbsqlexec()、dbsqlok()、dbrpcsend()返回成功之后调用dbresults()函数
printf("then fetch results:\n");
int count = 0;
while ((erc = dbresults(dbproc)) != NO_MORE_RESULTS) {
struct col { //保存列的所有信息
char *name; //列名字
char *buffer; //存放列数据指针
int type, size, status;
} *columns, *pcol;
int ncols;
int row_code;
if (erc == FAIL) {
exit(1);
}
ncols = dbnumcols(dbproc);//返回执行结果的列数目
if ((columns = calloc(ncols, sizeof(struct col))) == NULL) {
perror(NULL);
exit(1);
}
/* read metadata and bind. */
for (pcol = columns;pcol - columns < ncols;pcol++) {
int c = (int)(pcol - columns + 1);
pcol->name = dbcolname(dbproc, c); //返回指定列的列名
pcol->type = dbcoltype(dbproc, c);
pcol->size = dbcollen(dbproc, c);
printf("%*s(%d)", 20, pcol->name, pcol->size);
if ((pcol->buffer = calloc(1, 20)) == NULL) {
perror(NULL);
exit(1);
}
erc = dbbind(dbproc, c, NTBSTRINGBIND, 20, (BYTE*)pcol->buffer);
if (erc == FAIL) {
exit(1);
}
erc = dbnullbind(dbproc, c, &pcol->status); //(5)
if (erc == FAIL) {
exit(1);
}
}
printf("\n");
/* 打印数据 */
while ((row_code = dbnextrow(dbproc)) != NO_MORE_ROWS) {//读取行数据
switch (row_code) {
case REG_ROW:
for (pcol = columns; pcol - columns < ncols; pcol++) {
char *buffer = pcol->status == -1 ?
"null" : pcol->buffer;
printf("%*s ", 20, buffer);
}
printf("\n");
break;
case BUF_FULL:
break;
case FAIL:
exit(1);
break;
default:
printf("data for computeid %d ignored\n", row_code);
}
}
/* free metadata and data buffers */
for (pcol = columns; pcol - columns < ncols; pcol++) {
free(pcol->buffer);
}
free(columns);
if (DBCOUNT(dbproc) > -1) /* 得到SQL语句影响的行数 */
fprintf(stderr, "%d rows affected\n", DBCOUNT(dbproc));
}
dbclose(dbproc);
dbexit();
//\\
NSLog(@"%@",[sendmessage stringValue]);
NSData *data=[[sendmessage stringValue] dataUsingEncoding:NSUTF8StringEncoding];
[Socket writeData:data withTimeout:-1 tag:0];
}
为工程添加库路径
运行,点击send按钮,输出结果
2018-12-03 10:18:31.342076+0800 WindowSkip[2471:244406] sendchatmsg:中文
now str1=insert into [interview0710].[dbo].[Temp](id,flag,ChatMsg,ChatTime,IpAddress,UserName) values(3,NULL,N'中文
final str1=insert into [interview0710].[dbo].[Temp](id,flag,ChatMsg,ChatTime,IpAddress,UserName) values(3,NULL,N'中文','2018-12-03 10:18:31','192.168.1.212',N'中文')
c string is:insert into [interview0710].[dbo].[Temp](id,flag,ChatMsg,ChatTime,IpAddress,UserName) values(3,NULL,N'中文','2018-12-03 10:18:31','192.168.1.212',N'中文')
then fetch results:
1 rows affected
然后在数据库中查询
3,NULL,ä¸æ–‡,2018-12-03 10:18:31:000,192.168.1.212,ä¸æ–‡
无法显示出”中文“,登录http://www.mytju.com/classcode/tools/messyCodeRecover.asp查询乱码
这个时候,查Google,百度。
查dbgetcharset()获取字符集设置;
跟踪sql server语句执行情况。
最后,在.conf文件设置一下clientcharset=utf8解决了问题。
补充:数据库插入语句中有特殊符号,使用带参数的dbfcmd填充命令
dbfcmd(dbproc,"insert into [interview0710].[dbo].[Temp](ChatMsg,ChatTime,IpAddress,UserName) values(N'%s','%s',N'%s',N'%s')",[sendchatmsg cStringUsingEncoding:NSUTF8StringEncoding],[strDate cStringUsingEncoding:NSUTF8StringEncoding],[myip cStringUsingEncoding:NSUTF8StringEncoding],[sendchatmsg cStringUsingEncoding:NSUTF8StringEncoding]);
更多函数请看
https://www.cnblogs.com/fnlingnzb-learner/p/5835477.html