GVKun编程网logo

Phone数据持久化(plist|Archiver|Sqlite3)(ios 数据持久化方案)

22

在这里,我们将给大家分享关于Phone数据持久化的知识,让您更了解plist|Archiver|Sqlite3的本质,同时也会涉及到如何更有效地13.13sqlite3--DB-API2.0SQLit

在这里,我们将给大家分享关于Phone数据持久化的知识,让您更了解plist|Archiver|Sqlite3的本质,同时也会涉及到如何更有效地13.13 sqlite3 -- DB-API 2.0 SQLite数据库接口[Python参考库翻译]、Android数据持久化之SQLite数据库用法分析、Android:数据持久化(2/2) SQLite、Apache DolphinScheduler单机(Standalone)版本部署(数据持久化用MySQL)的内容。

本文目录一览:

Phone数据持久化(plist|Archiver|Sqlite3)(ios 数据持久化方案)

Phone数据持久化(plist|Archiver|Sqlite3)(ios 数据持久化方案)


1、plist

局限性:只有它支持的数据类型可以被序列化,存储到plist中。无法将其他Cocoa对象存储到plist,更不能将自定义对象存储。

支持的数据类型:Array,Dictionary,Boolean,Data,Date,Number和String.

xml文件 数据类型截图~其中基本数据(Boolean,Number和String.)、容器 (Array,Dictionary)

写入xml过程:先将基本数据写入容器 再调用容器的 writetoFile 方法,写入。

[theArray writetoFile:filePath atomically:YES];

拥有此方法的数据类型有:

atomically参数,将值设置为 YES。写入文件的时候,将不会直接写入指定路径,而是将数据写入到一个“辅助文件”,写入成功后,再将其复制到指定路径。

2、Archiver

特点:支持复杂的数据对象。包括自定义对象。对自定义对象进行归档处理,对象中的属性需满足:为基本数据类型(int or float or......),或者为实现了NSCoding协议的类的实例。自定义对象的类也需要实现NSCoding。

NSCoding 方法:-(id)initWithCoder:(NSCoder *)decoder; - (void)encodeWithCoder:(NSCoder *)encoder; 参数分别理解为解码者和编码者。

例如创建自定义类Student:NSObject <NSCoding>

1#import "Student.h"
2
3
4@implementation Student
5@synthesize studentID;
6@synthesize studentName;
7@synthesize age;
8@synthesize count;
9
10- (void)encodeWithCoder:(NSCoder *)encoder
11{
12 [encoder encodeObject: studentID forKey: kStudentId];
13 [encoder encodeObject: studentName forKey: kStudentName];
14 [encoder encodeObject: age forKey: kAge];
15 [encoder encodeInt:count forKey:kCount];
17}
18
19- (id)initWithCoder:(NSCoder *)decoder
20{
21if (self == [super init]) {
22 self.studentID = [decoder decodeObjectForKey:kStudentId];
23 self.studentName = [decoder decodeObjectForKey:kStudentName];
24 self.age = [decoder decodeObjectForKey:kAge];
25 self.count = [decoder decodeIntForKey:kCount];
27 }
28return self;
29}
30
31@end
复制代码

编码过程:

1/*encoding*/
2 Student *theStudent = [[Student alloc] init];
3 theStudent.studentID =@"神马";
4 theStudent.studentName =@"shenma";
5 theStudent.age =@"12";
6
7 NSMutableData *data = [[NSMutableData alloc] init];
8 NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
9
10 [archiver encodeObject: theStudent forKey:@"student"];
复制代码

NSKeyedArchiver可以看作“加密器”,将student实例编码后存储到data

NSMutableData 可看作“容器”,并由它来完成写入文件操作(inherits NSData)。

解码过程:

1/*unencoding*/
2 Student *studento = [[Student alloc] init];
3 data = [[NSData alloc] initWithContentsOfFile:documentsPath];
4 NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
5
6 studento = [unarchiver decodeObjectForKey:@"student"];
7 [unarchiver finishDecoding]; 复制代码

根据键值key得到反序列化后的实例。

3、sqlite

数据库操作~

13.13 sqlite3 -- DB-API 2.0 SQLite数据库接口[Python参考库翻译]

13.13 sqlite3 -- DB-API 2.0 SQLite数据库接口[Python参考库翻译]

13.13 sqlite3 -- DB-API 2.0 interface for sqlite databases

2.5版本中新增。

sqlite是一个提供轻量级的基于磁盘的数据库的C语言库,它不需要一个独立的服务进程,并且允许你使用一种非标准的sql查询语言来访问数据库。其他程序也可以使用sqlite来管理内部数据存储。你也可以使用sqlite来构建应用程序原型,然后把代码迁移到一个更大的数据库,比如Postgresql或者Oracle

pysqliteGerhard Haring写的,提供一个与PEP 249描述的DB-API 2.0规格说明书兼容的sql接口。

要使用这个模块,你必须现创建一个代表数据库的连接对象(Connection object)。如下的数据将被存储在/tmp/example文件中:

conn = sqlite2.connect('/tmp/example')

你还可以提供一个特殊的名称":memory:"在内存中来创建一个数据库。

一旦你建立了一个连接(Connection),你就可以创建一个游标对象(Cursor object),并且调用它的execute()方法来执行sql命令:

c = conn.cursor()

#Creat table

c.execute('''creat table stocks

(data text,trans text,symbol text,

qty real,price real)''')

#Insert a row of data

c.execute('''insert into stocks

values(' 2006-01-05 ','BUY','RHAT',100,35.14)''')

通常你的sql操作会用到你的Python程序中的变量的值。你不应该使用Python的字符串操作符来集合你的查询,因为这样是不安全的;它使你的程序容易首道sql注入攻击。

替代方法是使用DB-API的参数交换。在你需要使用一个变量的时候,使用"?"作为一个占位符,然后提供一个包含变量的元组作为油表对象的execute()方法的第二个参数。(其他的数据库模块可能使用不同的占位符,比如"%s"或者":1"。)例如:

#Never do this -- insecure!

symbol = 'IBM'

c.execute('''... where symbol = '%s' % symbol)

#Do this instead

t = (symbol,)

c.execute('select * from stocks where symbol=?',t)

#Larger example

for t in ((' 2006-03-28 ','IBM',1000,45.00),

(' 2006-04-05 ','MSOFT',72.00),

(' 2006-04-06 ','SELL',500,53.00),

):

c.execute('insert into stocks values (?,?,?)',t)

为了在执行一个select语句后返回数据,你既可以把游标作为一个迭代器来对待,调用游标对象的fetchone()方法来返回一个单一的匹配行,也可以调用fetchall()方法得到一个所有匹配行的列表。

下面这个例子使用了迭代器的方式:

>>> c = conn.cursor()

>>> c.execute('select * from stocks order by price')

>>> for row in c:

... print row

...

(u' 2006-01-05 ',u'BUY',u'RHAT',35.140000000000001)

(u' 2006-03-28 ',u'IBM',45.0)

(u' 2006-04-06 ',u'SELL',53.0)

(u' 2006-04-05 ',u'MSOFT',72.0)

>>>

See Also:

http://www.pysqlite.org

pysqlite的网站。

http://www.sqlite.org

sqlite的网站;相关文档描述了语法以及对其支持的可爱的sql的可用的数据类型。

PEP 249,Database API Specification 2.0

marc-André Lemburg写的PEP

13.13.1 模块函数和常量

PARSE_DECLTYPES

这个常量是用来作为connect函数的参数detect_types的值使用的。

设置它使sqlite3模块解析返回的每一列的声明类型。他会解析出声明类型中的第一个词,i. e. 对于'integer primary key',将会解析出'integer'。然后对于这一列,他会在转换字典中查找并且使用对这种类型注册的转换函数。注意:转换器的名称是大小写敏感的!

PARSE_COLNAMES

这个常量是用来作为connect函数的参数detect_types的值使用的。

设置它使sqlite接口解析返回的每一列的列名字。他会在其中查找一个这样书写的字符串[mytype],然后将‘mytype’作为这一列的类型。他将尝试在转换字典中查找‘mytype’的入口,然后使用在字典中找到的转换函数来返回值。在cursor.description中找到的名字只是这个列的第一个字,i. e. 如果你在sql中使用这样的语句:'as "x [datetime]"',那么将会解析出直到列名字中第一个空格出现时的任何字符——这个列的名字只是一个简单的'x'

connect(database[,timeout,isolation_level,detect_types,factory])

打开一个到sqlite数据库文件database的连接。你可以使用':memory:'来打开一个驻留在内存中而不是在磁盘上的数据库连接。

当一个数据库被多重连接时(when a database is accessed by multiple connections),并且有一个进程更改了数据库,这个sqlite数据库将会被锁住直到交易被执行。timeout参数指定这个连接在抛出一个异常之前等待的时间。默认的timeout参数是5.0(5秒钟)

对于isolation_level参数,请查看在 13.13.2 中描述的连接对象(Connection objects)isolation_level属性。

sqlite本身只支持TEXT,INTEGER,FLOAT,BLOBNULL类型。如果你希望使用其他类型,你需要添加自己的支持。detect_types参数,以及使用定制的利用模块级的register_converter函数注册的converters,可以让你很容易实现它。

detect_types默认为0i. e. off,没有类型检测),你可以设置其为PARSE_DECLTYPESPARSE_COLNAMES的任意组合来将类型检测打开。

sqlite3默认模块使用其Connection类来实现connection调用。然而,你可以编写Connection类的一个子类,然后将你的类提供给factory参数来使用你自己的类建立连接。

详细信息请参阅本手册的 13.13.4 章节。

register_converter(typename,callable)

注册一个转换器(callable)来将数据库中的字节串转换为一个Python类型。对于数据库中的所有typename类型的数据,这个转换器都将被调用。Confer the parameter detect_types of the connect function for how the type detection works.注意:typename和你的查询中的类型名字的大小写必须匹配。

register_adapter(type,callable)

注册一个转换器来将Python中的type类型转换为sqlite支持的一种类型。转换器只callable接受一个单一的Python值作为参数,也必须返回这些类型中的一个值:int,long,float,str(UTF-8 encoded)unicode代码或字节流。

complete_statement(sql)

返回True如果字符串sql是一个或多个用分号结束的sql语句。它不能识别出错误,如果sql语句在语法上是正确的,仅仅是在语义上不完整,但是语句是以分号结束的。

者可以用来构建一个sqlite的解释器,就如下面的例子一样:

#A minimal sqlite shell for experiments

import sqlite3

con = sqlite3.connect(":memory:")

con.isolation_level = None

cur = con.cursor()

buffer = ""

print "Enter your sql commands to execute in sqlite3."

print "Enter a blank line to exit."

while True:

line = raw_input()

if line == "":

break

buffer += line

if sqlite3.complete_statement(buffer):

try:

buffer = buffer.strip()

cur.execute(buffer)

if buffer.lstrip().upper().startswith("SELECT"):

print cur.fetchall()

except sqlite3.Error,e:

print "An error occurred:",e.args[0]

buffer = ""

con.close()

enable_callback_tracebacks(flag)

默认情况下,在用户定义的functions,aggregates,converters,authorizer等中你不会得到任何跟踪信息。如果你想要调试它们,你就可以用True作为flag参数的值来调用这个函数。之后,你就可以在sys.stderr中得到从回调中返回的跟踪信息。使用False来再次关闭这个功能。

13.13.2 连接对象(Connection Objects

一个连接实例具有以下属性和方法:

isolation_level

得到或设置当前的隔离级别(isolation level)。设置为None使用自动模式,或"DEFERRED","IMMEDIATE""EXLUSIVE"。更具体的说明请参考 13.13.5 章中的"Controlling Transactions"

cursor([cursorClass])

cursor方法接受一个单一的cursorClass参数。如果这个参数被提供,则必须是一个扩展了sqlite3.Cursor类的定制cursor类。

execute(sql,[parameters])

这是一个非标准的,通过调用cursor方法创建一个中间cursor对象的快捷方式,然后使用给出的参数调用cursorexecute方法。

executemany(sql,[parameters])

这是一个非标准的,通过调用cursor方法创建一个中间cursor对象的快捷方式,然后使用给出的参数调用cursorexecutemany方法。

executescript(sql_script)

这是一个非标准的,通过调用cursor方法创建一个中间cursor对象的快捷方式,然后使用给出的参数调用cursorexecutescript方法。

creat_function(name,num_params,func)

创建一个用户定义的函数,创建以后你可以在sql语句中通过name名字使用,num_params是这个函数接受的参数的数量,func是一个作为sql函数的Python调用器。

这个函数返回sqlite支持的任意类型:unicode,str,int,bufferNone

Example:

import sqlite3

import md5

def md5sum(t):

return md5.md5(t).hexdigest()

con = sqlite3.connect(":memory:")

con.creat_function("md5",1,md5sum)

cur = con.cursor()

cur.execute("select mdt(?)",("foo",))

print cur.getchone()[0]

creat_aggregate(name,aggregate_class)

创建一个用户定义的聚合函数。

一个聚合类必须提供一个step方法,它接受参数的数量num_params,和一个finalize方法,它返回最终的聚合结果。

finalize方法可以返回sqlite支持的任何类型:unicode,bufferNone

Example:

import sqlite3

class MySum:

def __init__(self):

self.count = 0

def step(self,value):

self.count += value

def finalize(self):

return self.count

con = sqlite3.connect(":memory:")

con.creat_aggregate("mysum",MySum)

cur = con.cursor()

cur.execute("create table test(i)")

cru.execute("insert into test(i) values (1)")

cru.execute("insert into test(i) values (2)")

cur.execute("select mysum(i) from test")

print cur.fetchone()[0]

creat_collation(name,callable)

使用指定的namecallable创建一个校对。调用器会被传入两个字符串参数。它应该返回-1,如果第一个排序比第二个低,返回0,如果两个排序一致,返回1,如果第一个排序比第二个高。需要指出的是,这个操作控制着排序(ORDER BY in sql),因此,你的比较不会影响其他sql操作。

注意:调用器将会按照Python的字节码来接受参数,这些参数通常的编码是UTF-8

下面的例子显示了一个定制的对"the wrong way"进行排序的校对:

import sqlite3

def collate_reverse(string1,string2):

return -cmp(string1,string2)

con = sqlite3.connect(":memory:")

con.create_collation("reverse",collate_reverse)

cur = con.cursor()

cur.execute("creat table test(x)")

cur.executemany("insert into test(x) values (?)",[("a",),("b",)])

cur.execute("select x from test order by x collate reverse")

for row in cur:

print row

con.close()

如果要删除一个校对,用None作为调用器来调用create_collation:

con.create_collation("reverse",None)

interrupt()

你可以从另一个线程中调用这个方法来中断一个连接上可能进行的任何查询操作。查询操作将会中断,查询的调用者将会得到一个异常。

set_authorizer(authorizer_callback)

这个动作注册一个调用器。在每一次尝试访问数据库中的一个表中的一列时,这个调用器都将会被调用。调用器应该返回sqlITE_OK,如果访问被允许,返回sqlITE_DENY,如果整个sql语句因为错误而应该被中断,返回sqlITE_IGnorE,如果这一列应该作为NULL值来对待。这些实例在sqlite3模块中可用。

调用器的第一个参数表明什么类型的操作要进行审定。第二个参数和第三个参数根据第一个参数,会使参数或者None。如果可用的话,第四个参数是数据库的名字("main","temp"等等)。第五个参数是对试图进行的访问负责的最内的(inner-most)触发器的名字或者视图,或者为None如果这个访问直接来自于sql代码。

关于第一个参数的可能的取值,以及依赖于第一个参数的第二个和第三个参数的含义,请参阅sqlite的文档。所有的必须的常量在sqlite3模块中都是可用的。

row_factory

你可以将这个属性改变为一个调用器的名字,这个调用器接受一个游标和一行的元组,返回计算后的行。这样,你可以实现返回结果的更高级的方法,比如,返回一个任然可以通过名字访问列的对象。

Example

import sqlite3

def dict_factory(cursor,row):

d = {}

for idx,col in enumerate(cursor.description):

d[col[0]] = row[idx]

return d

con = sqlite3.connect(":memory:")

con.row_factory = dict_factory

cur = con.cursor()

cur.execute("select 1 as a")

print cur.fetchone()["a"]

如果返回一个元组不能满足需要,而你想要对列的基于名字的访问,那么你可以考虑将row_factory设置为高度优化的sqlite3.Row类型。Row既提供基于索引的,也提供基于名字的对列的访问,却几乎不需要消耗额外的内存。它可能会比你自己定制的基于字典的访问甚至基于db_row的解决方法还要好。

text_factory

使用这个属性你可以控制对于TEXT数据类型返回什么样的对象。默认情况下,这个属性被设置为unicode,对于TEXTsqlite3模块会返回Unicode对象。如果你希望返回字节串(bytestrings)来替代,你可以设置其为str

你也可以将其设置为任何接受一个单一字节串参数并且返回最终对象的调用器。

参考下面的解释实例代码:

import sqlite3

con = sqlite3.connect(":memory:")

cur = con.cursor()

#Create the table

con.execute("create table person(lastname,firstname):)

AUSTRIA = u"/xd6sterreich"

# by default,rows are returned as Unicode

cur.execute("select ?",( AUSTRIA ,))

row = cur.fetchone()

assert type(row[0]) == str

# the bytestrings will be encoded in UTF-8,unless you stored garbage in the

# database ...

assert row[0] == AUSTRIA.encode("uft-8")

# we can also implement a custom text_factory ...

# here we implement one that will ignore Unicode characters that cannot be

# decoded form UTF-8

con.text_factory = lambda x: unicode(x,"utf-8","ignore")

cur.execute("select ?",("this is latin1 and would normally create errors" + u"/xe4/xf6/xfc".encode("latin1"),))

row = cur.fetchone()

assert type(row[0]) == unicode

# pysqlite offers a builtin optimized text_factory that will return bytestring

# objects,if the data is in ASCII only,and otherwise return unicode objects

con.text_factory = sqlite3.OptimizedUnicode

cur.execute("select ?",))

row = cur.fetchone()

assert type(row[0]) == unicode

cur.execute("select ?",(" germany ",))

row = cur.fetchone()

assert type(row[0]) == str

total_changes

返回从数据库连接开始后的所有被修改,插入或删除的行数。

13.13.3 游标对象(Cursor Objects)

一个游标实例拥有以下属性和方法:

execute(sql,[parameters])

执行一条sql语句。这条sql语句可能是参数化了(parametrized)(i. e. 用占位符替代了sql文字)sqlite3模块支持两种类型的占位符:问号(问好风格)和命名占位符(命名风格)

这个例子展示了怎样使用问号风格的参数:

import sqlite3

con = sqlite3.connect("mydb")

cur = con.cursor()

who = "Yeltsin"

age = 72

cur.execute("select name_last,age from people where name_last=? and age=?",(who,age))

print cur.fetchone()

下面的例子展示了如何使用命名风格:

import sqlite3

con = sqlite3.connect("mydb")

cur = con.cursor()

who = "Yeltsin"

age = 72

cur.execute("select name_last,age from people where name_last=:who and age=:age",{"who": who,"age": age})

print cur.fetchone()

execute()方法执行一条单一sql语句。如果你试图用其执行多余一条的语句,将会抛出一个警告(Warning)。你可以是用executescript()来再一次调用中执行多条sql语句。

executemany(sql,seq_of_parameters)

执行一条sql语句利用所有参数序列,或者从参数中得到的遍历图(mappings)sqlite3模块也允许使用迭代器生成参数来替代序列。

import sqlite3

class IterChars:

def __init__(self):

self.count = ord('a')

def __iter__(self):

return self

def next(self):

if self.count > ord('z'):

raise stopiteration

self.count += 1

return (chr(self.count - 1),) # this is a 1-tuple

con = sqlite3.connect(":memory:")

cur = con.cursor()

cur.execute("create table characters(c)")

theIter = IterChars()

cur.executemany("insert into characters(c) values (?)",theIter)

cur.execute("select c from characters")

print cur.fetchall()

下面是一个使用生成器的小例子:

import sqlite3

def char_generator():

import string

for c in string.letters[:26]:

yield (c,)

con = sqlite3.connect(":memory:")

cur = con.cursor()

cur.execute("create table charaters(c)")

cur.executemany("insert into characters(c) values (?)",char_generator())

cur.execute("select c from characters")

print cur.fetchall()

executescript(sql_script)

这是一个非标准的一次执行多条sql语句的简便方法。它首先提交一个COMMIT语句,然后将接收到的sql语句作为参数。

sql_script可以是一个字节串,也可以是一个Unicode字符串。

Example

import sqlite3

con = sqlite3.connect(":memory:")

cur = con.cursor()

cur.executescript("""

create table person(

firstname,

lastname,

age

);

create table book(

title,

author,

published

);

insert into book(title,author,published)

values (

'Dirk Gently''s Holistic Detective Agency',

'Douglas Adams',

1987

);

""")

rowcount

虽然sqlite3模块的游标类提供这一个属性,但是数据库引擎本身对"rows affected"/"rows selected"的定义的支持要更快一些。

对于select语句,rowcount总是None,以为在所有行都被取出之前,我们无从得知一个查询所产生的行数。

对于delete语句,如果你使用DELETE FROM而没有任何条件的话,sqliterowcount作为0报告。

根据Python DB API Spec的要求,这个rowcount属性“是-1,如果在这个游标上还没有executeXX()被执行过,或者其最后一册操作的rowcount对于接口不可知。”

13.13.4 sqlite and Python types

13.13.4 .1 Introduction

sqlite本身支持以下类型:NULL,REAL,TEXT,BLOB.

从而,如下的Python类型可以直接存入sqlite,而不会出现任何问题:

Python type

sqlite type

None

NULL

int

INTEGER

long

INTEGER

float

REAL

str(UTF8-encoded)

TEXT

unicod

TEXT

buffer

BLOB

下面是默认情况下sqlite类型转换为Python类型的对照:

sqlite type

Python type

NULL

None

INTEGER

intlong,取决于其大小

REAL

float

BLOB

buffer

sqlite3模块的类型系统可以通过两种方式扩展:可以通过对象适配器(object adaptation)将补充的Python类型存储在一个sqlite数据库中,也可以让sqlite3模块通过转换器把sqlite类型转换为不同的Python类型。

13.13.4 .2.2 使用适配器在sqlite数据库中储存补充的Python类型

如前所述,sqlite本身只支持有限的几种类型。为了在sqlite中使用其他Python类型,你必须使它们适配sqlite3模块支持的类型:None,unicodebuffer之一。

sqlite3模块使用Python对象适配器,在PEP 246中对此有描述。使用的约定为PrepareProtocol

有两种方法能够使sqlite3模块将定制的Python类型适配为被支持的类型。

13.13.4 .2.1 让你的对象自适应

如果你自己编写对象的话,这是一个很好的方法。假设你有这样的一个类:

class Point(object):

def __init__(self,x,y):

self.x,self.y = x,y

现在你希望将point储存于sqlite中的一个单独的列中。首先你需要选择一个支持的类型来替代point。让我们只是用一个字符串,用分号分割两个坐标。然后你需要为你的类添加一个__conform__(self,protocol)方法,它必须返回转换后的值。参数protocol将会是PrepaerProtocol

import sqlite3

class Point(object):

def __init__(self,y

def __conform__(self,protocol):

if protocol is sqlite3.PrepareProrocol:

return "%f;%f" % (self.x,self.y)

con = sqlite3.connect(":memory:")

cur = con.cursor()

p = Point(4.0,-3.2)

cur.execute("select ?",(p,))

print cur.fetchone()[0]

13.13.4 .2.2 注册一个适配器调用

另外一种可能是创建一个函数来将这种类型转换为字符串替代,并且使用register_adapter注册这个函数。

注意:进行适配的类型/类必须是一种新样式(new-style)的类,也即,它必须有对象作为其基类。

import sqlite3

class Point(object):

def __init__(self,y

def adapt_point(point):

return "%f;%f" % (point.x,point.y)

sqlite3.register_adapter(Point,adapt_point)

con = sqlite3.connect(":memory:")

cur = con.cursor()

p = Point(4.0,))

print cur.fetchone()[0]

对于Python的内置的datetime.datedatetime.datetime类型,sqlite3模块有两个默认的适配器。现在我们假设要按照Unix的时间戳而不是ISO替代来存储datetime.datetime对象。

import sqlite3

import datetime,time

def adapt_datetime(ts):

return time.mktime(ts.timetuple())

sqlite3.register_adapter(datetime.datetime,adapt_datetime)

con = sqlite3.connect(":memory:")

cur = cur.cursor()

Now = datetime.datetime.Now()

cur.execute("select ?",(Now,))

print cur.fetchone()[0]

13.13.4 .3 sqlite值转换为定制的Python类型

写一个适配器可以让你吧定制的Python类型存入到sqlite。但是要使它真正有用,我们需要完成Pythonsqlite再到Python的双向工作。

进入转换器。

让我们再次回到Point类。我们使用分号分割的字符串在sqlite中储存了xy坐标。

首先,我们会定义一个转换函数,接受这个字符串作为参数,并且从这个字符串构建一个Point对象。

注意:转换函数总是以字符串作为参数被调用,而不管你以什么方式将数据存入sqlite

注意:转换函数对大小写敏感。

def convert_point(s):

x,y = map(float,s.split(";"))

return Point(x,y)

现在你需要让sqlite3模块知道,你从数据库中取出的实际上是一个point。有两种方法可以做到这一点:

l 通过声明隐式进行

l 通过列名显式进行

两种方法都在“模块常量(Module Constants)”中描述, 13.13.1 章,在常量PARSE_DECLTYPESPARSE_COLNAMES的记录中。

下面的例子说明了两种方法:

import sqlite3

class Point(object):

def __init__(self,y

def __repr__(self):

return "(%f;%f)" % (self.x,self.y)

def adapt_point(point):

return "%f;%f" % (point.x,point.y)

def convert_point(s):

x,y)

# Register the adapter

sqlite3.register_adapter(Point,adapt_point)

# Register the converter

sqlite3.register_converter("point",convert_point)

p = Point(4.0,-3.2)

########################

# 1) Using declared types

con = sqlite3.connect(":memory:",detect_types=sqlite3.PARSE_DECLTYPES)

cur = con.cursor()

cur.execute("create table test(p point)")

cur.execute("insert into test(p) values (?)",))

cur.execute("select p from test")

print "with declared types:",cur.fetchone()[0]

cur.close()

con.close()

#################

# 2) Using column names

con = sqlite3.connect(":memory:",detect_types=sqlite3.PARSE_COLNAMES)

cur = con.cursor()

cur.execute("create table test(p)")

cur.execute("insert into test(p) values (?)",))

cur.execute('select p as "p [point]" from test')

print "with column names:",cur.fetchone()[0]

cur.close()

cur.close()

13.13.4 .4 默认的适配器和转换器

datetime模块中有对datedatetime类型有默认的适配器。它们会被作为ISO日期或ISO时间戳送入sqlite中。

默认的转换器以"date"名字对应datetime.date,和"timestamp"名字对应datetime.datetime注册。

这样,在大多数情况下,你可以从Python中使用date/timestamps,而不用做任何多余的工作。转换器的格式也与sqlite的实验性的date/time函数保持兼容。

下面的例子演示了这一点:

import sqlite3

import datetime

con = sqlite3.connect(":memory:",detect_types=sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES)

cur = con.cursor()

cur.execute("create table test(d date,ts timestamp)")

today = datetime.date.today()

Now = datetime.datetime.Now()

cur.execute("insert into test(d,ts) values (?,?)",(today,Now))

cur.execute("select d,ts from test")

row = cur.fetchone()

print today,"=>",row[0],type(row[0])

print Now,row[1],type(row[1])

cur.execute('select current_date as "d [date]",current_timestamp as "ts [timestamp]"')

row = cur.fetchone()

print "current_date",type(row[0])

print "current_timestamp",type(row[1])

13.13.5 交易控制

默认情况下,sqlite3模块在一个数据变更语言(Data Modification Language-DML)(i.e. INSERT/UPDATE/DELETE/REPLACE)语句之前,隐式的打开交易,并且在一个非DML,非查询(i.e. 任何非SELECT/INSERT/UPDATE/DELETE/REPLACE)语句之前,提交交易。

所以,如果你正处于一个交易之中,并且提交一个像CREATE TABLE ...,VACUUM,PRAGMA这样的命令,sqlite3模块将在执行这个名之前,隐式的提交者个交易。有两种原因要这样做。一是,这些命令中的一部分不能在交易中工作。二是pysqlite需要对交易保持跟踪(一个交易是否活跃)。

你可以控制pysqlite隐式的执行那种"BEGIN"语句,通过connect调用的isolation_level参数,或者通过连接的isolation_level属性。

如果你希望是自动执行模式,就把isolation_level设置为None

否则,就采用默认设置,这将会产生普通的"BEGIN"语句,或者将其设置为sqlite支持的isolation级别:DEFERRED,IMMEDIATE或者EXCLUSIVE

因为sqlite3模块需要对交易保持跟踪,所以你不能在你的sql中使用OR ROLLBACK或者ON CONFLICT ROLLBACK。取而代之,你需要捕捉IntegrityError错误,并且自己调用连接的rollback方法。

13.13.6 高效的使用pysqlite

13.13.6 .1 使用快捷方法

使用连接对象(Connection object)的非标准的execute,executemanyexecutescript方法,你可以书写更简洁的代码,因为你不用显式的(通常是冗余的)创建游标对象。这些快捷方法会隐式的创建并返回游标对象。这样你就可以直接使用对连接对象的单一单一调用,来执行一个SELECT语句,并对其进行迭代。

import sqlite3

persons = [

("Hugo","Boss"),

("Calvin","Klein")

]

con = sqlite3.connect(":memory:")

# Create ghe table

con.execute("create table person(firstname,lastname)")

# Fill the table

con.executemany("insert into person(firtname,lastname) values (?,persons)

# Print the table contents

for row in con execute("select firstname,lastname from person"):

print row

# Using a dummy WHERE clause to not let sqlite take the shortcut table deletes.

print "I just deleted",con.execute("delete from person where 1=1").rowcount,"rows")

13.13.6 .2 通过名字而不是索引来访问列

sqlite3模块的一个有用的特性是其内置的sqlite3.Row类,它被设计用来作为一个行的代理。

使用这个类包装的行,既可以通过索引(有点像元组)来访问,也可以通过大小写敏感的名字来访问。

import sqlite3

con = sqlite3.connect("mydb")

con.row_factory = sqlite3.Row

cur = con.cursor()

cur.execute("select name_last,age from people)

for row in cur:

assert row[0] == row["name_last"]

assert row["name_last"] == row["nAmE_1AsT"]

assert row[1] == row["age"]

assert row[1] == row["Age"]

本文档由sharkw翻译,转载请注明出处--http://blog.csdn.net/sharkw。

Android数据持久化之SQLite数据库用法分析

Android数据持久化之SQLite数据库用法分析

本文实例讲述了Android数据持久化之sqlite数据库用法。分享给大家供大家参考,具体如下:

这一节我将总结一下android中的另一种数据存储――sqlite 的相关知识点

sqlite数据库是android系统自带的,主要用到的类包括sqliteOpenHelpersqliteDatabase

1、sqliteOpenHelper:创建数据库和数据库版本管理的辅助类,该类是一个抽象类,所以我们一般都有一个子类sqliteOpenHelper,需要继承实现的方法主要有onCreate()、onUpgrade()、getWritableDatabase()等。getWritableDatabase()方法返回的是sqliteDatabase对象实例,如果数据库尚未创建,则会自动调用onCreate()方法来创建数据库,所以一些建表和数据初始化操作,应该放在onCreate()方法里 。

2、sqliteDatabase:操作sqlite数据库的类,可以进行sql语句,对数据库进行增、删、改、查的操作,该对象已经对基本的数据库操作进行了封装。可以调用insert()、delete()、executesql()等方法,进行实际的数据库操作 ,这个类相当于JDBC中的Connection,也类似Hibernate中的Session,或者Spring中的HibernateTemplate;也可以进行transaction的控制。很多对数据库的操作最终都是通过sqliteDatabase实例来调用执行的。

注意:数据库对于一个应用时私有的,并且在一个应用当中,数据库的名字也是唯一的。

3、Corsor:游标。通过Cursor可以对于从数据库中查询出来的结果集进行随机的读写访问。对于数据库的查询结果,一般是由子类sqliteCursor返回的。

特别注意:开发的时候一般会对前面两个类做一下包装,比如进行简单的封装,使得sqliteDatabase的查询方法不是返回原始的Cursor类(Cursor相当于JDBC中的ResultSet),而是返回业务对象等等

实现的代码如下:

sqliteOpenHelper类的实现:

package com.sql;
import android.content.Context;
import android.database.sqlite.sqliteDatabase;
import android.database.sqlite.sqliteDatabase.CursorFactory;
import android.database.sqlite.sqliteOpenHelper;
public class DataBaseHelpler extends sqliteOpenHelper{
  private static final int VERSION = 1;
  public DataBaseHelpler(Context context,String name,CursorFactory factory,int version) {
    super(context,name,factory,version);
    // Todo Auto-generated constructor stub
  }
  public DataBaseHelpler(Context context,String name){
    this(context,VERSION);
  }
  public DataBaseHelpler(Context context,int version){
    this(context,null,version);
  }
  @Override
  public void onCreate(sqliteDatabase db) {
    // Todo Auto-generated method stub
    System.out.println("creat database");
    db.execsql("create table student(no int,name verchar(20))");
  }
  @Override
  public void onUpgrade(sqliteDatabase db,int oldVersion,int newVersion) {
    // Todo Auto-generated method stub
    System.out.println("upgrade database");
  }
}

sqlite类的实现:

package com.sql;
import android.app.Activity;
import android.content.ContentValues;
import android.database.sqlite.sqliteDatabase;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class sqliteActivity extends Activity {
  /** Called when the activity is first created. */
  private Button button_create,button_upgreate,button_insert,button_up,button_query,button_delete;
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    button_create = (Button) findViewById(R.id.button1);
    button_upgreate = (Button) findViewById(R.id.button2);
    button_insert = (Button) findViewById(R.id.button3);
    button_up = (Button) findViewById(R.id.button4);
    button_query = (Button) findViewById(R.id.button5);
    button_delete = (Button) findViewById(R.id.button6);
    //创建数据库
    button_create.setonClickListener(new OnClickListener() {
      public void onClick(View v) {
        // Todo Auto-generated method stub
        DataBaseHelpler dbh = new DataBaseHelpler(sqliteActivity.this,"tabel_one");
        sqliteDatabase sql = dbh.getReadableDatabase();
      }
    });
    //更新数据库
    button_upgreate.setonClickListener(new OnClickListener() {
      public void onClick(View v) {
        // Todo Auto-generated method stub
        DataBaseHelpler dbh = new DataBaseHelpler(sqliteActivity.this,"tabel_one",2);
        sqliteDatabase sql = dbh.getReadableDatabase();
      }
    });
    //向数据库中的表中插入内容
    button_insert.setonClickListener(new OnClickListener() {
      public void onClick(View v) {
        // Todo Auto-generated method stub
        ContentValues values = new ContentValues();
        values.put("no",123);
        values.put("name","zhangsan");
        DataBaseHelpler dbh = new DataBaseHelpler(sqliteActivity.this,2);
        sqliteDatabase sql = dbh.getReadableDatabase();
        sql.insert("tabel_one",values);
      }
    });
    //更新表的内容
    button_up.setonClickListener(new OnClickListener() {
      public void onClick(View v) {
        // Todo Auto-generated method stub
        DataBaseHelpler dbh = new DataBaseHelpler(sqliteActivity.this,"tabel_one");
        sqliteDatabase sql = dbh.getReadableDatabase();
        ContentValues values = new ContentValues();
        values.put("name","wangwu");
        sql.update("tabel_one",values,"id=?",new String[]{"1"});
      }
    });
    //查找表的内容
    button_query.setonClickListener(new OnClickListener() {
      public void onClick(View v) {
        // Todo Auto-generated method stub
      }
    });
    //删除
    button_delete.setonClickListener(new OnClickListener() {
      public void onClick(View v) {
        // Todo Auto-generated method stub
      }
    });
  }
}

以上就是sqlite基本的应用。

更多关于Android相关内容感兴趣的读者可查看本站专题:《Android操作SQLite数据库技巧总结》、《Android数据库操作技巧总结》、《Android编程之activity操作技巧总结》、《Android文件操作技巧汇总》、《Android编程开发之SD卡操作方法汇总》、《Android开发入门与进阶教程》、《Android资源操作技巧汇总》、《Android视图View技巧总结》及《Android控件用法总结》

希望本文所述对大家Android程序设计有所帮助。

Android:数据持久化(2/2) SQLite

Android:数据持久化(2/2) SQLite

Abstract 概述

之前的随笔记录了普通文本文件和 SharedPreferences 两种保存数据的方式,现在来谈一谈用 SQLite 这个 Android 自带的数据库。

对 SQLite 的操作,从整体上来说分两步:

  1. 自定义一个 Helper 类,继承自 SQLiteOpenHelper,用于获取数据库的实例对象(或者说用于打开数据库的助手类);
  2. 通过该 Helper 类的对象,获取具体的数据库对象来进行 CRUD 操作;

Description 具体内容

1. 自定义 Helper 类

借助该 Helper 类的对象,我们才能获取数据库对象进行操作。
自定义 Helper 类的要求:

  • 继承 SQLiteOpenHelper 类;
  • 覆写(Override)onCreate()onUpgrade() 两个方法;
public class MyDBHelper extends SQLiteOpenHelper {

    public static final String BOOK_STORE = "BookStore.db";
    public static final String BOOK_TABLE = "Book";

    private Context context;

    public static final String CREATE_BOOK = "create table Book (id integer primary key autoincrement, name text, author text, price real, cover text)";

    public MyDBHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
        this.context = context;
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_BOOK);
        Toast.makeText(context, "Database created.", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        Toast.makeText(context, "Just upgraded.", Toast.LENGTH_SHORT);
    }
}

说明:

  • 继承了 SQLiteOpenHelper;
  • 覆写了 onCreate (),该方法在 helper 第一次获取数据库对象时调用,即创建 Book 表;
  • 覆写了构造器(构造方法 / Constructor),取得调用该实例的 context,以便内部使用(如这里的 Toast.makeText () 需要该参数);

注意:文中用到的文件都是使用 Android Studio 的默认定义,如 MainActivity.java, activity_main.xml 等。


2. 获取数据库对象

2. 0 通过 adb 工具查看数据库内容

在 SKD 下有一个 platform-tools 文件夹,里面有一个 adb 的工具可查看 sqlite 的数据。 该部分为可选内容,不使用该工具也可以进行开发,只是查看资料会相当不便,所以还是建议学会使用。

  1. 配置环境变量,将 platform-tools 目录加入到 PATH 中;
  2. 运行 App,在启动模拟器后,可以在 cmd/terminal 中输入 adb shell 启动 adb(可通过 exit 来退出);
  3. 切换目录到数据库所在文件夹:cd /data/data/com.scv.lawrence.databasetesting/databases(此处以 Nexus 为例,在真机上测试如小米等,目录很有可能不一样);
  4. 通过 ls 命令可以看到当前所有数据库(参考下面 2.1 的内容),此时使用 sqlite3 databaseName 命令可以打开该数据库。如 2.1 中创建了一个名为 BookStore.db 的数据库,含有一个 Book 表,则在 adb shell 中使用 ls 时能看到 BookStore.dbsqlite3 BookStore.db
  5. 在进入了 sqlite3 的环境后,可使用.schema 来查看创建表所执行的 SQL 语句,可使用.table 来查看所有表的名称,可使用.exit 来返回 adb shell,同样可使用各种 sqlite 支持的 SQL 语句;

2. 1 创建数据库

在布局文件中定义一个按钮。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Create DB"
        android:id="@+id/create_db"/>
<LinearLayout/>

在代码中(MainActivity.java)中对按钮的点击事件进行监听绑定,通过定义数据库的名称及版本号来创建 Helper 类的实例,并通过 getWritable () 获取该数据库的实例。

Button createDB = (Button) findViewById(R.id.create_db);
createDB.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        SQLiteOpenHelper helper = new MyDBHelper(MainActivity.this, MyDBHelper.BOOK_STORE, null, 1);//首先要获取相应数据库的helper,才能进行下一步操作
        SQLiteDatabase db = helper.getWritableDatabase();//第一次执行这个方法,会自动调用helper中定义的onCreate()
    }
});

第一次点击创建数据库的按钮,onCreate() 中的 Toast.makeText() 会提示消息表示执行了该方法,第二次点击开始不再有提示,不再执行 onCreate ();

2. 2 更新数据库

在获取 Helper 对象时必须传入数据库的名称和版本号,如果传入的版本号高于以前的版本号,则会自动执行 onUpgrade()

SQLiteOpenHelper helper = new MyDBHelper(MainActivity.this, MyDBHelper.BOOK_STORE, null, 2);
upgradeDB = (Button) findViewById(R.id.upgrade_db);
upgradeDB.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        SQLiteDatabase db = helper.getWritableDatabase();//由于helper中的第4个参数,版本信息version高于之前,故调用onUpgrade()
    }
});

3. CRUD 操作

CRUD 即增(Create)删(Delete)改(Update)查(Retrieve),均可使用执行 SQL 或调用 api 两种版本来执行;

3. 1 Create 插入数据

  • 将要插入的数据放入 ContentValues 的对象中;
  • 调用 SQLiteDatabase 对象的 insert ();

定义了两个 EditText 来输入书名和作者,点击按钮进行插入。(activity_main.xml)

<EditText
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:hint="book name"
    android:id="@+id/name"/>

<EditText
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:hint="author"
    android:id="@+id/author"/>

<Button
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="create"
    android:id="@+id/create"/>

MainActivity.java

Button createBtn = (Button) findViewById(R.id.create);
createBtn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        String bName = nameEdit.getText().toString();
        String author = authorEdit.getText().toString();

		ContentValues values = new ContentValues();
        values.put("name", bName);
        values.put("author", author);

        SQLiteDatabase db = helper.getWritableDatabase();
        db.insert(MyDBHelper.BOOK_TABLE, null, values);//与下面的SQL语句等价,且为了避免SQL注入攻击,必须使用传参的形式,不能自行拼装SQL语句。
        //db.execSQL("INSERT INTO Book (name, author) VALUES (?, ?)", new Object[]{bName, author});

        values.clear();//清空ContentValues对象的内容,为下次修改作准备
        Toast.makeText(MainActivity.this, "Data inserted.", Toast.LENGTH_SHORT).show();
        nameEdit.setText(null);
        authorEdit.setText(null);
    }
});

3. 2 Update 更新数据

  • 和 insert () 一样,以 ContentValues 对象来保存要更新的内容;
  • update () 比 insert () 多了 WHERE 条件及相应参数;

在布局文件(activity_main.xml)中加入按钮进行 update () 的绑定:

<Button
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="update"
    android:id="@+id/update"/>

MainActivity.java

Button updateBtn = (Button) findViewById(R.id.update);
updateBtn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
    	ContentValues values = new ContentValues();
        values.put("author", "updated Author");
        values.put("price", 23.33);

        SQLiteDatabase db = helper.getWritableDatabase();
        db.update(MyDBHelper.BOOK_TABLE, values, "id=?", new String[]{"1"});//等价于下面的SQL语句
        //db.execSQL("UPDATE Book SET author=?,price=? WHERE id=?", new Object[]{"updated SQL", 23.33, 1});
        values.clear();
        Toast.makeText(MainActivity.this, "Data updated.", Toast.LENGTH_SHORT).show();
    }
});

3. 3 Delete 删除数据

  • delete () 只需要传入表的名称和 WHERE 条件及相应参数即可;
Button deleteBtn = (Button) findViewById(R.id.delete);
deleteBtn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        SQLiteDatabase db = helper.getWritableDatabase();
        db.delete(MyDBHelper.BOOK_TABLE, "id=?", new String[]{"3"});//等价于下面的SQL语句
        //db.execSQL("DELETE FROM Book WHERE id=?;", new Object[]{1});//auto-boxing
    }
});

3. 4 Retrieve 查询数据

  • 查询的结果就是一张表格,需要使用 Cursor 对象的 moveToNext() 指向表格的每一行,在每一行中;
Button retrieveBtn = (Button) findViewById(R.id.retrieve);
retrieveBtn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        SQLiteDatabase db = helper.getWritableDatabase();
//      Cursor cursor = db.rawQuery("SELECT name, author, price FROM Book",null,null);//与下面的query()等价,然而要求API必须在16以上
        Cursor cursor = db.query(MyDBHelper.BOOK_TABLE, null, null, null, null, null, null);

        if(cursor.moveToNext()){
            do{
                String name = cursor.getString(cursor.getColumnIndex("name"));
                String author = cursor.getString(cursor.getColumnIndex("author"));
                double price = cursor.getDouble(cursor.getColumnIndex("price"));

                Log.i(TAG, name + ", " + author + ", " + price);
            }while(cursor.moveToNext());
        }

    }

});

虽然 query() 的参数很多,但只要将其视作完整的 SELECT 语句的拆分,就会很方便理解:
query(from_table, select_columns, where, whereArgs, group_by, having, order_by)

至少出版了一本书且每本书的售价在 50 以上,返回作者名字及作品数:

SELECT author, count(name)
FROM Book
WHERE price>50
GROUP BY author
HAVING count(name)>1
ORDER BY author

等价于:

db.query(MyDBHelper.BOOK_TABLE, "author, count(name)", "price>?", new String[]{"50"}, "author", "count(name)>1", "author");

Reference 参考

  • 《第一行代码:Android》郭霖(著)

Apache DolphinScheduler单机(Standalone)版本部署(数据持久化用MySQL)

Apache DolphinScheduler单机(Standalone)版本部署(数据持久化用MySQL)

  1. 下载并安装JDK (1.8+),并将 JAVA_HOME 配置到以及 PATH 变量中,验证如下:
  2. 下载最新版DolphinScheduler二进制包:3.0.0-alpha版本下载地址 ,可以用wget工具进行下载,近650M的文件,需要等待一段时间
    wget https://dlcdn.apache.org/dolphinscheduler/3.0.0-alpha/apache-dolphinscheduler-3.0.0-alpha-bin.tar.gz -c --no-check-certificate
  3. 下载完成后解压:
    tar -xvzf apache-dolphinscheduler-3.0.0-alpha-bin.tar.gz
  4. 数据库配置【MySQL】
    【备注:如果对配置数据持久化没有要求,可跳过此步骤,DolphinScheduler单机版本默认试用H2作为配置数据持久化存储介质,且是内存模式,重启后数据丢失】
  • 添加MySQL的jdbc驱动器mysql-connector-java(版本8.0.16+)的jar包到{解压目录}/apache-dolphinscheduler-3.0.0-SNAPSHOT-bin/standalone-server/libs/standalone-server/ 目录下
  • 修改配置
    修改{解压目录}/apache-dolphinscheduler-3.0.0-SNAPSHOT-bin/bin/env/dolphinscheduler_env.sh文件中如下内容:
    	export DATABASE=${DATABASE:-mysql}
    	export SPRING_PROFILES_ACTIVE=${DATABASE}
    	export SPRING_DATASOURCE_DRIVER_CLASS_NAME=com.mysql.cj.jdbc.Driver 
    	export SPRING_DATASOURCE_URL={url}【mysql连接地址】
    	export S PRING_DATASOURCE_USERNAME={username}【数据库账号】
    	export SPRING_DATASOURCE_PASSWORD={password}【数据库密码】
    
  1. 服务启动
    {解压目录}/apache-dolphinscheduler-3.0.0-SNAPSHOT-bin//bin/dolphinscheduler-daemon.sh start standalone-server

遇到的问题:

shell脚本异常:“/bin/sh^M: bad interpreter: No such file or directory” 和“$''\r'': command not found”
原因:windows下编写的脚本文件,放到Linux中无法识别格式 \

解决方案:

  1. 用vi打开脚本文件,在命令模式下输入:set ff=unix \
  2. 安装dos2unix命令,用“dos2unix”对脚本文件进行格式转换

今天关于Phone数据持久化plist|Archiver|Sqlite3的分享就到这里,希望大家有所收获,若想了解更多关于13.13 sqlite3 -- DB-API 2.0 SQLite数据库接口[Python参考库翻译]、Android数据持久化之SQLite数据库用法分析、Android:数据持久化(2/2) SQLite、Apache DolphinScheduler单机(Standalone)版本部署(数据持久化用MySQL)等相关知识,可以在本站进行查询。

本文标签: