This XML file does not appear to have any style information associated with it. The document tree is shown below.博客园_首页代码改变世界uuid:5de59c50-a92f-4447-96ed-ab86451ed183;id=61182014-07-27T11:59:08Zfeed.cnblogs.comhttp://www.cnblogs.com/jianyus/p/3865670.htmlSharePoint 2013 Designer系列之数据视图筛选 - 霖雨 在SharePoint中,我们经常需要对列表进行简单的筛选,这时,数据视图就有作用了,我们可以定制对于字段的筛选,来进行展示;特别的,筛选不同于搜索,并没有对于附件或者文档的全文检索,如果需要全文检索,可以使用列表的垂直搜索功能。 1、新建一个测试页面,然后右键在高级模式下编辑,如下图: 2...2014-07-27T11:22:00Z2014-07-27T11:22:00Z霖雨http://www.cnblogs.com/jianyus/
在SharePoint中,我们经常需要对列表进行简单的筛选,这时,数据视图就有作用了,我们可以定制对于字段的筛选,来进行展示;特别的,筛选不同于搜索,并没有对于附件或者文档的全文检索,如果需要全文检索,可以使用列表的垂直搜索功能。
1、新建一个测试页面,然后右键在高级模式下编辑,如下图:
2、在PlaceHolderMain节点里,加入webpartzone,用来添加数据视图;
3、数据视图选择News列表,如下图:
4、列表视图就选择第一个就可以了,如下图:
5、查看测试页面,如下图:
6、在ribbon上点击筛选,如下图:
7、选择筛选的域名Title,然后新建值,如下图:
8、新建参数为k,参数源是查询字符串,如下图:
9、建好的筛选条件,Title里包好参数k的值,如下图:
10、添加脚本和html,如下图:
11、测试数据视图的筛选,很简单吧,在有些场景下,数据视图的筛选,还是很有用的!
总 结
这里只是介绍了单一字段的筛选,我们通过数据视图,还可以制作更复杂的筛选,然后利用分组,可以做出非常漂亮和霸气的展示页面。
本文链接:SharePoint 2013 Designer系列之数据视图筛选,转载请注明。
http://www.cnblogs.com/jacksu-tencent/p/3871755.html个人github blog环境设置 - jack(小保) 每个人都想拥有自己的网站,但是大部分比较屌丝,不想花钱租赁服务器,哈哈,屌丝有屌丝办法。github应该都听说过吧,github.io提供了此功能,而且使用github来管理自己的代码,如果你有域名,还可以绑定你自己的域名欧。我在github的博客终于捣鼓好了,也给大家介绍一下建站步骤。2014-07-27T11:14:00Z2014-07-27T11:14:00Zjack(小保)http://www.cnblogs.com/jacksu-tencent/
每个人都想拥有自己的网站,但是大部分比较屌丝,不想花钱租赁服务器,哈哈,屌丝有屌丝办法。github应该都听说过吧,github.io提供了此功能,而且使用github来管理自己的代码,如果你有域名,还可以绑定你自己的域名欧。我在github的博客(jacksu blog)终于捣鼓好了,也给大家介绍一下建站步骤。
软件安装
大体需要以下几个东东,个性化的就需要自己去搜寻。
pelican
安装
我们需要网站的管理工具pelican,pelican可以把markdown的文件生成html和pdf,pelican又依赖于pipe,安装命令如下:sudo easy_install pipe
sudo pip install pelican
检查是否安装成功
执行如下命令:pelican -h
或者pelican .md所在目录
Markdown包
pelican不可以识别markdown,需要下载markdown包,markdown的下载方式为:sudo pip install Markdown
主题
没有主题,你的网站太难看了,那么下载一个主题:
git clone https://github.com/farseerfc/pelican-themes
设置
github上的设置
在github上建立username.github.io
的项目(如何在github建立项目,我就不说了,应该很多人会),参考官方文档设置,过十分钟左右你就可以通过username.github.io
访问了。
settings.py设置
settings.py的内容大体如下,我也是参考mx的blog的.
# -*- coding: utf-8 -*-
import sys
TIMEZONE = 'Asia/Shanghai'
DEFAULT_LANG = 'zhs'
SITENAME = "X. Wei's Blog"
AUTHOR = 'X.Wei'
DISQUS_SITENAME = 'xweisblog'
GITHUB_URL = '<https://github.com/X-Wei>'#github链接
SITEURL = '<http://x-wei.github.com>'
GOOGLE_ANALYTICS = 'UA-30756331-1'#谷歌站点分析
TAG_FEED = 'feeds/%s.atom.xml'
DEFAULT_PAGINATION = 4#默认每一页有多少篇文章
DEFAULT_CATEGORY ='misc'
OUTPUT_PATH = '.'
#需要把输出路径从默认的'output'改成根目录(your_id.github.com目录), 因为githubpage需要把index.html上传到repo的master分支的根目录才可以!
PATH = 'posts'#这个是指定放置.md/.rst文件的目录
LINKS = (('dofine', '<http://www.dofine.me>'),
('farseerfc', "<http://farseerfc.github.com/>"),
)#友情链接~
SOCIAL = (
('github', '<https://github.com/x-wei>'),
)#社交网络链接
#~ ('twitter', '<http://twitter.com/farseerfc>'),
#~ ('facebook', '<http://www.facebook.com/farseerfc>'),
#~ ('weibo', '<http://weibo.com/farseerfc>'),
#~ ('renren', '<http://www.renren.com/farseer>'),
#这个是farseerfc同学自己加的, 可以显示他的新浪微博内容, 有微博的话可以把这些加上~
#~ TWITTER_USERNAME = 'farseerfc'
#~ SIDEBAR_CUSTOM = r"""
#~ <li class="nav-header"><h4><i class="icon-list-alt"></i>Weibo</h4></li>
#~ <iframe width="100%" height="550" class="share_self" frameborder="0" scrolling="no"
#~ src="<http://widget.weibo.com/weiboshow/index.php?language=&width=0&height=550&fansRow=1&ptype=1&speed=0&skin=2&isTitle=1&noborder=1&isWeibo=1&isFans=1&uid=1862842353&verifier=b193b9de&dpc=1>">
#~ </iframe>
#~ """
#google自定义搜索(大概是站内搜索吧)
#~ GOOGLE_CUSTOM_SEARCH_SIDEBAR = "001578481551708017171:axpo6yvtdyg"
#~ GOOGLE_CUSTOM_SEARCH_NAVBAR = "001578481551708017171:hxkva69brmg"
import sys
TIMEZONE = 'Asia/Shanghai'
DEFAULT_LANG = 'zhs'
SITENAME = "X. Wei's Blog"
AUTHOR = 'X.Wei'
DISQUS_SITENAME = 'xweisblog'
GITHUB_URL = '<https://github.com/X-Wei>'#github链接
SITEURL = '<http://x-wei.github.com>'
GOOGLE_ANALYTICS = 'UA-30756331-1'#谷歌站点分析
TAG_FEED = 'feeds/%s.atom.xml'
DEFAULT_PAGINATION = 4#默认每一页有多少篇文章
DEFAULT_CATEGORY ='misc'
OUTPUT_PATH = '.'
#需要把输出路径从默认的'output'改成根目录(your_id.github.com目录), 因为githubpage需要把index.html上传到repo的master分支的根目录才可以!
PATH = 'posts'#这个是指定放置.md/.rst文件的目录
LINKS = (('dofine', '<http://www.dofine.me>'),
('farseerfc', "<http://farseerfc.github.com/>"),
)#友情链接~
SOCIAL = (
('github', '<https://github.com/x-wei>'),
)#社交网络链接
#~ ('twitter', '<http://twitter.com/farseerfc>'),
#~ ('facebook', '<http://www.facebook.com/farseerfc>'),
#~ ('weibo', '<http://weibo.com/farseerfc>'),
#~ ('renren', '<http://www.renren.com/farseer>'),
#这个是farseerfc同学自己加的, 可以显示他的新浪微博内容, 有微博的话可以把这些加上~
#~ TWITTER_USERNAME = 'farseerfc'
#~ SIDEBAR_CUSTOM = r"""
#~ <li class="nav-header"><h4><i class="icon-list-alt"></i>Weibo</h4></li>
#~ <iframe width="100%" height="550" class="share_self" frameborder="0" scrolling="no"
#~ src="<http://widget.weibo.com/weiboshow/index.php?language=&width=0&height=550&fansRow=1&ptype=1&speed=0&skin=2&isTitle=1&noborder=1&isWeibo=1&isFans=1&uid=1862842353&verifier=b193b9de&dpc=1>">
#~ </iframe>
#~ """
#google自定义搜索(大概是站内搜索吧)
#~ GOOGLE_CUSTOM_SEARCH_SIDEBAR = "001578481551708017171:axpo6yvtdyg"
#~ GOOGLE_CUSTOM_SEARCH_NAVBAR = "001578481551708017171:hxkva69brmg"
我的settings.py。
md头
每个md文件必须包含下面相关内容,各个字段含义应该通过英文意思就可以看出来。
Title: 个人github blog环境设置
Date: 2014-7-27 00:20
Modified: 2014-7-27 00:20
Category: env_set
Tags: github
Slug: my-github-blog-set
Author: jacksu
Summary: 每个人都想拥有自己的网站,但是大部分比较屌丝,不想花钱租赁服务器,哈哈,屌丝有屌丝办法。github应该都听说过吧,github.io提供了此功能,而且使用github来管理自己的代码,如果你有域名,还可以绑定你自己的域名欧。我在github的博客终于捣鼓好了,也给大家介绍一下建站步骤。
建站
把刚才github的项目拉到本地,执行如下命令:
git clone https://github.com/username/username.github.io
pelican -s settings.py -t ../pelican-themes/bootstrap2/ posts/
其中-t后面是你的主题目录,posts是md所在的目录。再执行如下命令:
git add .
git commit -m "add"
git push
我们的个人网站建成了,可以访问usrname.github.io
了.
jacksu blog
本文链接:个人github blog环境设置,转载请注明。
http://www.cnblogs.com/yangjunhua/p/3871763.htmljQuery.lazyload使用及源码分析 - 华子yjh 前言:貌似以前自己也写过图片懒加载插件,但是新公司使用的是jQuery.lazyload插件,为了更好的运用,自己还是把源码看了遍,分别记录了如何使用,插件原理,各个配置属性的完整解释,demo实例,源码分析(较简短),源码分析可以配合使用,配置属性,原理进行阅读,如需转载,请注明出处博客园 华子y...2014-07-27T10:56:00Z2014-07-27T10:56:00Z华子yjhhttp://www.cnblogs.com/yangjunhua/
前言:
貌似以前自己也写过图片懒加载插件,但是新公司使用的是jQuery.lazyload插件,为了更好的运用,自己还是把源码看了遍,分别记录了如何使用,
插件原理,各个配置属性的完整解释,demo实例,源码分析(较简短),源码分析可以配合使用,配置属性,原理进行阅读,如需转载,请注明出处
博客园 华子yjh
一、如何使用
$('img').lazyload();
// 带参数(配置对象),下面配置对象中的各个属性值都是默认的
$('img').lazyload({
threshold : 0,
failure_limit : 0,
event : "scroll",
effect : "show",
container : window,
data_attribute : "original",
skip_invisible : true,
appear : null,
load : null,
placeholder : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC"
});
二、内部原理
首先选中的img元素都绑定了一个appear事件(处理img显示真实的图片地址),方便以后满足条件时触发该事件;
在配置对象中有一个container属性配置,默认为window,如果img元素在该container容器视口中,则触发appear事件;
为了判断img元素是否在container容器视口范围中,造了如下四个轮子:
$.rightoffold = function(element, settings) {}; // 在视口右方
$.abovethetop = function(element, settings) {}; // 在视口上方
$.leftofbegin = function(element, settings) {}; // 在视口左方
看看源码中是如何利用这四个轮子:
/* Nothing. */
}
// 不满足在上方,左方;也不满足在下方,右方; 则触发appear事件
else if (!$.belowthefold(this, settings) && !$.rightoffold(this, settings)) {
$this.trigger("appear");
}
三、配置对象中的其他属性
临界值,这个值是针对container容器的,即距离container容器视口的临界值
threshold: 0
}
事件,container容器默认绑定这个事件,在这个事件被触发时,会不断的判断img元素是否满足触发appear的条件,
因此当浏览器不停的滚动下来时,如果满足条件,则显示图片;
另外还有一点,如果这个事件不是scroll事件,则选中的img元素都会绑定这个事件,绑定的这个事件中同样会触发内部appear事件;
event: 'scroll'
}
显示方法,默认为show,也可以设置为fadeIn,API中隐藏了一个配置属性effectspeed,动画运行的时间
effect: "show"
}
img元素的一个data属性,用于存放图片的真实地址
data_attribute: "original",
}
忽略隐藏的img元素
skip_invisible: true
}
图片占位符,img元素默认src属性为1*1像素的透明图片
placeholder: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC"
}
在img触发appear事件时执行的回调
appear: null
}
在img触发load事件时执行的回调
load: null
}
最后一个配置属性failure_limit
failure_limit: 0
}
为了便于理解,我们先来看一段与其有关的源码:
elements.each(function() {
if ($.abovethetop(this, settings) || $.leftofbegin(this, settings)) {
// ...
} else if (!$.belowthefold(this, settings) && !$.rightoffold(this, settings)) {
// ...
} else {
if (++counter > settings.failure_limit) {
return false;
}
}
});
什么意思呢,如果找到的是第 failure_limit 个img元素,且不在container视口上方,左方及视口内(可以允许在视口下方,右方),则中断循环
三、demo
看完原理和配置属性,是否觉得很简单呢,来看看几个demo吧
demo1 下拉滚动: http://jsfiddle.net/ddEPL/
demo2 Tab切换: http://jsfiddle.net/ddEPL/1/
四、源码分析
* Lazy Load - jQuery plugin for lazy loading images
*
* Copyright (c) 2007-2013 Mika Tuupola
*
* Licensed under the MIT license:
* http://www.opensource.org/licenses/mit-license.php
*
* Project home:
* http://www.appelsiini.net/projects/lazyload
*
* Version: 1.9.3
*
*/
(function($, window, document, undefined) {
var $window = $(window);
$.fn.lazyload = function(options) {
var elements = this;
var $container;
var settings = {
threshold : 0,
failure_limit : 0,
event : "scroll",
effect : "show",
container : window,
data_attribute : "original",
skip_invisible : true,
appear : null,
load : null,
placeholder : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC"
};
function update() {
var counter = 0;
elements.each(function() {
var $this = $(this);
// 如果图片隐藏,且忽略隐藏,则中断循环
if (settings.skip_invisible && !$this.is(":visible")) {
return;
}
if ($.abovethetop(this, settings) || $.leftofbegin(this, settings)) {
/* Nothing. */
}
// img满足在container视口中,则显示
else if (!$.belowthefold(this, settings) && !$.rightoffold(this, settings)) {
$this.trigger("appear");
/* if we found an image we'll load, reset the counter */
counter = 0;
}
// 如果找到的是第(failure_limit + 1)个img元素,且不在container视口上方,左方及视口内(可以允许在视口下方,右方),
// 则中断循环
else {
if (++counter > settings.failure_limit) {
return false;
}
}
});
}
if(options) {
/* Maintain BC for a couple of versions. */
if (undefined !== options.failurelimit) {
options.failure_limit = options.failurelimit;
delete options.failurelimit;
}
if (undefined !== options.effectspeed) {
options.effect_speed = options.effectspeed;
delete options.effectspeed;
}
$.extend(settings, options);
}
/* Cache container as jQuery as object. */
$container = (settings.container === undefined ||
settings.container === window) ? $window : $(settings.container);
/* Fire one scroll event per scroll. Not one scroll event per image. */
// 为container绑定scroll事件
if (0 === settings.event.indexOf("scroll")) {
$container.bind(settings.event, function() {
return update();
});
}
this.each(function() {
var self = this;
var $self = $(self);
self.loaded = false;
/* If no src attribute given use data:uri. */
// 设置占位符
if ($self.attr("src") === undefined || $self.attr("src") === false) {
if ($self.is("img")) {
$self.attr("src", settings.placeholder);
}
}
/* When appear is triggered load original image. */
// one绑定appear,触发后则移除该事件
$self.one("appear", function() {
if (!this.loaded) {
// 存在回调则触发
if (settings.appear) {
var elements_left = elements.length;
settings.appear.call(self, elements_left, settings);
}
$("<img />")
.bind("load", function() {
var original = $self.attr("data-" + settings.data_attribute);
$self.hide();
if ($self.is("img")) {
$self.attr("src", original);
} else {
$self.css("background-image", "url('" + original + "')");
}
$self[settings.effect](settings.effect_speed);
self.loaded = true;
/* Remove image from array so it is not looped next time. */
// 更新elements,过滤掉已经加载的img元素,避免下次在update中轮循
var temp = $.grep(elements, function(element) {
return !element.loaded;
});
elements = $(temp);
// 存在回调则触发
if (settings.load) {
var elements_left = elements.length;
settings.load.call(self, elements_left, settings);
}
})
.attr("src", $self.attr("data-" + settings.data_attribute));
}
});
/* When wanted event is triggered load original image */
/* by triggering appear. */
// 绑定不是scroll的事件,用于触发appear事件
if (0 !== settings.event.indexOf("scroll")) {
$self.bind(settings.event, function() {
if (!self.loaded) {
$self.trigger("appear");
}
});
}
});
/* Check if something appears when window is resized. */
$window.bind("resize", function() {
update();
});
/* With IOS5 force loading images when navigating with back button. */
/* Non optimal workaround. */
if ((/(?:iphone|ipod|ipad).*os 5/gi).test(navigator.appVersion)) {
$window.bind("pageshow", function(event) {
if (event.originalEvent && event.originalEvent.persisted) {
elements.each(function() {
$(this).trigger("appear");
});
}
});
}
/* Force initial check if images should appear. */
$(document).ready(function() {
update();
});
return this;
};
/* Convenience methods in jQuery namespace. */
/* Use as $.belowthefold(element, {threshold : 100, container : window}) */
$.belowthefold = function(element, settings) {
var fold;
if (settings.container === undefined || settings.container === window) {
fold = (window.innerHeight ? window.innerHeight : $window.height()) + $window.scrollTop();
} else {
fold = $(settings.container).offset().top + $(settings.container).height();
}
return fold <= $(element).offset().top - settings.threshold;
};
$.rightoffold = function(element, settings) {
var fold;
if (settings.container === undefined || settings.container === window) {
fold = $window.width() + $window.scrollLeft();
} else {
fold = $(settings.container).offset().left + $(settings.container).width();
}
return fold <= $(element).offset().left - settings.threshold;
};
$.abovethetop = function(element, settings) {
var fold;
if (settings.container === undefined || settings.container === window) {
fold = $window.scrollTop();
} else {
fold = $(settings.container).offset().top;
}
return fold >= $(element).offset().top + settings.threshold + $(element).height();
};
$.leftofbegin = function(element, settings) {
var fold;
if (settings.container === undefined || settings.container === window) {
fold = $window.scrollLeft();
} else {
fold = $(settings.container).offset().left;
}
return fold >= $(element).offset().left + settings.threshold + $(element).width();
};
$.inviewport = function(element, settings) {
return !$.rightoffold(element, settings) && !$.leftofbegin(element, settings) &&
!$.belowthefold(element, settings) && !$.abovethetop(element, settings);
};
/* Custom selectors for your convenience. */
/* Use as $("img:below-the-fold").something() or */
/* $("img").filter(":below-the-fold").something() which is faster */
$.extend($.expr[":"], {
"below-the-fold" : function(a) { return $.belowthefold(a, {threshold : 0}); },
"above-the-top" : function(a) { return !$.belowthefold(a, {threshold : 0}); },
"right-of-screen": function(a) { return $.rightoffold(a, {threshold : 0}); },
"left-of-screen" : function(a) { return !$.rightoffold(a, {threshold : 0}); },
"in-viewport" : function(a) { return $.inviewport(a, {threshold : 0}); },
/* Maintain BC for couple of versions. */
"above-the-fold" : function(a) { return !$.belowthefold(a, {threshold : 0}); },
"right-of-fold" : function(a) { return $.rightoffold(a, {threshold : 0}); },
"left-of-fold" : function(a) { return !$.rightoffold(a, {threshold : 0}); }
});
})(jQuery, window, document);
本文链接:jQuery.lazyload使用及源码分析,转载请注明。
http://www.cnblogs.com/stoneniqiu/p/3871754.htmlSpeechLib 语音播报 - stoneniqiu SpeechLib这的dll专门用来播放语音,能够识别英语、简体和繁体。并且可以播放声音文件,支持WAV格式,但不支持MP3。在报警场合下已经够用了。 基本播放语音及文件。支持异步。using System;using System.Threading;using SpeechLib;namesp....2014-07-27T10:50:00Z2014-07-27T10:50:00Zstoneniqiuhttp://www.cnblogs.com/stoneniqiu/
SpeechLib这的dll专门用来播放语音,能够识别英语、简体和繁体。并且可以播放声音文件,支持WAV格式,但不支持MP3。在报警场合下已经够用了。
基本播放语音及文件。支持异步。
using System.Threading;
using SpeechLib;
namespace Model.AlarmHandle
{
/// <summary>
///语音播报
/// </summary>
public class SpeechVoice
{
/// <summary>
/// The _voice
/// </summary>
private SpVoice _voice;
private SpVoiceClass spVoice;
private readonly SpFileStreamClass spFile;
/// <summary>
/// Initializes a new instance of the <see cref="SpeechVoice"/> class.
/// </summary>
public SpeechVoice()
{
_voice = new SpVoice();
spVoice = new SpVoiceClass();
spFile = new SpFileStreamClass();
}
/// <summary>
/// 播放
/// </summary>
/// <param name="text">The text.</param>
/// <param name="speakFlag">The speak flag.</param>
public void Speak(string text, SpeechVoiceSpeakFlags speakFlag = SpeechVoiceSpeakFlags.SVSFDefault)
{
_voice.Speak(string.Empty); _voice.Speak(text, speakFlag);
}
/// <summary>
/// 异步播放
/// </summary>
/// <param name="text">The text.</param>
public void SpeakAsync(string text)
{
_voice.Speak(text, SpeechVoiceSpeakFlags.SVSFlagsAsync);
}
/// <summary>
/// Plays the sound.
/// </summary>
/// <param name="fileName">Name of the file.</param>
public void PlaySound(string fileName)
{
//要加载COM组件:Microsoft speech object Library
if (!System.IO.File.Exists(fileName)) return;
spFile.Open(fileName, SpeechStreamFileMode.SSFMOpenForRead, true);
var istream = spFile as ISpeechBaseStream;
spVoice.SpeakStream(istream, SpeechVoiceSpeakFlags.SVSFIsFilename);
spFile.Close();
}
/// <summary>
/// 暂停
/// </summary>
public void Pause()
{
if (_voice != null)
_voice.Pause();
}
/// <summary>
/// Stops the voice.
/// </summary>
public void StopVoice()
{
_voice.Pause();
_voice.Speak(string.Empty, SpeechVoiceSpeakFlags.SVSFPurgeBeforeSpeak);
}
/// <summary>
/// Pauses the file.
/// </summary>
public void StopFile()
{
try
{
spFile.ISpStream_Close();
}
catch (Exception)
{
}
}
/// <summary>
/// 恢复
/// </summary>
public void Resume()
{
if (_voice != null)
_voice.Resume();
}
}
/// <summary>
///语音播报状态
/// </summary>
public enum VoiceStatus
{
/// <summary>
/// The play
/// </summary>
Play,
/// <summary>
/// The ready
/// </summary>
Ready,
/// <summary>
/// The pause
/// </summary>
Pause,
}
}
但真的运用时,还需要支持循环播放,以及播放状态。
private bool _isLoopAudioFile; // 是否循环播放声音文件
private bool _isLoopSpeech; //循环语音
private VoiceStatus speakStatus=VoiceStatus.Ready;
private bool IsStopPlayFile; //是否停止
private bool IsStopPlayVoice;
/// <summary>
/// 播放文件结束
/// </summary>
public event EventHandler PlayFileComplete;
/// <summary>
/// 播放文件开始
/// </summary>
public event EventHandler PlayFileStart;
/// <summary>
/// 播放文件结束
/// </summary>
public event EventHandler PlayAudioComplete;
/// <summary>
/// 播放文件开始
/// </summary>
public event EventHandler PlayAudioStart;
/// <summary>
/// 播放语言
/// </summary>
/// <param name="voiceContent">Content of the voice.</param>
public void SpeechVoice(string voiceContent)
{
IsStopPlayVoice = false;
if (speakStatus == VoiceStatus.Play) return; //正在播放就返回
Speaker.Resume();
Action invoke = () =>
{
OnPlayAudioStart();//触发开始播放事件
speakStatus = VoiceStatus.Play;
Speaker.Speak(voiceContent);
};
invoke.BeginInvoke(VoiceCallback, invoke);
}
private void VoiceCallback(IAsyncResult ar)
{
var ac = ar.AsyncState as Action;
if (ac != null)
{
try//原dll不能多次停止 所以加了try catch 和状态判断
{
if ((IsLoopSpeech) && !IsStopPlayVoice)
{
//一次播放结束之后如果是循环播放 就继续播放
ac.BeginInvoke(VoiceCallback, ac);
}
else
{
speakStatus = VoiceStatus.Pause;
//触发停止事件
OnPlayAudioComplete(this, new EventArgs());
ac.EndInvoke(ar);
}
}
catch (Exception)
{
}
}
}
//以下同理
/// <summary>
/// 暂停播放
/// </summary>
public void StopSpeechVoice()
{
if (IsStopPlayVoice) return;
IsStopPlayVoice = true;
speakStatus = VoiceStatus.Pause;
Action invoke = () => Speaker.StopVoice();
invoke.BeginInvoke(null, invoke);
OnPlayAudioComplete(this, new EventArgs());
}
/// <summary>
/// 停止播放声音文件
/// </summary>
public void StopPlayer()
{
if (IsStopPlayVoice) return;
IsStopPlayFile = true;
speakStatus = VoiceStatus.Pause;
Speaker.PauseFile();
OnPlayFileComplete(this, new EventArgs());
}
/// <summary>
/// 播放声音文件
/// </summary>
public void PlayAudioFile()
{
player = new SoundPlayer { SoundLocation = _audioFile.FilePath };
if(speakStatus==VoiceStatus.Play) return;
IsStopPlayFile = false;
if (File.Exists(_audioFile.FilePath))
{
Action invoke = () =>
{
OnPlayFileStart();
speakStatus = VoiceStatus.Play;
Speaker.PlaySound(_audioFile.FilePath);
};
invoke.BeginInvoke(Callback, invoke);
}
}
/// <summary>
/// Called when [play start].
/// </summary>
public void OnPlayFileStart()
{
var handler = PlayFileStart;
if (handler != null) handler(this, EventArgs.Empty);
}
/// <summary>
/// Called when [play start].
/// </summary>
public void OnPlayAudioStart()
{
var handler = PlayAudioStart;
if (handler != null) handler(this, EventArgs.Empty);
}
/// <summary>
/// Called when [play complete].
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
public void OnPlayAudioComplete(object sender, EventArgs e)
{
EventHandler handler = PlayAudioComplete;
if (handler != null) handler(this, EventArgs.Empty);
}
/// <summary>
/// Called when [play complete].
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
public void OnPlayFileComplete (object sender, EventArgs e)
{
EventHandler handler = PlayFileComplete;
if (handler != null) handler(this, EventArgs.Empty);
}
/// <summary>
/// Callbacks the specified ar.
/// </summary>
/// <param name="ar">The ar.</param>
private void Callback(IAsyncResult ar)
{
var ac = ar.AsyncState as Action;
if (ac != null)
{
OnPlayFileComplete(this,new EventArgs());
try
{
if ((IsLoopAudioFile)&& !IsStopPlayFile)
{
ac.BeginInvoke(Callback, ac);
}
else
{
speakStatus = VoiceStatus.Pause;
ac.EndInvoke(ar);
}
}
catch (Exception)
{
}
}
}
语音播放小Demo:SpeechVoice
本文链接:SpeechLib 语音播报,转载请注明。
http://www.cnblogs.com/yydcdut/p/3860460.html排序(选择、希尔、二分插入) - 我爱物联网 选择排序法 第1趟,在待排序记录r[1]~r[n]中选出最小的记录,将它与r[1]交换;第2趟,在待排序记录r[2]~r[n]中选出最小的记录,将它与r[2]交换;以此类推,第i趟在待排序记录r[i]~r[n]中选出最小的记录,将它与r[i]交换,使有序序列不断增长直到全部排序完毕。初始序列:{ 4...2014-07-27T10:42:00Z2014-07-27T10:42:00Z我爱物联网http://www.cnblogs.com/yydcdut/
选择排序法
第1趟,在待排序记录r[1]~r[n]中选出最小的记录,将它与r[1]交换;第2趟,在待排序记录r[2]~r[n]中选出最小的记录,将它与r[2]交换;以此类推,第i趟在待排序记录r[i]~r[n]中选出最小的记录,将它与r[i]交换,使有序序列不断增长直到全部排序完毕。
初始序列:{ 49 27 65 97 76 12 38 }
第1趟:12与49交换:12 { 27 65 97 76 49 38 }
第2趟:27不动:12 27 { 65 97 76 49 38 }
第3趟:65与38交换:12 27 38 { 97 76 49 65 }
第4趟:97与49交换:12 27 38 49 { 76 97 65 }
第5趟:65与76交换:12 27 38 49 65 { 97 76 }
第6趟:97与76交换:12 27 38 49 65 76 97 完成
代码
public static void main(String[] args) {
int[] i = { 1, 5, 6, 12, 4, 9, 3, 23, 39, 403, 596, 87 };
System.out.println("结果:");
xuanZe(i);
System.out.println();
}
// 选择排序算法
public static void xuanZe(int[] x) {
for (int i = 0; i < x.length; i++) {
int lowerIndex = i;
// 找出最小的一个索引
for (int j = i + 1; j < x.length; j++) {
if (x[j] < x[lowerIndex]) {
lowerIndex = j;
}
}
// 交换
int temp = x[i];
x[i] = x[lowerIndex];
x[lowerIndex] = temp;
}
for (int i : x) {
System.out.print(i + " ");
}
}
}
时间复杂度为O(N2) 。
希尔排序
对于n个元素的数组,假设增量为 h:
第一趟 : 从第1个元素开始,每隔h取一个元素,那么最后可以得到n/h个元素,一边取,一边通过直接插入将这h个元素排序
第二趟 : 从第2个元素开始,每隔h取一个元素,跟第一趟一样。
...
第h趟 : 从第h个元素开始,每隔h取一个元素,跟第一趟一样。
(此时,整个数组还不是有序的)
然后,减少h的值,重复上面的操作,直到h减小为1,排序完成。
代码
int len = nums.length / 2;
while (len >=1) {
for (int i = 0; i < len; i++) {
// 直接插入排序对分组进行排序
for (int k = i; k < nums.length-len; k +=len) {
int j = k + len;
int temp = nums[j];
while (k >= 0 && nums[k] > temp) {
nums[j] = nums[k];
k -= len;
j -= len;
}
nums[j] = temp;
}
}
len = len/2;
}
}
时间复杂度是O(N*lgN)。
二分插入排序
二分查找插入排序的原理:是直接插入排序的一个变种,区别是:在有序区中查找新元素插入位置时,为了减少元素比较次数提高效率,采用二分查找算法进行插入位置的确定。
代码
public static void main(String args[])
{
int array[]={49,38,65,97,76,13,27};
binarySort(array,array.length);
System.out.println(Arrays.toString(array));
}
//二分查找
public static int binarySearch(int array[],int low,int high,int temp)
{
int mid=0;
while(low<=high)
{
mid=(low+high)/2;
if(array[mid]<temp&&temp<=array[mid+1])
return (mid+1);
else if(array[mid]<temp)
low = mid + 1;
else
high = mid -1;
}
return high;
}
//二分排序
public static void binarySort(int array[],int size)
{
int i,j,k,temp;
for(i=1;i<size;i++)
{
temp=array[i];
if(array[i]<array[0])
k=0;
else
k = binarySearch(array,0,i,temp);
for(j=i;j>k;j--)
{
array[j]=array[j-1];
}
array[k]=temp;
System.out.println(Arrays.toString(array));
}
}
}
时间复杂度为O(N2) ;空间复杂度为O(1)。
我是天王盖地虎的分割线
本文链接:排序(选择、希尔、二分插入),转载请注明。
http://www.cnblogs.com/fnng/p/3871712.htmlRobot Framework自动化测试(一)---第一个脚本 - 虫师 最近工具中用Robot Framework框架来做自动化,。学习记录是我的习惯,所以,这里记录总结,编写第一个脚本。2014-07-27T10:18:00Z2014-07-27T10:18:00Z虫师http://www.cnblogs.com/fnng/
最近工具中用Robot Framework框架来做自动化,所以,花时间学习了一下。
=======所需环境===================
Python:
https://www.python.org/
RF框架是基于python 的,所以一定要有python环境。
Robot framework :
https://pypi.python.org/pypi/robotframework/2.8.5
这个不是解释了,RF框架。虽然在做基于UI的自动化时,它展现出来的很像QTP,我之前也以为它和QTP差不多,仔细了解你会发展它能做的事情还是很多的。就像初学selenium 者,会误以为selenium 就是selenium IDE。
wxPython :
http://www.wxpython.org/download.php
Wxpython 是python 非常有名的一个GUI库,因为RIDE 是基于这个库开发的,所以这个必须安装。
Robot framework-ride
https://pypi.python.org/pypi/robotframework-ride
RIDE就是一个图形界面的用于创建、组织、运行测试的软件。
Robot framework-selenium2library:
https://pypi.python.org/pypi/robotframework-selenium2library/1.5.0
RF-seleniumlibrary 可以看做RF版的selenium 库,selenium (webdriver)可以认为是一套基于web的规范(API),所以,RF 、appium 等测试工具都可以基于这套API进行页面的定位与操作。
----------------------
可以通过python 的pip工具包进行安装:
>pip install robotframework-selenium2library
如果初次接触上面的东西的话,觉得装的东西有点多。 如果之前有了解过python 或selenium的话就不会有这样的感觉。
================================================
在你安装好RF-ride之后,桌面就会生成一个RIDE图标。双击启动,界面如下:
下面我们就一步一步的创建第一条用例,至于细节不多解释,只是对RF框架写用例有个感性的认识。
创建测试项目
选择菜单栏file----->new Project
Name 输入项目名称。
Type 选择Directory。
创建测试套件
右键点击“测试项目”选择new Suite 选项
Name 输入项目名称。
Type 选择File。
创建测试用例
右键点击“测试项目”选择new Test Case
用例只需要输入用例name ,点击OK即可。
导入selenium2library库
因为RF框架编写基于web 的测试用例,所以,我们需要selenium 的库支持。所以,我们在使用的过程中需要加载selenium2library库。
在“测试套件”的Edit标签页,点击“Library”按钮,弹出输入框,Name输入:Selenium2Library ,点击OK 完。
如果导入的库显示为红色,表示导入的库不存在。如果是黑色则表示导入成功。
编写用例
下面就可以开始写我们的用例了,可是怎么写呢?我们可以通过按F5 快捷键来查询脚本的关键字。如果你接触过QTP 或 selenium IDE 等自动化工具的话,应该会有一些思路。
如上图,自动化脚本从打开浏览器开发,如上图,我想打开一个浏览器,想的是“open”为关键字进行搜索,结果找到了一个“Open Browser”的关键字,点击这个关键字,想显示它的用法和说明。
根据说明,我们来尝试创建这个打开浏览器的操作吧:
“Open Browser”变蓝了,说明它是一个合法的关键字,后面有一个方框是红色的,表示这个参数不能缺省的。通过说明信息中,我发现它需要一个url 地址是必填的,当然还需要指定browser (默认不填为 friefox)
更多关键的使用,请参考相关API 文档。这里不过多介绍。按照上面的方法。创建百度搜索用例如下:
运行测试用例
勾选当前需要运行的测试用例,点击工具栏运行按钮,如果只运行单个用例的话,也可以切换到用例的Run标签页,点击“start”按钮。
运行信息:
运行信息显示会生成三个文件:Output.xml、Log.html、Report.html
我们重点查看Log.html和Report.html ,Log.html更关注脚本的执行过程的记录,Report.html更关注脚本的执行结果的展示。
赶快打开你的测试报告看看效果吧!
================================================================================
错误:
command: pybot.bat --argumentfile c:\users\keikei\appdata\local\temp\RIDEama2ym.d\argfile.txt --listener D:\Python27\lib\site-packages\robotide\contrib\testrunner\TestRunnerAgent.py:52418 E:robot\测试项目
解决:
将“C:\Python27\Scripts ”添加到PATH环境变量中。命令提示符号查看,RF版本。提示pybot 不是内部命令,说明环境变量设置有问题。
本文链接:Robot Framework自动化测试(一)---第一个脚本,转载请注明。
http://www.cnblogs.com/pbendan/p/3871695.html如何建立一个子程序 - pBendan 首先我们要理解这里面的“子程序”指的是什么?在这里,“子程序”理解为一个函数比较合适,可以是一个执行特定功能的全局函数、可以是一个类里面的成员函数(注:《代码大全》里面之所以用“子程序”来表述,是因为它所讲述的规则与方法是与语言无关的,我们应该根据自己实际编程中所使用的语言来自行定义“子程序”),....2014-07-27T09:56:00Z2014-07-27T09:56:00ZpBendanhttp://www.cnblogs.com/pbendan/
首先我们要理解这里面的“子程序”指的是什么?在这里,“子程序”理解为一个函数比较合适,可以是一个执行特定功能的全局函数、可以是一个类里面的成员函数(注:《代码大全》里面之所以用“子程序”来表述,是因为它所讲述的规则与方法是与语言无关的,我们应该根据自己实际编程中所使用的语言来自行定义“子程序”),也可以这样理解“子程序”,它是完成某一特定功能的一个小单元,其粒度比模块要小,而比数据类型、结构控制语句要大。
综合《代码大全》里面的介绍和自己的理解,将“如何建立一个子程序”分为三个步骤:1. 之间的准备工作;2. 建立操作;3. 检查及测试。(其实这是放之四海而皆准的普遍规则,做任何事情只要遵守这三条都不会有太大问题的,在这里算是投机取巧了,主要是为了方便理解和记忆)
1. 之前的准备工作
问题引发设计,如果没有需求我们就不需要去设计一个“子程序”啦。那么我们首先就要明确地定义出这个“问题”啦,一般情况下,这个“问题”在整个的程序框架中会有所提及,而如何那时问题定义比较清晰的话,我们的准备工作可以几乎省去。不管怎么样,我们都要明确几个问题:
- 将要设计的“子程序”要完成什么任务;
- “子程序”主要应该在哪些场合,是为某个模块定义的功能,还是适用于整个系统;
- “子程序”的输入、输出分别是什么;
- 建立这个“子程序”需要与系统其他哪些部分发生关系,需要建立新的数据类型和算法么;
明确这些以后,我们的准备工作就做完了,那么最后为我们的“子程序”起一个清晰、直观、简介的名字吧!
2. 建立操作
建立操作的第一步,也是大部分程序员忽视的一个步骤(《代码大全》里面这样说的啊!)——使用PDL(program design language)语言来精确描述“子程序”所执行的操作。
这一操作是相对于使用具体程序语言编写“子程序”更抽象、更上一层的操作步骤,抽象并不意味着复杂,反而会更加简单清晰,以便使后续的编码、检查、调试操作更容易。因为使用PDL语言会带来以下好处:
- 使用自然语言而不是编程语言来描述“子程序”操作,使程序员不必受具体编程语言的语法约束,而将精力放在设计上;
- 容易查找错误,即使查找出错误,修改的代价也比较小;
- 如果PDL语言描述得比较得当,那么编码工作就是一个简单的翻译工作;
- 维护起来比较方便,PDL语言为“子程序”提供了清晰的注释信息;
确认PDL描述没有问题后,可以着手编写程序代码了,如果PDL语言对于“子程序”描述的足够准确并得当的话,那么编写代码的过程可能是一个简单、机械的工作,程序员应该很快能完成“子程序“设计。
但是,并不是说设计出一个行为良好的PDL描述就晚上大吉了,有些情况下,PDL描述看似完美,但将它转化为代码时会出现很多错误,所以这是要不检查是不是代码本身出错,或者是PDL语言描述还存在不足,如果是的话,重新用PDL语言描述。
3. 检查及测试
在编译“子程序”之前,程序员有必要进行一些检查工作:
- “子程序”是不是按照最初的需求?
- 完成的任务是不是明确?
- 可以能现的意外情况?如何解决?
- “子程序”接口设计、实现得合适么?
- “子程序”会不会有明显的语法错误?
确认检查无误以后,程序员可以对“子程序”实施测试了,《代码大全》里面建议我们不要急于对“子程序”实施编译,并试图通过编译来查找程序中的错误,如果某次编译出现了错误,那么程序员往往急切万分得修改以试下次编译不要出错,这并不是发现并排除错误的正确方法。《大码大全》推荐我们这样做:
- 自己在心里逐行执行“子程序”,通过自己的“计算机系统”将程序执行一下,试图找出错误并更正;
- 编译“子程序”,消除所有错误和警告;
- 逐行调试程序,监视程序运行结果,必要情况下编写一些辅助代码帮助程序测试;
总结:
阅读《代码大全》关于“子程序”建立这部分,确实对程序员的编程工作有一定启发,也确实指出了一些有效的方法和容易忽略的问题。下面结合书中介绍的内容和自己的理解谈一下自已的认识:
- 首先,我们应该清楚地认识到,书中所谈应该是大而全的方法论,实际执行的时候可以根据具体情况做酌情取舍。书中所列出的条条目目加起来总共得几十条,如果我们所写的“子程序”是一个功能简单、逻辑清晰的操作,确实没有必要按照书中所写的来逐步进行,这样反而会降低效率。
- 书中介绍使用PDL语言说要使用自然语言,避免设计具体编程语言的引入,这点我认为过于刻板,PDL语言之所以没有确定语法格式,就是为了可以抽象地、明了地描述“子程序”操作,所以加入具体编程语言的风格、语法未尝不可,毕竟最终我们是要使用具体的编程语言来实现功能,加入这些具体编程语言的语法、风格反而使描述效果更贴切。
本文链接:如何建立一个子程序,转载请注明。
http://www.cnblogs.com/insus/p/3869575.htmlASP.NET开发,简化与封装 - Insus.NET 微软的ASP.NET的开发,就是面向对象的编程,当然前端也能体验至面向对象的话,使用Web控件也必须的。任一控件,我们均可以在后端.aspx.cs或.aspx.vb程序中new一个对象出来。很多场合里,在开发ASP.NET开发中,后端与前端交互,我们使用控件确实能方便与快捷互通。本篇所涉及的内容以h...2014-07-27T09:50:00Z2014-07-27T09:50:00ZInsus.NEThttp://www.cnblogs.com/insus/
微软的ASP.NET的开发,就是面向对象的编程,当然前端也能体验至面向对象的话,使用Web控件也必须的。
任一控件,我们均可以在后端.aspx.cs或.aspx.vb程序中new一个对象出来。
很多场合里,在开发ASP.NET开发中,后端与前端交互,我们使用控件确实能方便与快捷互通。
本篇所涉及的内容以html markup标签与javascript(或jQuery)无关,因为演示的是Web控件应用。Insus.NET经常会针对开发的应用程序,写一些常用或是特定的对象或是控件。在一起开发的团队的能句方便用使用,能得到客户需求的功能与效果。
开发这些对象或是控件,也并非一开始就能写得出来,均是经过一系列编程之后,再次review或是优化的结果。
举个例子来说明,先在SQL Server数据库中,创建一个数据表,如[dbo].[UserInfo]:
对这个表,添加几笔记录:
写一个存储过程,是获取刚才添加的所有记录:
OK,数据库层已经设计好了,我们去ASP.NET网站写程序,需要把这些数据呈现于网页上。
逻辑层就使用《ASP.NET开发,从二层至三层,至面向对象 (3)》 http://www.cnblogs.com/insus/p/3826706.html 将来的的博文演示中也会使用它。
在App_Code目录下,创建一个以数据表名相同的一个类(对象):
新建一个网页.aspx,用它来呈现数据表的数据,直接拉数据控件GridView:
在.aspx.cs代码中,实现给GridView控件绑定数据:
浏览一下:
以上的实现,相信很多学习与开发asp.net的人都会,太简单了,根本不值得一说一提一写。
问题来了,最后一列[Sex],性别具体1代码是男还是女,反之亦如此。能否以更友好清晰的表达来表现?下面列几种网友用常的方法:
第一种方法,写一个小函数去替换:
写好函数之后,在.aspx的Gridview控件,需要修改,不能使用自动产生列了:
预览结果:
第二种方法,较为复杂一些,不过也较常使用的,在.aspx分三步来修改GridView:
下面是实现OnRowDataBound="GridView1_RowDataBound":
此种方法预览结果跟第一种方法是一样的。
第三种方法,是Insus.NET使用的方法。出现这样的问题,多数是多表关联,一张表某些字段是另一个表的外键值。呈现时,均是键值或是代号。但实际是需要显示实际意义的字段。
解决它,可以创建一个表,是key与value对应的表:
然后修改一下[dbo].[usp_UserInfo_GetAll]存储过程:
在asp.net程序中,在后端无需修心任代码。
在.aspx页中的GridView中,仅引用另一个字段名:
三种方法,均演示完成了。哪一种方法,你觉得好,就使参考哪一种。
下面有新的要求,客户要求,将显示的0或1的信息,使用一张图片来替代。
这要求不难,用文字“男”或“女”还是用图片去替代0或1的方法,均是一样的。用Image去实现就是了。下面Insus.NET来演示一番:
实现之前,得先准备好两张图片,放于站点某一目录之下。
上面有三个步骤标记1至标记3。
编写一个私有函数:
然后就可以写 OnRowDataBound="GridView1_RowDataBound"事件了:
改好了,浏览看看效果:
上面图片替换方法,是通用大家所使用的方法。不过Insus.NET的方法,却不是这样来做。Insus.NET需要封装 GetSexImagePath(string num)或是GetSex(string num)函数
另外,由这地方的呈现在一个网站中,也许不止一次(网页)呈现,在管理员后台,会员后台,前端面向所有用户呈现的网页均要此样式呈现。
因此,可以写成一个控件即可,先在App_Code创建一个类别SexImg:
有三个public的property以及override方法RenderContents()。重要的一点,是继承了WebControl类。
接着,我们打开web.config文件,注册一下上面我们创建好的控件:
现在的问题是怎样使用这个控件呢?跟Web控件一样,没有什么分别:
在GridView控件内,使用刚才写好的控件,直接指写三个property。
在.aspx.cs内,无需写什么代码:
浏览的效果,跟上面的一样:
最后一个演示,学会了封装与创建自己定义控件。Insus.NET虽然只是依性别来举列,实现开发时,可会遇上很多类别,目录等相关的。
方法掌握了,相似的功能可以轻易实现。
本文链接:ASP.NET开发,简化与封装,转载请注明。
http://www.cnblogs.com/barros/p/3871652.htmlPDF解决方案(3)--PDF转SWF - 铁皮鸭子 相关专题链接PDF解决方案(1)--文件上传PDF解决方案(2)--文件转PDFPDF解决方案(3)--PDF转SWF前言:上一篇中介绍了上传的文件转PDF,主要是一些常用的文档格式转换为PDF;这一篇主要介绍如何把PDF转换为SWF,为下一步文件在线浏览做准备;PDF在线浏览的主要以下几种方式:1...2014-07-27T09:36:00Z2014-07-27T09:36:00Z铁皮鸭子http://www.cnblogs.com/barros/
相关专题链接
PDF解决方案(1)--文件上传
PDF解决方案(2)--文件转PDF
PDF解决方案(3)--PDF转SWF
前言:上一篇中介绍了上传的文件转PDF,主要是一些常用的文档格式转换为PDF;这一篇主要介绍如何把PDF转换为SWF,为下一步文件在线浏览做准备;
PDF在线浏览的主要以下几种方式:
1、PDF浏览器插件
这种方式依赖PDF阅读器厂商提供的浏览器插件,主流的PDF阅读器如Adobe、福昕在安装本地客户端的时候都会附带安装这种控件,直接把本地的PDF文件拖到浏览器即可看到效果,如下:
如上图所示,不同阅读器的插件功能差异较大,不同的浏览器效果也有不同,无法保证有一个统一的展现效果,而且这种方式必须依赖客户机安装上述软件,有很大的局限性;
2、使用Jquery插件
这种方式大部分都依赖html5技术,鉴于目前国内的浏览器市场占比,这种方式显示极其小众,这里就不再介绍了,有兴趣的可以参考:8个实现在线浏览PDF文件的实用jQuery插件
3、通过转换为SWF来实现
虽然html5技术正在兴起,但在目前flash依然受支持比较广泛的技术,通过Flash控件也保证了不同浏览器相同的展示效果,一些开源的前端控件也提供很好的SWF在线浏览体验,所以这里我们就重点介绍这种实现方式;
PDF转SWF
这里采用开源软件swftools,它支持把PDF、图片、声音等文件转换为SWF文件,并且提供windows和linux版本,可以在linux环境部署;
swftools下载地址:http://www.swftools.org/download.html
下载安装后就可以通过命令行的方式进行调用,pdf2swf提供很多的参数来进行配置,常用的如-p设置打开pdf的密码,-z使用Flash 6的zlib压缩机制,-s设置更信息的参数(可以通过pdf2swf -s help来获取更详细的参数信息),-o输出swf文件位置等:
下面就通过Java代码启动系统进程的方式来调用pdf2swf命令进行转换,Java在启动进程成功后就会返回,而实际上我们希望等待文件转换成功后程序再返回,此时就需要用到Process
类了,在Java文档提到“ProcessBuilder.start() 和 Runtime.exec 方法创建一个本机进程,并返回 Process 子类的一个实例,该实例可用来控制进程并获得相关信息”,利用这一点我们可以获取文件转换的信息并可以让程序再系统进程结束后再返回,代码中的dealWith方法就是对Process,详细转换代码如下:
* 把pdf转换为swf
* @param pdfPath pdf文件路径
* @throws Exception
*/
public static boolean convert2SWF(String pdfPath) throws Exception
{
String swfFile = pdfPath.substring(0, pdfPath.lastIndexOf("."))
+ ".swf";
File outFile = new File(swfFile);
if (outFile.exists())
{
return true;
}
File pdfFile = new File(pdfPath);
if (!pdfFile.exists())
{
return false;
}
//创建调用swftools命令list
List<String> command = new ArrayList<String>();
command.add("c:\\SWFTools\\pdf2swf");//pdf2swf命令路径
command.add("-z");
command.add("-s");
command.add("flashversion=9");
command.add("-s");
command.add("languagedir=C:\\xpdf\\xpdf-chinese-simplified");//添加xpdf解决转换时出现的字符集问题
command.add(pdfPath);
command.add("-o");
command.add(swfFile);
/**
* java启动系统进程时,启动成功后就直接返回了,并不会等待系统进程执行结束,这里我们需要等待系统进程调用结束后java方法再返回
*/
try
{
//创建系统进程
ProcessBuilder processBuilder = new ProcessBuilder();
processBuilder.command(command);//设置系统进程要执行的系统程序和参数
Process process = processBuilder.start();//使用此进程生成器的属性启动一个新进程
dealWith(process);
try
{
process.waitFor();// 等待子进程的结束,子进程就是系统调用文件转换这个新进程
} catch (InterruptedException e)
{
throw new Exception(e.getMessage());
}
} catch (IOException e)
{
throw new Exception(e.getMessage());
}
return true;
}
/**
* 处理进程的IO防止出现阻塞、死锁等情况
* @param pro
* @throws
*/
private static void dealWith(final Process pro)
{
// 下面是处理堵塞的情况
try
{
//启动单独线程来清空pro.getInputStream()的缓冲区
new Thread() {
public void run()
{
BufferedReader br1 = new BufferedReader(
new InputStreamReader(pro.getInputStream()));
try
{
String text;
while ((text = br1.readLine()) != null)
{
System.out.println(text);
}
} catch (IOException e)
{
e.printStackTrace();
}
}
}.start();
} catch (Exception e)
{
e.printStackTrace();
}
try
{
//不要忘记处理出理时产生的错误信息,不然会堵塞不前的
new Thread() {
public void run()
{
BufferedReader br2 = new BufferedReader(
new InputStreamReader(pro.getErrorStream()));
String text;
try
{
while ((text = br2.readLine()) != null)
{
System.err.println(text);
}
} catch (IOException e)
{
e.printStackTrace();
}
}
}.start();
} catch (Exception e)
{
e.printStackTrace();
}
}
上面的代码中在创建swftools命令行时使用了参数 -s languagedir=C:\\xpdf\\xpdf-chinese-simplified,这里的地址对应的是另外一个工具xpdf,swftools本身会支持一些字体,但是当pdf中存在它不支持的字体时就会报错,如下图,此时我们就需要用到xpdf,Xpdf 是一个开源的PDF文件浏览器,通过语言包可以很好的支持中文,并且提供了windows和linux版本,可以跨平台部署
xpdf:ftp://ftp.foolabs.com/pub/xpdf/xpdfbin-win-3.04.zip
语言包:ftp://ftp.foolabs.com/pub/xpdf/xpdf-chinese-simplified.tar.gz
xpdf解压放到C盘根目录即可,语言包解压放到xpdf的根目录中,修改xpdf-chinese-simplified的文件add-to-xpdfrc,添加需要的字体目录和要展示的具体字体,然后在swftools命令中通过参数指定语言包目录即可
相关文件下载:
PDF解决方案demo: http://pan.baidu.com/s/1i3mmwux
swftools、xpdf:http://pan.baidu.com/s/1dDu1Yoh(注:解压放在C盘根目录即可)
本文链接:PDF解决方案(3)--PDF转SWF,转载请注明。
http://www.cnblogs.com/Enumz/p/3871651.html算法总结之拓扑排序 - Enumz 拓扑排序1.一般应用 拓扑排序常用来确定一个依赖关系集中,事物发生的顺序。例如,在日常工作中,可能会将项目拆分成A、B、C、D四个子部分来完成,但A依赖于B和D,C依赖于D。为了计算这个项目进行的顺序,可对这个关系集进行拓扑排序,得出一个线性的序列,则排在前面的任务就是需要先完成的任务。2.实现.....2014-07-27T09:35:00Z2014-07-27T09:35:00ZEnumzhttp://www.cnblogs.com/Enumz/
拓扑排序
1.一般应用
拓扑排序常用来确定一个依赖关系集中,事物发生的顺序。例如,在日常工作中,可能会将项目拆分成A、B、C、D四个子部分来完成,但A依赖于B和D,C依赖于D。为了计算这个项目进行的顺序,可对这个关系集进行拓扑排序,得出一个线性的序列,则排在前面的任务就是需要先完成的任务。
2.实现的基本方法
(1)从有向图中选择一个没有前驱(即入度为0)的顶点并且输出它.
(2)从网中删去该顶点,并且删去从该顶点发出的全部有向边.
(3)重复上述两步,直到剩余的网中不再存在没有前趋的顶点为止.
3.C++版本实现
2 /*假设不相连的边的Edge==INT_MAX*/
3 void Topo()
4 {
5 sort(Edge+1,Edge+N+1);
6 for (int i=1; i<=N; i++)
7 {
8 int j;
9 for (j=1; j<=N; i++) //vis为标记数组,标记是否已经存在在Topo数组中
10 if (!vis[j]&&!in[j]) //in数组表示的下标顶点的入度
11 {
12 Topo[i]=j;
13 vis[j]=1;
14 break;
15 }
16 for (int k=1;k<=N;k++) //将与j点相连的顶点的入度刷新
17 if (Edge[j][k]!=INT_MAX)
18 in[k]--;
19 }
20 }
4.反向建图
拓扑排序并不一定唯一,有时会要求顶点数值大的尽量在前,这个时候应该反向建图,再进行拓扑排序,来保证更多小数值顶点在后面。(贪心思想?)
Eg:HDU 4857 POJ 3687(反向建图+拓扑排序)
5.优化
拓扑排序每次选择一个顶点进入序列后,要更新所有与这个顶点相连的顶点的入度。而上面的代码全是把所有的顶点都遍历了一边。
这里可以使用邻接表或者链式前向星这一类数据结构,来记录图的信息,可以做到只遍历与该顶点相连的顶点。
6.优先队列实现
2 void Topo()
3 {
4 priority_queue<int> que;
5 for (int i=1; i<=N; i++) //将入度为零的顶点压入队列
6 if (dis[i]==0)
7 que.push(i);
8 int p=N+1;
9 while (!que.empty())
10 {
11 int tmp=que.top();
12 que.pop();
13 topo[--p]=tmp; //存入topo数组
14 int z=first[tmp];
15 while (z!=-1) //邻接表遍历与tmp相连的顶点
16 {
17 dis[end[z]]--; //入度减一
18 if(!dis[end[z]]) que.push(end[z]);
19 z=next[z];
20 }
21 }
22 }
本文链接:算法总结之拓扑排序,转载请注明。
http://www.cnblogs.com/f1194361820/p/3871633.html Spring源码阅读:IOC容器的设计与实现(二)——ApplicationContext - 螺 丝 钉 上一主题中,了解了IOC容器的基本概念,以及BeanFactory的设计与实现方式,这里就来了解一下ApplicationContext方式的实现。ApplicationContext 在Spring的参考文档中,为啥要推荐使用ApplicationContext?它能给我们的应用带来什么好处呢?作...2014-07-27T09:25:00Z2014-07-27T09:25:00Z螺 丝 钉http://www.cnblogs.com/f1194361820/
上一主题中,了解了IOC容器的基本概念,以及BeanFactory的设计与实现方式,这里就来了解一下ApplicationContext方式的实现。
ApplicationContext
在Spring的参考文档中,为啥要推荐使用ApplicationContext?它能给我们的应用带来什么好处呢?作为BeanFactory的实现之一,它又是如何设计的?在SpringMVC中使用的WebApplictionContext\XmlApplicationContext与之有何关联?
ApplicationContext与BeanFactory:
从类图中,可以明显的看出ApplicationContext是作为BeanFactory的子接口呈现的。但是它又在BeanFactory的基础上添加了新的特性:MessageSource(消息支持,例如国际化),ApplicationEventPublisher(事件支持),ResourcePatternResolver(解析资源)。
AbstractApplicationContext
在ApplicationContext的实现类AbstractApplicationContext已经基本上实现了ApplicationContext的功能。
* Abstract implementation of the {@link org.springframework.context.ApplicationContext}
* interface. Doesn't mandate the type of storage used for configuration; simply
* implements common context functionality. Uses the Template Method design pattern,
* requiring concrete subclasses to implement abstract methods.
*
* <p>In contrast to a plain BeanFactory, an ApplicationContext is supposed
* to detect special beans defined in its internal bean factory:
* Therefore, this class automatically registers
* {@link org.springframework.beans.factory.config.BeanFactoryPostProcessor BeanFactoryPostProcessors},
* {@link org.springframework.beans.factory.config.BeanPostProcessor BeanPostProcessors}
* and {@link org.springframework.context.ApplicationListener ApplicationListeners}
* which are defined as beans in the context.
*
* <p>A {@link org.springframework.context.MessageSource} may also be supplied
* as a bean in the context, with the name "messageSource"; otherwise, message
* resolution is delegated to the parent context. Furthermore, a multicaster
* for application events can be supplied as "applicationEventMulticaster" bean
* of type {@link org.springframework.context.event.ApplicationEventMulticaster}
* in the context; otherwise, a default multicaster of type
* {@link org.springframework.context.event.SimpleApplicationEventMulticaster} will be used.
*
* <p>Implements resource loading through extending
* {@link org.springframework.core.io.DefaultResourceLoader}.
* Consequently treats non-URL resource paths as class path resources
* (supporting full class path resource names that include the package path,
* e.g. "mypackage/myresource.dat"), unless the {@link #getResourceByPath}
* method is overwritten in a subclass.
*/
上面的描述的大致意思是:
1、这个类是对ApplicationContext的抽象的实现,并不会指定使用哪种bean definition 配置策略。它完成了各种ApplicationContext的通用的实现。在设计这个类时,使用了模板方法设计模式,有的方法需要在具体的子类中实现。
2、与BeanFactory相比,ApplicationContxt中可以把BeanFactoryPostProcessor、BeanPostProcessor、ApplicationListener作为一个普通的Bean定义。
3、在ApplicationContext中也可以使用MessageSource,使用它时用的name是"messageSource"。同时也可以使用ApplicationEventMulticaster(name是"applicationEventMulticaster"),如果不指定,默认使用的是:SimpleApplicationEventMulticaster。
4、通过继承DefaultResourceLoader方式实现了资源文件的加载。
接下来看看这个类的继承关系:
从类图中可以看出AbstractApplicationContext主要分为两支: ·AbstractRefreshWebApplicationContext(主要用于Web环境下)
·AbstractXmlApplicationContext(主要用于非Web环境下)
对于AbstractRefreshWebApplicationContext,从配置类别上分为两类:使用Annotation配置或者使用XML文件配置。
对于AbstractXmlApplicationContext,采用的是XML作为Bean定义的配置文件,它的两个子类的区别是从哪里去加载bean定义件。
下一节,就可以看看ApplicationContext是如何初始化IOC容器的。
本文链接:Spring源码阅读:IOC容器的设计与实现(二)——ApplicationContext,转载请注明。
http://www.cnblogs.com/senlie/p/3871617.html大数据技术 —— MapReduce 简介 - senlie zheng 本文为senlie原创,转载请保留此地址:http://www.cnblogs.com/senlie/1.概要很多计算在概念上很直观,但由于输入数据很大,为了能在合理的时间内完成,这些计算必须分布在数以百计数以千计的机器上。例如处理爬取得到的文档、网页请求日志来计算各种衍生数据,如倒排索引,网页文档...2014-07-27T09:16:00Z2014-07-27T09:16:00Zsenlie zhenghttp://www.cnblogs.com/senlie/
本文为senlie原创,转载请保留此地址:http://www.cnblogs.com/senlie/
1.概要
很多计算在概念上很直观,但由于输入数据很大,为了能在合理的时间内完成,这些计算
必须分布在数以百计数以千计的机器上。例如处理爬取得到的文档、网页请求日志来计算
各种衍生数据,如倒排索引,网页文档的各种图结构表示,从每个主机上爬取的文档数,
在某一天最频繁的查询的集合。
MapReduce 是为处理和生成大数据集的编程模式和相应的实现。
用户指定一个 map 函数来处理一个键值对来生成一个键值对的集合,
和一个 reduce 函数来合并具有相同中间键的实值。
例如,有大一堆文档,要统计里面每一个文档的出现的次数。可以这样写map 函数和 reduce 函数
//key: document name
//value: document contents
for each word w in value:
EmitIntermediate(w, '1');
reduce(String key, Iterator values):
//key: a word
//values: a list of counts
int result = 0;
for each v in values:
result += ParseInt(v);
Emit(AsString(result));
??疑问:map 返回的是一个 key/value ,为什么到了 resuce 这的输入却变成了 key/list of values ,这中间
发生了什么?
解答:
map 函数接受一个键值对(如上面例子中的文档名/文档内容)并产生一组键值对(单词/1)。在将这组
键值对传给 reduce 函数之前, MapReduce 库会组合所有具有相同键值的实值产生新的一组键/值(单词/次数)。
reduce 函数接受来自多个 map 函数产生的键值对,它们在被 reduce 函数处理前,会先被 MapReduce 库组合成
键/值列表(单词/次数列表)。下图解释了这一过程。
(声明:图来自实验室 adonis 同学的 seminar 展示ppt)
2.MapReduce 的执行的大概流程
通过将输入数据划分为 M 个分片, map 函数的调用分布在多台机器上,这些分片可同
不同的机器并行地处理。
通过将中间结果的键空间划分为 R 个分片, reduce 函数的调用分布在多台机器上。
下图展示了 MapReduce 操作的整个流程。
1). 客户程序中的 MapReduce 库首先将输入文件分成 M 个大小通常为 16MB 或者64MB 的分片。
然后开始在集群上的机器复制客户程序
2).其中有一个程序的备份是特殊的,它就是主节点。其它的是由主节点分配任务的从节点。
主节点有 M 个 map 任务和 R 个 reduce 任务要分配给那些空闲的从节点。
3).一个被分配了 map 任务的从节点从输入分片中读取内容,然后从输入中解析出键值对被传递给
用户定义的 map 函数,由它来产生中间结果的键值对并缓存在内存中
4).在内存中的键值对被周期性地写入到本地磁盘,通过分片函数被分成 R 个分片。
这些分片的位置被回传给主节点,由主节点告诉 reduce 从节点它们的位置
5).当 reduce 从节点被主节点告知分片的位置时,它从使用 RPC(remote procedure call) 去读取
那些缓存数据,当读完后,它会按键值进行排序,然后将有相同键值的键值对组合在一起,形成键/值列表
6).reduce 从节点遍历已经排序合并好了的中间数据,将每一个键/值列表对传递给客户定义的 reduce 函数。
reduce 函数返回的结果被添加到这个 reduce 从节点的结果文件中。
7).当所有 map 从节点和 reduce 从节点完成后,主节点唤醒客户程序。
如果 MapReduce 程序成功完成,结果文件被存储在 R 个输出文件中。
3.示例
这个示例统计了一组输入文件里每个单词的出现次数
//user's map function
class WordCounter : public Mapper{
public:
virtual void Map(const MapInput &input){
const string &text = input.value();
const int n = text.size();
for(int i = 0; i < n; ){
//忽略单词前空格
while(i < n && isspace(text[i])) i++;
//找到单词的结尾
int start = i;
while(i < n && !isspace(text[i])) i++;
if(start < i) Emit(text.substr(start, i - start), "1");
}
}
};
REGISTER_MAPPER(WordCounter); // 这个是干嘛用的??
//User's reduce function
class Adder : public Reducer {
// 这里不用加个 public 的关键字?
virtual void Reduce(ReduceInput *input){
//把有相同键值的数值加起来
int64 value = 0;
while(!input->done()){
value != StringToInt(input->value());
input->NextValue();
}
Emit(IntToString(value));
}
}
REGISTER_REDUCER(Adder);
int main(int argc, char **argv){
ParseCommandLineFlags(argc, argv);
MapReduceSpecification spec;
//把输入文件列表存入 "spec"
for(int i = 1; i < argc; i++){
MapReduceInput *input = spec.add_input();
input->set_format("text");
input->set_filepattern(argv[i]);
input->set_mapper_class("WordCounter");
}
//指定输出文件
MapReduceOutput *out = spec.output();
out->set_filebase("gfs/test/freq");
out->set_num_tasks(100);
out->set_format("text");
out->set_reducer_class("Adder");
//可选:在 map 节点中做部分和运算以节省带宽
out->set_combiner_class("Adder");
//调节参数:使用最多2000台机器,每个任务最多100MB内存
spec.set_machines(2000);
spec.set_map_megabytes(100);
spec.set_reduce_megabytes(100);
//开跑
MapReduceResult result;
if(!MapReduce(spec, &result)) abort();
//失败的时候 abort, 能运行在这里就是成功了。
return 0;
}
参考:
MapReduce: Simplified Data Processing on Large Clusters
本文链接:大数据技术 —— MapReduce 简介,转载请注明。
http://www.cnblogs.com/aexin/p/3871613.html04-1. 水仙花数(20) - aexin 水仙花数是指一个N位正整数(N>=3),它的每个位上的数字的N次幂之和等于它本身。例 如:153 = 13+ 53+ 33。 本题要求编写程序,计算所有N位水仙花数。输入格式:输入在一行中给出一个正整数N(3 2 3 int main() 4 { 5 int n; 6 scanf(...2014-07-27T09:12:00Z2014-07-27T09:12:00Zaexinhttp://www.cnblogs.com/aexin/
水仙花数是指一个N位正整数(N>=3),它的每个位上的数字的N次幂之和等于它本身。例 如:153 = 13 + 53+ 33。 本题要求编写程序,计算所有N位水仙花数。
输入格式:
输入在一行中给出一个正整数N(3<=N<=7)。
输出格式:
按递增顺序输出所有N位水仙花数,每个数字占一行。
输入样例:
3
输出样例:
153
370
371
407
2
3 int main()
4 {
5 int n;
6 scanf("%d", &n);
7
8 int i, low = 1, high; //low表示n位数的最小数,high表示n位数的最大数
9 for(i = 1; i < n; i++) {
10 low *= 10;
11 }
12 high = low * 10;
13 for(i = low; i < high; i++ ) { //从最小数到最大数逐一遍历
14 int t = i;
15 int sum = 0;
16 do {
17 int r = t % 10; //r为数的末尾数字
18 t /= 10;
19 int j, b = r;
20 for(j = 1; j < n; j++) { //让r自乘最后得到r的n次方
21 r *= b;
22 }
23 sum += r;
24 } while(t > 0);
25 if(sum == i) { //让r的n次方和i比较
26 printf("%d\n", i);
27 }
28 }
29
30 return 0;
31 }
本文链接:04-1. 水仙花数(20),转载请注明。
http://www.cnblogs.com/hook-haifeng/p/3871606.htmlthinkphp多表查询 - Mapleth 在学习thinkphp 的过程中,需要对多表进行操作,但是在实际过程中,总是遇到各种问题,所以写下这篇博文,作为自己的学习历程在操作过程中,两表查询都没有问题,但是三表查询就开始出现问题有以下三张表,分表为pl表(uid,content),user表(id,username),lyb表(uid,ti...2014-07-27T09:11:00Z2014-07-27T09:11:00ZMaplethhttp://www.cnblogs.com/hook-haifeng/
在学习thinkphp 的过程中,需要对多表进行操作,但是在实际过程中,总是遇到各种问题,所以写下这篇博文,作为自己的学习历程
在操作过程中,两表查询都没有问题,但是三表查询就开始出现问题
有以下三张表,分表为pl表(uid,content),user表(id,username),lyb表(uid,title)
多表查询操作有以下几种方法:
㈠视图模型(推荐)
定义视图模型,只需要继承Think\Model\ViewModel,然后设置viewFields属性即可
'pl' =>array('uid','rid','content'),
'user' =>array('id','username','_on'=>'pl.uid=user.id'),
'lyb' =>array('uid'=>'lid','content'=>'lyb_content','title','_on'=>'pl.uid=lyb.uid'), //如果表中有字段重名,可以通过=>设置别名,'uid'=>'lid'
);
视图查询:
视图查询和不同模型的查询一样,没有什么区别。
如果发现查询的结果存在重复数据,还可以使用group方法来处理。
㈡join
JOIN方法也是连贯操作方法之一,用于根据两个或多个表中的列之间的关系,从这些表中查询数据。
join通常有下面几种类型,不同类型的join操作会影响返回的数据结果。
- INNER JOIN: 如果表中有至少一个匹配,则返回行,等同于 JOIN
- LEFT JOIN: 即使右表中没有匹配,也从左表返回所有的行
- RIGHT JOIN: 即使左表中没有匹配,也从右表返回所有的行
- FULL JOIN: 只要其中一个表中存在匹配,就返回行
join方法可以支持以上四种类型:
同样是对以上三张表进行操作
->join('lyb on pl.uid = lyb.uid')
->join('user on pl.uid = user.id')
->field('user.username,lyb.title,pl.content')
->select();
㈢table
table方法也属于模型类的连贯操作方法之一,主要用于指定操作的数据表。
用法
一般情况下,操作模型的时候系统能够自动识别当前对应的数据表,所以,使用table方法的情况通常是为了:
- 切换操作的数据表;
- 对多表进行操作; $Model = D("pl")
->field('pl.content,user.username,lyb.title')
->table('pl,lyb,user')
->limit(10)
->select();注:table方法默认查询的是所有字段的值
本文链接:thinkphp多表查询,转载请注明。
http://www.cnblogs.com/jack-1900/p/3871607.html详解Android定位 - JackCho 移动互联时代,社交APP火热,应运而生大量基于LBS的兴趣点。本文将从三个方面讲解如何获取用户的实时位置。2014-07-27T09:11:00Z2014-07-27T09:11:00ZJackChohttp://www.cnblogs.com/jack-1900/
相信很多的朋友都有在APP中实现定位的需求,今天我就再次超炒冷饭,为大家献上国内开发者常用到的三种定位方式。它们分别为GPS,百度和高德,惯例先简单介绍下定位的背景知识。
什么是GPS定位、基站定位和Wi-Fi定位?
1、GPS定位:根据设备GPS芯片和GPS卫星实现定位,GPS定位在室内是不可以使用的。GPS定位精度和芯片本身以及实际使用环境有关,一般情况下,GPS定位精度在10m左右。
2、基站定位:根据设备获取的基站信息实现定位,基站定位精度一般不受使用环境影响,主要和基站的覆盖半径有关。基站定位服务精度目前在200m左右。
3、Wi-Fi定位:根据设备获取的Wi-Fi的信息进行定位,Wi-Fi定位精度一般不受使用环境影响,主要和Wi-Fi半径,密度有关。Wi-Fi定位精度目前在20m左右。
目前智能手机都内置了GPS芯片,相对应的各个手机系统厂商也开放了对外的GPS接口。但是由于GPS受外界因素影响比较大,一般的APP也都是室内使用,所以严重影响到了GPS搜星的数量。不过以前在外包公司接手过这么一个项目,主要面向室外的骑行爱好者使用,满足他们骑行路线的绘制。面对这样一个需求,GPS定位是最靠谱的选择了,但是伤不起的耗电量呀。
所以一般比较靠谱的方式,就是使用百度高德这些企业为开发者提供的成熟方案,可以选择混合定位的方式以应对APP复杂的实际使用情况。混合定位就是使用以上三种定位技术,选择最优的方式去获取当前的地理位置。下面来介绍下今天为大家带来的简单实践。
一、GPS定位
没有特别的业务需求,请慎用GPS定位,选用NETWORK_PROVIDER是个不错的选择。另外,大家有没有发注意到,通过GPS可以获取当前时间,在获取不到准确时间的时候是个不错的选择。
2、百度定位
百度定位的location里包含了很多的信息,足以应付我们工作的需要了。百度定位Jar包大小180Kb,相对来说还是可以接受的。百度的三种定位策略也正好是对应上述我们背景介绍的三种定位技术。
三、高德定位
高德定位回调的位置信息也是比较丰富的,而且依赖包大小140kb,一般我工作中定位都是用高德。
备注:通过Android系统的LocationManager去定位也是满靠谱的,室外需求使用GPS provider,室内使用Network provider;定位的经度和效率也是可以接受的,而且还不用依赖第三方包,不会增加程序的体积。但是很多业务情形下,我们不止想得到简单的经纬度信息,我们还需要城市、邮编、具体的位置等信息。
代码地址:https://github.com/JackCho/LocationDemo
如果觉得对你有所帮助,欢迎大家订阅我的微信公众账号——Android干货分享(ID:android_share)。下面是微信的二维码,为你提供及时高质的Android干货。
本文链接:详解Android定位,转载请注明。
http://www.cnblogs.com/justconnor/p/3871548.html数据字典生成工具 - justconnor 之前找的数据库字典生成工具基本上都依赖于 Office Com 组件,在不安装 Office的情况下无法使用。怒,于是自己用C# 写了一个。 特征如下: 一、支持的数据库 MS SQL Server 2005+、My Sql、Oracle 二、支持的文档类型 HTML、CHM、 WOR...2014-07-27T08:39:00Z2014-07-27T08:39:00Zjustconnorhttp://www.cnblogs.com/justconnor/
之前找的数据库字典生成工具基本上都依赖于 Office Com 组件,在不安装 Office的情况下无法使用。怒,于是自己用C# 写了一个。
本文链接:数据字典生成工具,转载请注明。
http://www.cnblogs.com/eastsea/p/3865852.html DBA_Oracle日志文件 - altert / trace /audit / redo / archive log(概念) - 东方瀚海 2014-07-26 BaoXinjian一、摘要1. 日志简称日志一般指的是联机重做日志文件(Redlog)。主要功能是恢复异常关闭的数据库和保证数据的完整性、一致性。还有可恢复近期丢失的数据(这要看重做日志文件的容量)。重做文件的原理是:把DML(Insert、Update、Delete)语句所... 2014-07-27T08:27:00Z 2014-07-27T08:27:00Z 东方瀚海 http://www.cnblogs.com/eastsea/ 【摘要】2014-07-26 BaoXinjian一、摘要1. 日志简称日志一般指的是联机重做日志文件(Redlog)。主要功能是恢复异常关闭的数据库和保证数据的完整性、一致性。还有可恢复近期丢失的数据(这要看重做日志文件的容量)。重做文件的原理是:把DML(Insert、Update、Delete)语句所... 阅读全文http://www.cnblogs.com/2050/p/3871517.html性能更好的js动画实现方式——requestAnimationFrame - 无双 用js来实现动画,我们一般是借助setTimeout或setInterval这两个函数,css3动画出来后,我们又可以使用css3来实现动画了,而且性能和流畅度也得到了很大的提升。但是css3动画还是有不少局限性,比如不是所有属性都能参与动画、动画缓动效果太少、无法完全控制动画过程等等。所以有的时候...2014-07-27T08:16:00Z2014-07-27T08:16:00Z无双http://www.cnblogs.com/2050/
用js来实现动画,我们一般是借助setTimeout或setInterval这两个函数,css3动画出来后,我们又可以使用css3来实现动画了,而且性能和流畅度也得到了很大的提升。但是css3动画还是有不少局限性,比如不是所有属性都能参与动画、动画缓动效果太少、无法完全控制动画过程等等。所以有的时候我们还是不得不使用setTimeout或setInterval的方式来实现动画,可是setTimeout和setInterval有着严重的性能问题,虽然某些现代浏览器对这两函个数进行了一些优化,但还是无法跟css3的动画性能相提并论。这个时候,就该requestAnimationFrame出马了。
requestAnimationFrame 是专门为实现高性能的帧动画而设计的一个API,目前已在多个浏览器得到了支持,包括IE10+,Firefox,Chrome,Safari,Opera等,在移动设备上,ios6以上版本以及IE mobile 10以上也支持requestAnimationFrame,唯一比较遗憾的是目前安卓上的原生浏览器并不支持requestAnimationFrame,不过对requestAnimationFrame的支持应该是大势所趋了,安卓版本的chrome 16+也是支持requestAnimationFrame的。
requestAnimationFrame 比起 setTimeout、setInterval的优势主要有两点:
1、requestAnimationFrame 会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率,一般来说,这个频率为每秒60帧。
2、在隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流,这当然就意味着更少的的cpu,gpu和内存使用量。
像setTimeout、setInterval一样,requestAnimationFrame是一个全局函数。调用requestAnimationFrame后,它会要求浏览器根据自己的频率进行一次重绘,它接收一个回调函数作为参数,在即将开始的浏览器重绘时,会调用这个函数,并会给这个函数传入调用回调函数时的时间作为参数。由于requestAnimationFrame的功效只是一次性的,所以若想达到动画效果,则必须连续不断的调用requestAnimationFrame,就像我们使用setTimeout来实现动画所做的那样。requestAnimationFrame函数会返回一个资源标识符,可以把它作为参数传入cancelAnimationFrame函数来取消requestAnimationFrame的回调。怎么样,是不是也跟setTimeout的clearTimeout很相似啊。
所以,可以这么说,requestAnimationFrame就是一个性能优化版、专为动画量身打造的setTimeout,不同的是requestAnimationFrame不是自己指定回调函数运行的时间,而是跟着浏览器内建的刷新频率来执行回调,这当然就能达到浏览器所能实现动画的最佳效果了。
目前,各个支持requestAnimationFrame的浏览器有些还是自己的私有实现,所以必须加前缀,对于不支持requestAnimationFrame的浏览器,我们只能使用setTimeout,因为两者的使用方式几近相同,所以这两者的兼容并不难。对于支持requestAnimationFrame的浏览器,我们使用requestAnimationFrame,而不支持的我们优雅降级使用传统的setTimeout。把它们封装一下,就能得到一个统一兼容各大浏览器的API了。
代码可以到这里来查看:https://gist.github.com/chaping/88813f56e75b0fd43f8c
var prefixes = 'webkit moz ms o'.split(' '); //各浏览器前缀
var requestAnimationFrame = window.requestAnimationFrame;
var cancelAnimationFrame = window.cancelAnimationFrame;
var prefix;
//通过遍历各浏览器前缀,来得到requestAnimationFrame和cancelAnimationFrame在当前浏览器的实现形式
for( var i = 0; i < prefixes.length; i++ ) {
if ( requestAnimationFrame && cancelAnimationFrame ) {
break;
}
prefix = prefixes[i];
requestAnimationFrame = requestAnimationFrame || window[ prefix + 'RequestAnimationFrame' ];
cancelAnimationFrame = cancelAnimationFrame || window[ prefix + 'CancelAnimationFrame' ] || window[ prefix + 'CancelRequestAnimationFrame' ];
}
//如果当前浏览器不支持requestAnimationFrame和cancelAnimationFrame,则会退到setTimeout
if ( !requestAnimationFrame || !cancelAnimationFrame ) {
requestAnimationFrame = function( callback, element ) {
var currTime = new Date().getTime();
//为了使setTimteout的尽可能的接近每秒60帧的效果
var timeToCall = Math.max( 0, 16 - ( currTime - lastTime ) );
var id = window.setTimeout( function() {
callback( currTime + timeToCall );
}, timeToCall );
lastTime = currTime + timeToCall;
return id;
};
cancelAnimationFrame = function( id ) {
window.clearTimeout( id );
};
}
//得到兼容各浏览器的API
window.requestAnimationFrame = requestAnimationFrame;
window.cancelAnimationFrame = cancelAnimationFrame;
这样子我们就能在所有浏览器上使用requestAnimationFrame和cancelAnimationFrame了。
下面举个简单的例子来说明怎么运用requestAnimationFrame进行动画,下面的代码会将id为demo的div以动画的形式向右移动到300px
<script>
var demo = document.getElementById('demo');
function rander(){
demo.style.left = parseInt(demo.style.left) + 1 + 'px'; //每一帧向右移动1px
}
requestAnimationFrame(function(){
rander();
//当超过300px后才停止
if(parseInt(demo.style.left)<=300) requestAnimationFrame(arguments.callee);
});
</script>
参考资料:
http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
http://msdn.microsoft.com/zh-cn/library/ie/hh920765(v=vs.85).aspx
https://developer.mozilla.org/zh-CN/docs/Web/API/window.requestAnimationFrame
本文链接:性能更好的js动画实现方式——requestAnimationFrame,转载请注明。
http://www.cnblogs.com/workest/p/3871515.html我给女朋友讲编程-题外话系列(1)--害怕过七夕,不知道买什么礼物 - 拼命工作 我女朋友在外地出差,平常能够做的就是打打电话,聊聊天。下周六就是中国的情人节了。说实话,也许你们觉得七夕很浪漫,但是我是有一点小恐惧,因为不知道送什么礼物给女朋友好。以前给女朋友送过一些礼物,尽管精心准备,有时难免留有遗憾。不管怎么说,又是一次情人节,我还是需要精心准备一下,于是花时间来搜索礼物,另...2014-07-27T08:15:00Z2014-07-27T08:15:00Z拼命工作http://www.cnblogs.com/workest/
下周六就是中国的情人节了。
说实话,也许你们觉得七夕很浪漫,但是我是有一点小恐惧,因为不知道送什么礼物给女朋友好。以前给女朋友送过一些礼物,尽管精心准备,有时难免留有遗憾。
不管怎么说,又是一次情人节,我还是需要精心准备一下,于是花时间来搜索礼物,另外也请朋友们给我出一点建议。
下面是我搜到的一些礼物,想看大图片的朋友可以单击小标题。请大家给点建议。
1, 戒指
曾经在网上买了两对情侣戒指,还刻上了双方名字首字母,满心欢喜送出去,女朋友收到后发现,太大了,不合适,容易掉。
当时我是认真看了说明和评论的,有人说偏小,让买大一点的,我还对照了身高、体重和戒指尺寸的对照比,精心挑选了两对,没想到还是大了。
不过,上次买的那对戒指比较简单,没有任何修饰,这次搜到了一个带珠子的,不知道怎么样。
2, 连衣裙:
女朋友在外地出差,刚过去的时候没有裙子换,就给她买了两条。
后来,女朋友收到货,有一件太大了,撑不起来;还有一件像睡衣,和网上看到的出入很大,后来,这两个裙子都扔了。
这次搜到一个如图所示的,不知道她会不会喜欢。
3, 项链
项链倒是没有给女朋友买过,不过女朋友说她也不怎么喜欢这些金银首饰,不过,我是不是要尝试一下呢?
买过项链的朋友,请给我点建议。
4, 小熊
女朋友比较喜欢比较可爱的娃娃,这是一个小笨熊,也挺可爱的,不知道女朋友会不会喜欢。
学校里,见很多人追女朋友,一般都是送棉娃娃,不过,不知道会不会很俗啊。
5,巧克力
我查的时候,看到好多人买巧克力,不过,我女朋友不喜欢吃甜的,所以,我感觉,买这个也不是太好。
6, 花
另外,我搜索到了这么一盒花,也是有很多人买,感觉这个比巧克力更适合女朋友一些。不过,仍然不知道女朋友会不会喜欢。
上个月,女朋友毕业,我买了一束云南干花去看她,不过后来因为东西太多了,就丢到了宿舍。
我也没啥兴趣爱好,也不怎么懂幽默,不过也是希望进步,所以,想请朋友给点建议。
各位朋友,你有没有送过什么礼物,结果特别让女朋友满意的啊,请给我说说。
本文链接:我给女朋友讲编程-题外话系列(1)--害怕过七夕,不知道买什么礼物,转载请注明。
http://www.cnblogs.com/dongyu9521/p/3871507.html.NET基础笔记(C#) - 七步、 闲着没事就把以前学习时的笔记拿出来整理了一下,个人感觉有点用,就想拿出来跟园友共享一下。有些基础性的内容比如基本概念、语法什么的就不发了。内容:1、构造方法(函数) 2、继承 3、访问修饰符 4、静态和非静态 5、隐藏基类方法 6、重写基类方法 7、抽象方法 8、接口 9、多态和接口 10、值...2014-07-27T08:08:00Z2014-07-27T08:08:00Z七步、http://www.cnblogs.com/dongyu9521/
闲着没事就把以前学习时的笔记拿出来整理了一下,个人感觉有点用,就想拿出来跟园友共享一下。有些基础性的内容比如基本概念、语法什么的就不发了。
内容:1、构造方法(函数) 2、继承 3、访问修饰符 4、静态和非静态 5、隐藏基类方法 6、重写基类方法 7、抽象方法 8、接口 9、多态和接口 10、值类型与引用类型 11、ref和out 12、类型转换 13、异常处理 14、string字符串处理 15、string常用方法 16、StringBulider 17、File类读取文件 18、文本高亮 19、泛型集合List<T>和Dictionary<TKey,TValue> 20、装箱和拆箱 21、读写数据(Ststem.IO) 22、文件流(FileStream) 23、StreamReader和StringWriter 24、File和Directory 25、序列化 26、Directory类的静态方法 27、正则表达式 28、Regex类 29、字符串提取 30、贪婪模式 31、字符串替换 32、byte数组和字符串的转换 33、委托 34、多播委托 35、事件 36、委托和事件的区别 37、var类型 38、扩展方法 39、XML
1、构造方法(函数):在对象创建的时候初始化字段
public 类名([参数])
{
//执行内容,如this.name=name;(有参数)
}
构造方法没有返回值,void也不写
创建对象时如果没写构造方法系统默认有一个无参的构造方法,一旦用户添加了构造方法,原来默认的无参的构造方法就没有了
默认初始值为:int:0; bool:false; string:null; char:‘\0’;
构造方法的作用:初始化字段(实例化类的时候传入初始值)
调用构造方法:
1)除了new对象的时候,不能直接使用构造方法名调用构造方法
2)this调用构造方法(难点)(一个构造方法调用另一个构造方法)
为了解决代码的重用才使用this调用构造方法
当一个类中出现多个构造方法重载的时候,同时构造方法都需要为字段赋初值
使用this代表当前类的构造方法来使用,根据this后面的参数决定使用哪一个重载
public 构造方法名():this([参数])
{
//代码
}
如:
public Person(string name, int age, string sex)
{
this.name = name;
this.age = age;
this.sex = sex;
}
public Person(string name)
: this(name, 0, null)
{
}
2、继承:
多个类都有共同的字段、属性或方法时,可以使用继承
需要写一个学生类、一个老师类、一个校长类
事先写一个Person类,在写其他类的时候继承这个类,Person叫父类,继承他的叫子类
特征:子类既有父类的非私有字段、属性和方法,又有子类独有的字段、属性和方法
一个子类只能有一个父类,一个父类可以有若干个子类
[访问修饰符] class 子类名:父类名
子类调用父类构造方法的问题(难点)
->构造方法的执行顺序:先父类再子类
->关于构造方法的重载:如果不声明调用哪个构造方法默认执行无参的构造方法
避免错误的方法:
->为父类提供无参构造方法
->在子类中指定调用父类有参的构造方法
注意:父类除构造函数(方法)之外的非私有成员都可以被子类所继承,构造方法不可以被继承,但可以被调用,调用方法: [访问修饰符] 构造方法名([参数]):base(父类构造方法的参数)
public Teacher():base("狗蛋”,20,"女")
{ } //直接赋值,实例化对象时不需再赋值
public Teacher(string name, int age, string sex)
: base(name, age, sex)
{
this.className = "C#";//老师类独有的字段
} //继承传递参数,实例化对象时需要赋值
base关键字用于显示声明要调用父类的哪个构造方法,是调用,不是声明不是继承
注意:为避免继承父类时因为构造方法出错,应该为每个声明的类都手动添加一个无参的构造方法
3、访问修饰符
public 最公开的,所有地方都可以访问
private 最隐蔽的,只有本类可以访问
protected 可以在当前类和子类中被访问
internal 只能在本项目中被访问
C#中规定类的访问修饰符只能是public 和internal
类中成员的访问修饰符除以上四种以外还有个(protected internal)即可以在当前类、子类和本项目中被访问
4、静态和非静态
静态使用static标记
静态成员
->如何定义静态成员
在成员前加static关键字
->如何使用静态成员
静态成员不能由实例对象调用,只能用类名调用
使用静态成员,不需要实例化对象
静态成员会在整个应用程序退出时才释放资源,所以这个变量可以在整个程序中共享数据,可用于窗体间传递参数
注意:静态变量过多会占用系统内存,因此声明静态变量时要多加考虑
Main方法是静态的,所以它只能调用静态的方法或参数,若想调用非静态方法,必须new一个类的实例,用实例名调用。
静态类
1)什么情况下要将一个类标记为静态类?
一般情况是,当这个类是一个工具类,里面都是方法,为了让用户调用的时候方便,不需要实例化对象,这时可以将该类标记为static类,此时该类中只能包含静态成员,不能包含实例成员,比如Convert、Console、Read、ReadInt等
2)什么情况下需要在一个普通类中编写一个静态成员,而这个类不能标记为static?
当这个类需要被实例化的时候,如果这个类中有一个成员是所有对象要共享的数据,这时可以将该类中的这个成员标记为静态的,但是这个类还是一个非静态类
3)静态类不能被实例化,不能被继承
4)由于静态成员会在整个程序退出时才释放资源,所以尽量避免写静态字段或静态属性,最好只写静态方法
5)静态类中可以有静态成员和实例成员,因为其不能被实例化,所以静态类中的实例成员无法被调用,也就没有任何意义了
6)静态类不能被继承,也不能继承自其它类,只能继承自Object类
静态构造函数
1)静态构造方法(或字段)在第一次访问这个类的时候执行,并且只执行一次
->静态构造方法与静态类的生命周期(了解)
静态成员属于该类的所有对象。实例成员只属于当前实例
静态类的生命周期:第一次访问类的时候创建,程序结束时才释放
5、隐藏基(父)类方法(了解,用的不多)
当子类和父类有相同名字的方法时,实例化子类调用该方法为子类内的方法,把子类转换成父类后再调用该方法就是父类内的方法了
为了代码规范化,如要有意隐藏父类的方法,要在子类的同名方法前加new关键字,用于 提醒这里是要隐藏父类方法
//隐藏基类方法
class MyBase
{
public void Func()
{Console.WriteLine("我是父类的方法);}
}
class MySub : MyBase
{
public new void Func() //在方法前用new关键字提醒这里是有意隐藏父类方法
{Console.WriteLine("我是子类的方法);}
}
class Program
{
static void Main(string[] args)
{
MySub ms = new MySub();
ms.Func();
// ((MyBase)ms).Func();
MyBase my = ms;
my.Func();
Console.ReadKey();
}
}
6、重写基(父)类方法(多态的体现)
在父类方法前加virtual(虚方法),表示这个方法可以被重写
在子类方法前加override,表示重写基类方法,子类如果不写override就是隐藏
当子类和父类有相同名字的方法时,实例化父类调用的是父类的方法,将子类赋值给父类后,父类调用的就是子类的方法了
一旦父类的方法在子类中被重写,除了实例化父类调用以外,子类或者子类的子类调用时都是调用的最新的那个子类重写方法
此时base和this就有区别了:
如果子类和父类中有名字相同的方法,那么base.Func()就代表父类的方法,this.Func()代表当前类的方法
如果不希望子类再重写,需要在子类方法前加sealed
//重写基类方法
class USB
{
public virtual void Usb() //virtual关键字表示父类方法可以被重写′
{Console.WriteLine("我是父类的方法);}
}
class Phone:USB
{
public override void Usb()//override关键字表示这个方法重写父类的方法
{Console.WriteLine("手机");}
}
class Mp3:USB
{
public override void Usb()
{Console.WriteLine("MP3");}
}
class Program:USB
{
static void Main(string[] args)
{
Console.WriteLine("输入数字");
USB usb = new USB();
switch (Console.ReadLine() )
{
case "1":
usb = new Phone();
break;
case "2":
usb = new Mp3();
break;
default:
break;
}
usb.Usb();
Console.ReadKey();
}
}
隐藏看类型,重写只管新
7、抽象方法
方法前加abstract,圆括号后加分号
[public] abstract void 方法名(参数);
抽象方法所在的类也必须是抽象的,不能被实例化,类前加abstract
abstract class 类名
抽象成员必须出现在抽象类中,但抽象类中可以有抽象成员和实例成员
抽象类的抽象方法默认可以且必须被重写,所以需要重写时不需要再写virtual,直接在子类方法中写override关键字即可重用
抽象类的成员:方法、属性、索引、事件
注意:抽象类中的所有抽象方法在子类中必须要被重写,如果子类中不重写,那这个子类也必须声明成抽象类,但此时子类也就不能被实例化了
8、接口
[访问修饰符] interface 接口名(接口一般以I开头,I+行为+able)
{
//接口成员,一定是抽象成员
}
成员的定义:与抽象成员的写法一样(没有关键字),没有执行体(方法体);没有访问修符;
实现:子类“继承”自接口,接口可以看做是子类的“干爹”,补全抽象的方法
调用:和类的使用方法一样,要使用接口中的方法,则将对象赋值给接口变量
多态:和抽象类的实现一样,接口的多继承使得多态的实现变得灵活
子类继承父类,子类实现(继承)接口:class 子类名:父类名,接口名 或class 类名:接口名
个人理解:接口就是把类中的一种特定的功能性方法封装成接口(如开车、飞等),声明类的时候可以继承这个接口,就拥有了这个接口所特有的功能,就可以在类中实现这个功能,那实现后的这个功能就成为了这个类的一个功能性方法。用接口可以实现多继承,一个类可以继承多个接口,也就拥有了多个功能性方法,声明类的时候可以选择性的继承,比如定义老师的时候可以继承说话、吃饭、教课等接口,定义司机的时候又可以继承说话、吃饭、开车等接口
//定义一个接口,表示Driving功能
interface IDrivable
{
void Driving();
}
class Teacher:Person,IDrivable//继承父类和接口
{
public Teacher(string name, int age, string sex):base(name,age,sex)
{//继承父类的构造方法
}
public void Driving()//实现接口的Driving功能
{
Console.WriteLine("我会开车");
}
public override void ShowMe()//重写父类方法
{
Console.WriteLine("我是老师");
}
}
static void Main(string[] args)
{
Teacher tea = new Teacher("张三", 18, "男");
tea.ShowMe();//调用重写后的方法
tea.Driving();//调用实现后的接口方法
Console.ReadKey();
}
9、多态和接口
接口是可以多继承的,多继承同时会引起方法的重名
为避免重名,显示实现接口:
返回值 接口名.方法名(参数)
{
//方法体
}
显示实现接口的这个方法只能由接口变量进行调用
现阶段记住接口使用的语法,然后将接口直接当做抽象类使用(初学阶段)
10、值类型和引用类型
变量可以看做是一个数据
值类型,是一个存储数据的容器,这个数据就是这个类型表示的数据
引用类型,也是一个容器,但是这个容器存储对象的一个引用(地址),真正的数据在另一块内存中,就相当于是一个指向数据的快捷方式
值类型存储在栈里面,引用类型存储在栈和堆里面(堆里存储的是真实数据,栈里面存储的是真实数据在堆里面的内存地址)
值类型和引用类型会涉及到传参和赋值时的不同,要注意区分
委托(delegate)也是引用类型
值类型赋值赋的是真实的值,引用类型赋值赋的是地址
引用类型是一个变量,两个快捷方式,修改一个快捷方式会影响另一个快捷方式;值类型是一个变量和一个复制后的变量,修改一个变量不会影响另一个变量
所有的值类型都继承自System.ValueType类
11、引用传递:ref和out
使用ref或out就可以实现将引用传递过来
定义方法时,在参数类型前加ref或out
调用方法时,在参数前加ref或out
注:定义变量时不需要加ref或out
当一个变量传递给一个方法时,总是复制一份自己的数据,赋值给方法,那么方法中执行的变量就与方法外的那么变量没有关系了,方法内修改变量值,也不会影响方法外的那个变量。有时会要求多个返回操作(方法中的变量有多个在方法外需要使用),此时可以使用ref标记参数,此时在方法中使用的这个变量与方法外的变量就是同一个变量了,相当于可以使用参数实现返回值
out与ref的作用与使用方法相同
out 必须在方法中赋值
ref必须在方法外赋值
12、类型转换
隐式类型转换:兼容类型,小转大
显式类型转换:即强制类型转换 (转换后的类型)要转换的变量
Convert类型转换:系统封装的方法,Convert.ToInt32()、Convert.ToDouble()等
其他转换方法:int.Parse()、int.TryParse()(第一个参数表示待转换的字符串,第二个out(result)参数表示数字转换成功后的变量,如果转换失败,返回false,为out result赋值为0)
用int.TryParse()代替try-catch,释放内存,提高性能
不止int类型有TryParse,所有基本类型都有TryParse,用法都一样
13、异常处理(try-catch、try-catch-finally、try-finally)
Exception是一个专门用来封装异常的一个类型
两个属性:message(异常说明文本)和stackTrace(异常抛出的顺序)
抛出异常:throw Exception的一个对象(new一个对象)
从出现异常的try向上依次抛出(方法之间依次调用),直至处理,找到最近的catch捕获异常(用于try-finally语句,catch块在方法以外,执行顺序是try-finally再向上寻找catch执行异常捕获)
Finally用于释放资源,finally{},程序执行完try-catch后会再执行finally语句块
Finally总会执行,即使try-catch中有return
try
{ }
catch (Exception ex)
{ throw; }
finally
{ }
Try-finally用处不多,用于释放资源
使用异常会降低系统性能,尽量少用
14、string字符串处理
构造函数:string(char[] chs)、string(char ch,int count)
string str = new string(’a‘,’b’,’c’);//abc
string str = new string(‘a’,3);//aaa
3.144.ToString(“0.0”);用于控制格式,会四舍五入
字符串可以当做数组进行处理(使用下标读取,拥有数组的各种属性)
string str = “不可见的你”; 此时str[1]就是“可”了
通过索引得到的数据是char类型,字符串不可改变,使用索引无法修改字符串的数据
要想修改可将字符串变成字符型数组,str.ToCharArray()
string是引用类型
string str = string.Empty;声明一个空字符串
判断字符串是否为空:
str.Length == 0(推荐)
str ==“”
str == string.Empty
String.IsNullOrEmpty(要判断的字符串)(推荐)
15、string常用方法
1)字符串比较:bool isTrue = string.Equals(string a,string b)
String.Equals(str1,str2);
Str1.Equals(str2,StringComparison.OrdinalIgnoreCase); //第二个参数意思是不区分大小写
string.Compare(str1,str2);
Equals方法:
String重载的方法判断两个,一个是==,一个是EqualsHelper
==判断两字符串是否相同
EqualsHelper判断两个字符串中每一个字符是否相同
Object的重载是来自于object类,是继承下来的
在object中使用的是==和Equals方法
Equals方法是一个虚方法,由string重写了,调用的是object.ReferenceEquals方法和EqualsHelper方法
注意:==默认表示判断两个对象的地址是否相同,与object.ReferenceEquals方法一样
String提供的str1.Equals(str2)判断地址是否相同,字符串是否object提供的virtual Eaulse(object)
Equals和==的区别:
Equals会判断两个值的内存地址;==只判断值是否相等。
Compare方法:
int result = String.Compare(str1,str2);
str1>str2 -> 1 str1 == str2 -> 0 str1<str2 -> -1
string.Compare方法比较两个字符串,这两个字符串字母顺序表示大小,按照字典排序规则进行比较(对于char类型:A>a,B>a,b>A同一个字母,大写>小写,不同字母按照字母顺序后面的大于前面的;对于string类型不是按照埃克森码比较,A>a,b>A,B>a)
2)大小写转换:ToLower()和ToUpper()
3)str1 = str1.Trim([params char[] chs]);去除字符串两边的空格,或chs中出现的字符(重载)
str1 = str1.TrimEnd(params char[] chs);去除字符串的末尾chs中出现的字符
str1 = str1.TrimStart(params char[] chs);去除字符串的开头chs中出现的字符
4)合并与分割
合并字符串:string s = string.Join(str1,str2,str3,...)
分割字符串:string[] strArray = str1.Split(‘a’);
string[] strArray= str1.Split(new char[]{‘a’,’b’},StringSplitOptions.RemoveEmptyEntries);去掉a、b和所有空格
5)字符串查找
Contain包含,返回bool类型的值,str1.Contain(“爱”);
IndexOf检索位置,返回字符串中某个字符的位置(int)找不到则返回-1
str.IndexOf(要找的字符或字符串)
str.IndexOf(要找的字符或字符串,开始寻找的位置)
str.LastIndexOf()是从后往前寻找,用法同str.IndexOf()
注意:IndexOf和LastIndexOf中第二个参数(开始寻找的位置)只是为了说明要查找的是字符串中的哪一个字符(如果字符有重复),即按查找顺序在开始位置后的第一个字符,返回的索引依然是该字符在整个字符串中的索引,而不是从查找位置开始的索引。
6)截取子字符串
string s =str1.Substring(开始的位置,子字符串的长度)
String s = str1.Substring(开始的位置)
7)判断字符串的开头和结尾
bool a = str1.StratWith(字符串);
bool a = str1.EndWith(字符串);
8)字符串的插入、移除和替换
插入:string str2 = str1.Insert(位置(int),要插入的人字符串(string));
移除:string str2 = str1.Remove(位置(int),长度(int));
string str2 = str1.Remove(位置(int));移除该位置后的所有字符
替换:string str2 = str1.Replace(旧字符串,新字符串);
9)格式化字符串
String.Format(格式化的字符串,填坑的参数)
string str1 = String.Format(“{0},{1}”,str2,str3);
10)判断字符串是否为空
String.IsNullOrEmpty(str);
10)字符串的方法(总结)
增:添加(+=)、插入(Insert)
删:Remove
改:Replace、 ToUpper、ToLower、Trim、TrimStart、TrimEnd、Split、Join、new String、SubString、Format
查:Contains、IndexOf、LastIndexOf、StartsWith、EndsWith、ToCharArray、Length、Empty
比:Equals、Compare
16、StringBuilder
大量字符串拼接的时候性能非常差,很难完成大型数据的拼接
(Stopwatch对象可以记录程序运行的时间,Start开始计时,Stop结束)
使用StringBuilder:StringBuilder sb = new StringBuilder();
sb.Append(要添加的字符串);//向sb中添加数据
sb.AppendLine(要添加的字符串);//向sb中添加数据后换行,等价于sb.Append(要添加的字符串+”\r\n”);
sb.AppendFormat(要添加的字符串);格式化处理,等价于 sb.Append(string.Format(要添加的字符串));
string str = sb.ToString;输出或赋值时要转换成string
在处理大型数据的时候,StringBuilder要比普通的string处理快很多,因为处理大型数据要用StringBuilder
17、读取文件
string[] lines = File.ReadAllLines(@"H:\传智播客实训资料代码\第9天\a.csv", Encoding.Default);//按行读取
string[] lines = File.ReadAllText(@"H:\传智播客实训资料代码\第9天\a.csv", Encoding.Default);//读取所有文本
string[] lines = File.ReadLines(@"H:\传智播客实训资料代码\第9天\a.csv", Encoding.Default);//读取一行文本
string[] lines = File.ReadAllBytes(@"H:\传智播客实训资料代码\第9天\a.csv", Encoding.Default);//读取字节
18、文本高亮
->让文本框获得焦点--txtContent.Focus();
->找到要高亮的位置--pos
->选中(高亮)的字符串长度--length
->调用TextBox的选中方法txtContent.Select(pos,length)
19、泛型集合 List<T>和Dictionary<TKey,TValue>
List集合:动态的自定义数组
List<类型> 集合名 = new List<类型>();
List用法与ArrayList一样,不同点在于定义时和使用时的类型固定,因此可以完全替代ArrayList
类型[] = list.ToArray()//将List转换成该类型的数组
Dictionary集合:动态的自定义键值对
Dictionary<类型,类型> 集合名 = new Dictionary<类型,类型>();
Dictionary用法与Hashtable一样,不同点在于定义时和使用时的类型固定,因此可以完全替代Hashtable
20、装箱与拆箱
直接将值类型赋值给引用类型就是装箱
将存储值类型的引用类型强制转化为对应的值类型就称为拆箱
Object o = 1;//装箱 int n = (int)o;//拆箱
装箱对值类型保持无关性,装箱对引用类型保持相关性
装箱和拆箱会对性能有一定的影响
21、读写数据(I/O操作、System.IO类)
System.IO.Path类,专门处理字符串路径,是一个静态类
string path = @“D:\window\system\file.txt”;
获取文件名:string fileName = Path.GetFileName(path);
获取后缀名:string fileName = Path.GetExtension(path);
修改后缀名:path = Path.ChangeExtension(待修改的路径,“mp3”);
获取文件夹名:string fileName = Path.GetDirectoryName(path);
合并路径:path= Path.Combine(路径1,路径2,路径3...);
临时文件夹:path = Path.GetTempPath();
绝对路径:从盘符开始的最精确的路径
相对路径:相对于当前文件同一个目录作为参照的路径
相对路径和绝对路径:区分就看有没有根目录盘符的标记
22、文件流(FileStream)
1)引入命名空间
2)创建FileStream对象FIleStream fs = new FileStream(文件名,FileModel,FileAccess)
FileStream fs = new FileStream(@"D:\a.txt", FileMode.Create, FileAccess.Write);
FileModel:对文件的处理方式(打开、创建、追加)
FileAccess:对文件的操作:读、写
3)使用方法操作
写一个字节:fs.WriteByte()
写一个字节片段(将字节数组中的数据写入到fs指定的文件):
fs.Write(存放字节的数组,开始写字节的数组下标,要写的字节数)
读一个字节:fs.ReadByte()
读一个字节片段(将fs指定的文件中的数据读取到字节数组):
fs.Read(存放字节的数组,开始存放字节的数组下标,要读的字节数)
4)释放资源
fs.Close()和fs.Dispose()方法(两个都要调)
23、StreamReader和StreamWriter
StreamReader:专门用来读取文本文件的流
1)引入命名空间:IO
2)new一个FileStream指向文件
3)new一个StreamReader,构造函数参数是FileStream的对象
4)调用方法
StreamReader有一个属性EndOfStream:判断当前的流位置是否在文件流的末端,一般用来判断文件是否读完
StreamWriter:专门用来写入文本文件的流
1)引入命名空间:IO
2)new一个FileStream指向文件
3)new一个StreamWriter,构造函数参数是FileStream的对象
4)调用方法
24、File和Directory对文件和目录操作做了一个封装
增(Create):创建文件或文件夹
删(Delete):删除文件或文件夹
改:改名字 File.Move(“1.txt”,“2.txt”)
查:...
判断是否存在(Exists)
移动文件:File.Move(“1.txt”,“2\1.txt”)
复制文件:Copy
Directory用于处理文件夹(目录)
删除目录的时候如果目录不是空的则会报错,此时需要在Delete内添加一个true
Directory.Delete(“1”,true);
25、序列化:
序列化:把一个对象的内容存储到磁盘上(文件流)、网络上(网络流)、内存里(内存流),下次需要用到对象数据的时候可以直接拿来用
反序列化:把已经序列化存储到磁盘、网络、内存里的对象数据,提取到项目中的对象中
序列化步骤:
1)创建一个文件流
2)为需要序列化的对象添加标记([Serializable]表示此对象可以被序列化)
3)创建BinaryFormatter(导入命名空间)
4)调用Seralize方法(bf.Seralize(文件流对象,需要被序列化的对象))
反序列化步骤:
1)创建一个文件流
2)为需要序列化的对象添加标记([Serializable]表示此对象可以被序列化)
3)创建BinaryFormatter(导入命名空间)
4)调用DeSeralize方法(object ob = bf.DeSeralize(文件流对象))
反序列化定义接收类型的时候必须根据原序列化的程序集确定类型
建议:在使用序列化的时候尽量避免使用自动属性,因为自动属性在每次编译的时候自动生成的字段名可能不一样,所以在反序列化的时候可能会造成问题
注意:如果序列化和反序列化不在同一项目下,那反序列化的时候需要引用原序列化的程序集
26、关于文件夹下的文件与子文件夹
Directory类的静态方法
获取子文件名:string[] s = Directory.GetFiles(@“D:\dir“);
获取特定后缀名的子文件名:string[] s = Directory.GetFiles(@“D:\dir“,”*.txt“);
获取所有文件夹名:string[] s = Directory.GetDirectories(@“D:\dir“);
27、正则表达式
元字符:
1).(点)表示除\n以外任意的单个字符
2)[ ]表示括号内的任意一个字符(如a[xyz]b表示axb或ayb或azb)
3)^在[ ]括号内表示取反(如^[a-z]表示除小写字母以外的所有字符)
4)^匹配一串字符的开始(^abc表示以abc为开头的任意字符串)
5)$匹配一串字符的结尾(abc$表示以abc为结尾的任意字符串)
5)|表示“或”,优先级最低(如a|bc表示a或bc,(a|b)c表示ac或bc)
6)()可以改变优先级、提取组
7)*表示限定前面的字符出现任意次数(abc*表示ab、abc、abcccc......(abc)*表示abc、abcabc)
8)+ 至少出现一次,也可出现多次(xa+y表示xay、xaay...)
9)?表示出现0次或1次
10){n}限定前面的表达式出现n次
11){n,m}至少出现n次,最多出现m次
12){n,}至少出现n次
13)\d表示0-9,\D表示\d的反面
14)\s表示空白符,\S表示\s的反面
15)\w表示数字、字母、下划线、汉字,\W表示\w的反面
注:正则表达式的转义符也是\,如果要表示*(星),可以写成\*,要表示一个\,可以写成\\
28、Regex类
Regex.IsMatch(待判断的字符串,正则表达式)判断一个字符串是否匹配某个正则表达式
!如果正则表达式没有^和$,那默认不设定开头和结束,此时只要待判断的字符串中含有该正则表达式就返回true
^z|food$:表示以z开头的任意字符串或者以food结尾的任意字符串,都返回true
因为|的优先级最低,所以上边的表达式其实是(^z)|(food$)
Regex.Match()从某个字符串中提取匹配某个正则表达式的某个子字符串,但只能提取一个
Regex.Matches()同上,可以提取所有的匹配字符串
Regex.Replace()字符串替换,把所有匹配正则表达式的字符串替换为对应的字符
29、字符串提取
字符串提取的时候一般不加^和$
Match m = Regex.Match(原字符串,要提取的正则表达式)
m.Value属性表示提取到的字符串
m.Group[n]:提取组,按正则表达式中()括号对的个数,m.Group[0]表示整个字符串,m.Group[1]表示第一个括号内的内容,所以提取组的时候要从下标1的地方开始
MatchCollection mc = Regex.Matches(原字符串,要提取的正则表达式)
MatchCollection是一个集合,需要foreach遍历输出(遍历时的类型是Match)
30、贪婪模式
定义:当正则表达式提取的时候,如果一个字符或多个字符都能匹配,这时会按照使用最多字符的方式来匹配。
正则表达式默认采用贪婪模式,尽多的匹配
在限定符后面使用问号用来终止贪婪模式,即只会提取匹配的第一个
string s = “aaaa bbbbbbbb”;string s1 = “[a-zA-Z]+”; 匹配结果:aaaa
string s = “aaaa bbbbbbbb”;string s1 = “[a-zA-Z]+?”; 匹配结果:a
31、字符串替换
String s =Regex.Replace(原字符串,待替换的正则表达式,替换后的字符串);
如:string s = Regex.Replace(s, "-+", "-");//把字符串s中的所有-替换成一个-
提取组替换:
string s = "aaa13812345678bbb";
s = Regex.Replace(s, @"(\d{3})(\d{4})(\d{4})", "$1****$3");//把手机号中间4位替换成4个*
32、byte数组和字符串的转换
byte[] bt= System.Text.Encoding.UTF8.GetBytes(str);
string str = System.Text.Encoding.UTF8.GetString(bt);
33、委托(delegate)
委托是一种数据类型(和类一样),可以将方法赋值给委托对象,可以理解为方法类型
定义(在命名空间下,或添加一个cs文件,和类的定义一样):
1)使用delegate关键字
2)这个委托将来要存储的方法如果没有返回值,那么委托也要定义成void,参数也要与方法保持一致
3)委托是一个数据类型,用的时候需要传递一个变量
4)创建委托对象的时候可以不用new,系统编译时会自动给我们new
定义委托类型:public delegate void ChangeTxtDelegate(string str);
创建委托对象:public ChangeTxtDelegate changeTxt; //Form1中
给委托赋值:f1.changeTxt = ChangeText; //Form2中
调用委托:changeTxt(txt); //Form1中,此种调用方法为简写,实际上是changeTxt.invoke(txt);
创建委托对象时也可直接对其赋值:ChangeTxtDelegate changeTxt = new ChangeTxtDelegate(ChangeText) ;
用法(窗口间传值):委托要声明在需要被传值的对象内,比如Form1要提取Form2中的数据(即Form2往Form1传值),就应该把委托声明在Form1中,在Form2中写一个方法获取要传的数据,并且赋值给Form1的委托,最后在Form1中调用委托就ok了。(委托声明位置不绝对,还需要考虑当前活动窗口的问题,要声明再活动窗口中,有时也可声明在被提取值的窗口内)
作用:可以在某个代码内部,嵌入一段外部代码(外部写一个方法赋值给委托或在定义方法时把委托类型作为参数,调用时把方法名作为参数传递给委托对象,那么委托就执行该方法,不同的外部项目可以写不同的方法赋值给委托,可以实现一个委托选择性执行多种方法的灵活性),相当于是注入
什么情况下使用委托?
当一个类型中需要嵌入一段代码,但是这段代码具有不确定性,是根据使用这个类型的用户来确定代码的,这时就可以在该类型中使用一个委托,保证在某种情况下会调用该委托,用户将对应的方法传递给该委托,就会调用这个方法
可以把静态方法或者私有方法赋值给委托了变量,赋值后只要能使用到该委托变量的地方就能使用该方法,打破了访问修饰符的限制
一般在调用委托前或者触发事件前,都要判断委托变量或事件是否为null,如果为null则不调用
34、多播委托
MyDelegate md = new MyDelegate(M1); md+=M2;(增加某个方法)md-=M4;(去掉某个方法)
使用委托时,如果不是+=而是直接用=赋值,则会将前面增加的所有方法都覆盖掉
多播委托可以存储多个方法,委托中方法调用的顺序与增加方法时的顺序是一致的,但是,不要依赖于这个顺序(微软没有承诺这样的顺序,万一以后改了就GG了)
多播委托中,如果要是有返回值,只会得到最后一个方法返回的结果。可以通过调用GetInvocationList()方法返回当前委托中的所有方法,返回值类型时一个Delegate数组(委托数组),再用foreach遍历调用所有方法
所有定义的委托都继承自抽象类MulticastDelegate,而MulticastDelegate又继承自Delegate抽象类
多播委托内部是将绑定在当前委托对象上的每个方法,都转换成一个委托对象,并且存储在了一个叫_invocationList的object数组中。当调用委托的时候,其实就是循环遍历_invocationList数组,并且调用其中的每个委托
多播委托中,如果其中的某个方法执行时发生了异常,则后面的方法不再执行。
多播委托中只能存储同一类型的委托,即返回值、参数都相同
35、事件
事件是在特定条件下执行的动作,这个动作是由用户确定的。如Button的Click事件,其动作是由程序员写的,因此可以用委托来实现这个功能,写不同的方法赋值给委托变量。
但用委托模拟方法有两个缺点:
1)定义的委托可以在类外部的任何地方被触发,即非特定条件下也可以调用。因为委托变量是public修饰,改为private就不能在外部赋值了。
2)由于委托可以用=(等号)赋值,所以就有可能把前面已经注册的事件处理程序都覆盖掉。
而用事件就可以解决以上问题:
1)用事件必须先定义一个委托,否则无法使用事件
2)实例化委托变量的时候,在委托类型名前面、访问修饰付的后面加event关键字,就定义了一个事件
3)使用方法与委托一样,只是避免了以上两个问题
4)由于事件只能有+=或-=赋值,所以就避免了用=(等号)赋值时覆盖前面事件处理程序的问题
5)由于事件不能在定义事件的类以外被触发,所以也就避免了冒充事件触发的问题。
36、委托和事件的区别(面试常考)
委托和事件没有可比性,委托是数据类型,事件是委托类型的变量
委托可以用+=、-=、=赋值,可以在类的外部被调用
事件只能用+=、-=赋值,不能在类的外部被调用
事件的内部是用委托实现的
事件的作用和委托变量(不是委托,是委托变量)一样,只是在功能上受到一定的限制
37、var类型
var只能用作局部变量,可以赋任何类型的值。
var a = 1;和int a = 1;两者完全一样
var不能作为类的成员的类型,不能用作参数,不能用作返回值,只能用作局部变量
38、扩展方法(不修改微软系统方法源代码,实现系统方法的增加)
1)添加一个静态类
2)在静态类中增加一个静态方法,方法参数是:(this+要扩展的类名+变量名,该方法的参数)
如要在string类中添加一个验证是否是Email格式的扩展方法:
public static bool IsEmail(this string str)
{
return Regex.IsMatch(str, @"^\w+@\w+\.\w+$");
}
string a = "1321321@qq.com";
Console.WriteLine(a.IsEmail());
扩展方法只是看起来像类中的方法,其实不是,所以在扩展方法中也访问不到类中原来的私有成员
因为会扰乱系统方法,所以不建议使用,尽量不用
39、XML
XML是用一种格式化的方式来存储数据,可以用记事本打开
1)XML有且只有一个根元素
2)有开始标签就必须得有结束标签,且区分大小写
3)元素的属性值要用引号
读取XML文件,通过XDocument(需要添加命名空间)
读取整个XML文件: XDocument xd = XDocument.Load(XML文件路径)
循环遍历每个节点:
1)获取根节点 XElement xeRoot = xd.Root;(xeRoot.ToString()表示XML文档的内容)
2)递归遍历XML中的每个节点
遍历xeRoot下面的所有子节点:
public static void GetEle(XElement xe)
{
foreach (XElement ele in xe.Elements())
{
Console.WriteLine(ele.Name);
GetEle(ele);
}
}
GetEle(xeRoot);
例:读取XML数据
<CFX>
<MSG>
<交易码val="1000" />
<流水号val="100000000000000001" />
<金额val="1234567890.12" />
<付款机构val="1234" />
<付款单位账号val="12345678901234567890" />
<收款机构val="1234" />
<收款单位账号val="12345678901234567890" />
</MSG>
<MSG>
<交易码val="1000" />
<流水号val="100000000000000002" />
<金额val="1234567890.12" />
<付款机构val="1234" />
<付款单位账号val="12345678901234567890" />
<收款机构val="1234" />
<收款单位账号val="12345678901234567890" />
</MSG>
</CFX>
VS中读取XML的数据:
XDocument xd = XDocument.Load("ytbank.xml"); //加载XML文件
XElement xeRoot = xd.Root; //获取根节点
foreach (XElement item in xeRoot.Elements()) //遍历根节点,获取第一级子节点(MSG)
{
foreach (XElement itema in item.Elements()) //遍历第一级子节点,获取第二级子节点
{
Console.WriteLine("{0}:{1}", item.Element(itema.Name).Name, item.Element(itema.Name).Attribute("val").Value); //获取节点名和属性值
}
Console.WriteLine("==================================");
}
XML写入:
XDocument xdoc = new XDocument(); //创建一个XML对象
XElement xeleRoot = new XElement("Websites"); //创建一个根节点
xdoc.Add(xeleRoot); //将根节点添加到XML文件中
xeleRoot.SetElementValue("baidu", "http://www.baidu.com"); //添加一个节点以及内容
XElement xeleGoogle = new XElement("website"); //创建一个节点
xeleGoogle.SetAttributeValue("url", "http://www.google.com"); //添加属性和属性值
xeleGoogle.SetElementValue("name", "谷歌"); //为节点添加子节点
xeleGoogle.SetElementValue("age", "14");
xeleGoogle.SetElementValue("boss", "谢盖尔");
xeleRoot.Add(xeleGoogle); //将节点添加进根节点
xdoc.Save("website.xml"); //保存一个XML文档路径
本文链接:.NET基础笔记(C#),转载请注明。