Quantcast
Channel: CSDN博客推荐文章
Viewing all 35570 articles
Browse latest View live

webkit在win32下的编译规则(九)

$
0
0

webkit在win32下的编译规则(七)中,曾经说到要专门介绍js binding这部分的,但后面逐渐忘记了,最近有网友提起,特加入js binding这部分的介绍。

首先来说是js engine,js engine是一个解释器。解释器比较直观的就是命令行(cmd.exe),在命令行中,我们输入一行脚本,然后cmd.exe就是解析这段脚本然后执行。同样我们也可以将命令写在bat文件中,然后让cmd.exe执行。js engine与此类似,不同的是它执行js脚本。在命令行输入脚本,相当于js engine里面的执行标签中内嵌的脚本,例如onClick=”javascript:window.alert(1);” ;执行bat脚本,相当于js engine里面的解释运行一个js文件。

为了便于介绍,下面都已JavascriptCore这里面的js engine(SquirrelFish or SquirrelFish Extreme)为准,V8等引擎原理与此类似,只不过具体实现不一样。

在JavascriptCore中,处于主线的函数是:

JSValue JSC::evaluate(ExecState* exec, ScopeChainNode* scopeChain, const SourceCode& source, JSValue thisValue, JSValue* returnedException)

这个函数完成javascript具体的解析和执行,如果不调用这个函数,js engine将什么事也不会做。在webkit中,调用这个函数是webcore的JSMainThreadExecState::evaluate函数。当webkit下载完一个js文件,或者标签中内嵌的js脚本被触发时,JSMainThreadExecState::evaluate就会被调用,从而导致JSC::evaluate被调用。js engine是单线程的,一个页面(或一个window对象)一般配一个js engine,如果一个页面有多个frame,则会有多个js engine,这些js engine在不同的线程中运行。下面是对html进行parse时遇到js脚本后调用JSC::evaluate的堆栈:

     

WebKit_debug.dll!WebCore::JSMainThreadExecState::evaluate(JSC::ExecState * exec, JSC::ScopeChainNode * chain, const JSC::SourceCode & source, JSC::JSValue thisValue, JSC::JSValue * exception)  行 57    C++
>    WebKit_debug.dll!WebCore::ScriptController::evaluateInWorld(const WebCore::ScriptSourceCode & sourceCode, WebCore::DOMWrapperWorld * world)  行 144 + 0x33 字节    C++
     WebKit_debug.dll!WebCore::ScriptController::evaluate(const WebCore::ScriptSourceCode & sourceCode)  行 161 + 0x16 字节    C++
     WebKit_debug.dll!WebCore::ScriptElement::executeScript(const WebCore::ScriptSourceCode & sourceCode)  行 292 + 0x17 字节    C++
     WebKit_debug.dll!WebCore::HTMLScriptRunner::executePendingScriptAndDispatchEvent(WebCore::PendingScript & pendingScript)  行 140    C++
     WebKit_debug.dll!WebCore::HTMLScriptRunner::executeParsingBlockingScript()  行 119    C++
     WebKit_debug.dll!WebCore::HTMLScriptRunner::executeParsingBlockingScripts()  行 196    C++
     WebKit_debug.dll!WebCore::HTMLScriptRunner::executeScriptsWaitingForLoad(WebCore::CachedResource * cachedScript)  行 207    C++
     WebKit_debug.dll!WebCore::HTMLDocumentParser::notifyFinished(WebCore::CachedResource * cachedResource)  行 517 + 0x19 字节    C++
     WebKit_debug.dll!WebCore::CachedResource::checkNotify()  行 151 + 0x13 字节    C++
     WebKit_debug.dll!WebCore::CachedScript::data(WTF::PassRefPtr<WebCore::SharedBuffer> data, bool allDataReceived)  行 105    C++
     WebKit_debug.dll!WebCore::CachedResourceRequest::didFinishLoading(WebCore::SubresourceLoader * loader, double __formal)  行 170    C++
     WebKit_debug.dll!WebCore::SubresourceLoader::didFinishLoading(double finishTime)  行 196 + 0x28 字节    C++
     WebKit_debug.dll!WebCore::ResourceLoader::didFinishLoading(WebCore::ResourceHandle * __formal, double finishTime)  行 472 + 0x18 字节    C++
     WebKit_debug.dll!WebCore::ResourceHandleManager::downloadTimerCallback(WebCore::Timer<WebCore::ResourceHandleManager> * timer)  行 400 + 0x35 字节    C++
     WebKit_debug.dll!WebCore::Timer<WebCore::ResourceHandleManager>::fired()  行 100 + 0x23 字节    C++
     WebKit_debug.dll!WebCore::ThreadTimers::sharedTimerFiredInternal()  行 115 + 0xf 字节    C++
     WebKit_debug.dll!WebCore::ThreadTimers::sharedTimerFired()  行 94    C++
     WebKit_debug.dll!WebCore::TimerWindowWndProc(HWND__ * hWnd, unsigned int message, unsigned int wParam, long lParam)  行 103 + 0x8 字节    C++

JSC::evaluate有5个参数:ExecState* exec表示js engine的运行整体环境,例如全局变量的值;ScopeChainNode* scopeChain表示当前运行Scope Chain,这个和js的闭包(Closure)有关;const SourceCode& source表示js脚本的内容;JSValue thisValue表示这段脚本的this对象;JSValue* returnedException表示脚本执行后的异常信息。

我们可以看一下ScriptController::evaluateInWorld的代码:

ScriptValue ScriptController::evaluateInWorld(const ScriptSourceCode& sourceCode, DOMWrapperWorld* world)
{
    const SourceCode& jsSourceCode = sourceCode.jsSourceCode();
    String sourceURL = ustringToString(jsSourceCode.provider()->url());
    // evaluate code. Returns the JS return value or 0
    // if there was none, an error occurred or the type couldn't be converted.
    // inlineCode is true for <a href="javascript:doSomething()">
    // and false for <script>doSomething()</script>. Check if it has the
    // expected value in all cases.
    // See smart window.open policy for where this is used.
    JSDOMWindowShell* shell = windowShell(world);
    ExecState* exec = shell->window()->globalExec();
    const String* savedSourceURL = m_sourceURL;
    m_sourceURL = &sourceURL;
    JSLock lock(SilenceAssertionsOnly);
    RefPtr<Frame> protect = m_frame;
    InspectorInstrumentationCookie cookie = InspectorInstrumentation::willEvaluateScript(m_frame, sourceURL, sourceCode.startLine());
    JSValue evaluationException;
    exec->globalData().timeoutChecker.start();
    JSValue returnValue = JSMainThreadExecState::evaluate(exec, exec->dynamicGlobalObject()->globalScopeChain(), jsSourceCode, shell, &evaluationException);
    exec->globalData().timeoutChecker.stop();
    InspectorInstrumentation::didEvaluateScript(cookie);
    if (evaluationException) {
        reportException(exec, evaluationException);
        m_sourceURL = savedSourceURL;
        return ScriptValue();
    }
    m_sourceURL = savedSourceURL;
    return ScriptValue(exec->globalData(), returnValue);
}


从中我们可以看到,JSMainThreadExecState::evaluate的exec,scopeChain,thisValue都和JSDOMWindowShell这个对象有关。从JSDOMWindowShell这个名字可以看出,webkit是将js脚本的执行比喻成在shell中输入命令。thisValue为JSDOMWindowShell则表明js脚本最顶层的this为window对象,这也于DOM模型的window对象处于顶端一致了。


在上面的JSDOMWindowShell* shell = windowShell(world);执行过程中,会调用JSDOMWindowShell::setWindow,调用堆栈如下:

     WebKit_debug.dll!WebCore::JSDOMWindowShell::setWindow(WTF::PassRefPtr<WebCore::DOMWindow> domWindow)  行 75    C++
     WebKit_debug.dll!WebCore::JSDOMWindowShell::finishCreation(JSC::JSGlobalData & globalData, WTF::PassRefPtr<WebCore::DOMWindow> window)  行 57    C++
     WebKit_debug.dll!WebCore::JSDOMWindowShell::create(WTF::PassRefPtr<WebCore::DOMWindow> window, JSC::Structure * structure, WebCore::DOMWrapperWorld * world)  行 62    C++
     WebKit_debug.dll!WebCore::ScriptController::createWindowShell(WebCore::DOMWrapperWorld * world)  行 111 + 0x21 字节    C++
     WebKit_debug.dll!WebCore::ScriptController::initScript(WebCore::DOMWrapperWorld * world)  行 213 + 0xc 字节    C++
     WebKit_debug.dll!WebCore::ScriptController::windowShell(WebCore::DOMWrapperWorld * world)  行 79 + 0x43 字节    C++
>    WebKit_debug.dll!WebCore::ScriptController::evaluateInWorld(const WebCore::ScriptSourceCode & sourceCode, WebCore::DOMWrapperWorld * world)  行 130 + 0xc 字节    C++


JSDOMWindowShell::setWindow会调用JSDOMWindow::create创建真正的window对象:

JSDOMWindow* jsDOMWindow = JSDOMWindow::create(*JSDOMWindow::commonJSGlobalData(), structure, domWindow, this);


而后会调用两个参数的setWindow将jsDOMWindow赋值给JSDOMWindowShell的成员变量m_window。

void setWindow(JSC::JSGlobalData& globalData, JSDOMWindow* window)
        {
            ASSERT_ARG(window, window);
            m_window.set(globalData, this, window);
            setPrototype(globalData, window->prototype());
        }


接下面我们看JSDOMWindowShell的定义,如下:

class JSDOMWindowShell : public JSC::JSNonFinalObject

JSDOMWindowShell继承于JSC::JSNonFinalObject,JSC::JSNonFinalObject继承于JSObject,总之JSDOMWindowShell是一个JSObject。JSObject有如下函数是可以被重载的(多态):

        virtual bool getOwnPropertySlot(JSC::ExecState*, const JSC::Identifier& propertyName, JSC::PropertySlot&);
        virtual bool getOwnPropertyDescriptor(JSC::ExecState*, const JSC::Identifier& propertyName, JSC::PropertyDescriptor&);
        virtual void put(JSC::ExecState*, const JSC::Identifier& propertyName, JSC::JSValue, JSC::PutPropertySlot&);
        virtual void putWithAttributes(JSC::ExecState*, const JSC::Identifier& propertyName, JSC::JSValue, unsigned attributes);
        virtual bool deleteProperty(JSC::ExecState*, const JSC::Identifier& propertyName);
        virtual void getPropertyNames(JSC::ExecState*, JSC::PropertyNameArray&, JSC::EnumerationMode mode = JSC::ExcludeDontEnumProperties);
        virtual void getOwnPropertyNames(JSC::ExecState*, JSC::PropertyNameArray&, JSC::EnumerationMode mode = JSC::ExcludeDontEnumProperties);
        virtual void defineGetter(JSC::ExecState*, const JSC::Identifier& propertyName, JSC::JSObject* getterFunction, unsigned attributes);
        virtual void defineSetter(JSC::ExecState*, const JSC::Identifier& propertyName, JSC::JSObject* setterFunction, unsigned attributes);
        virtual bool defineOwnProperty(JSC::ExecState*, const JSC::Identifier& propertyName, JSC::PropertyDescriptor&, bool shouldThrow);
        virtual JSC::JSValue lookupGetter(JSC::ExecState*, const JSC::Identifier& propertyName);
        virtual JSC::JSValue lookupSetter(JSC::ExecState*, const JSC::Identifier& propertyName);


JSDOMWindowShell重载了这些函数,所以当执行js的相关函数时就会调用这些函数,例如执行

window.onload= function() {}

js engine就会调用JSDOMWindowShell::put函数,JSDOMWindowShell::put接着会调用JSDOMWindow::put

void JSDOMWindowShell::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
{
    m_window->put(exec, propertyName, value, slot);
}


JSDOMWindow::put会lookupPut在s_info查找onload对应的函数并执行:

void JSDOMWindow::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
{
    if (!impl()->frame())
        return;
    // Optimization: access JavaScript global variables directly before involving the DOM.
    if (JSGlobalObject::hasOwnPropertyForWrite(exec, propertyName)) {
        if (allowsAccessFrom(exec))
            JSGlobalObject::put(exec, propertyName, value, slot);
        return;
    }
    if (lookupPut<JSDOMWindow>(exec, propertyName, value, s_info.propHashTable(exec), this))
        return;
    if (allowsAccessFrom(exec))
        Base::put(exec, propertyName, value, slot);
}


我们看一下JSDOMWindow::s_info定义:

const ClassInfo JSDOMWindow::s_info = { "DOMWindow", &JSDOMWindowBase::s_info, &JSDOMWindowTable, 0 };


其中s_info的parentClass为JSDOMWindowBase::s_info,staticPropHashTable为JSDOMWindowTable。JSDOMWindowTable的定义在D:\project\WebKit\WebKitBuild\Debug_Cairo_CFLite\obj\WebCore\DerivedSources\JSDOMWindow.cpp,JSDOMWindow.cpp是由DOMWindow.idl通过generate-bindings.pl生成的,这个在webkit在win32下的编译规则(七)中已经解释过了:

static JSC_CONST_HASHTABLE HashTable JSDOMWindowTable = { 1125, 1023, JSDOMWindowTableValues, 0 };


它是一个HashTable,其值在JSDOMWindowTableValues中。下面截取了JSDOMWindowTableValues的一部分:

static const HashTableValue JSDOMWindowTableValues[] =
{
    { "screen", DontDelete, (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsDOMWindowScreen), (intptr_t)setJSDOMWindowScreen THUNK_GENERATOR(0) },
    { "history", DontDelete, (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsDOMWindowHistory), (intptr_t)setJSDOMWindowHistory THUNK_GENERATOR(0) },
    { "locationbar", DontDelete, (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsDOMWindowLocationbar), (intptr_t)setJSDOMWindowLocationbar THUNK_GENERATOR(0) },
    { "menubar", DontDelete, (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsDOMWindowMenubar), (intptr_t)setJSDOMWindowMenubar THUNK_GENERATOR(0) },
    { "personalbar", DontDelete, (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsDOMWindowPersonalbar), (intptr_t)setJSDOMWindowPersonalbar THUNK_GENERATOR(0) },
    { "scrollbars", DontDelete, (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsDOMWindowScrollbars), (intptr_t)setJSDOMWindowScrollbars THUNK_GENERATOR(0) },
    { "statusbar", DontDelete, (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsDOMWindowStatusbar), (intptr_t)setJSDOMWindowStatusbar THUNK_GENERATOR(0) },
    { "toolbar", DontDelete, (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsDOMWindowToolbar), (intptr_t)setJSDOMWindowToolbar THUNK_GENERATOR(0) },
    { "navigator", DontDelete, (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsDOMWindowNavigator), (intptr_t)setJSDOMWindowNavigator THUNK_GENERATOR(0) },
    { "clientInformation", DontDelete, (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsDOMWindowClientInformation), (intptr_t)setJSDOMWindowClientInformation THUNK_GENERATOR(0) },
…………… 
    { "document", DontDelete | ReadOnly, (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsDOMWindowDocument), (intptr_t)0 THUNK_GENERATOR(0) },
   …………… 
    { "console", DontDelete, (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsDOMWindowConsole), (intptr_t)setJSDOMWindowConsole THUNK_GENERATOR(0) },
    { "onabort", DontDelete, (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsDOMWindowOnabort), (intptr_t)setJSDOMWindowOnabort THUNK_GENERATOR(0) },
    { "onbeforeunload", DontDelete, (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsDOMWindowOnbeforeunload), (intptr_t)setJSDOMWindowOnbeforeunload THUNK_GENERATOR(0) },
    { "onblur", DontDelete, (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsDOMWindowOnblur), (intptr_t)setJSDOMWindowOnblur THUNK_GENERATOR(0) },
    { "oncanplay", DontDelete, (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsDOMWindowOncanplay), (intptr_t)setJSDOMWindowOncanplay THUNK_GENERATOR(0) },
    { "oncanplaythrough", DontDelete, (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsDOMWindowOncanplaythrough), (intptr_t)setJSDOMWindowOncanplaythrough THUNK_GENERATOR(0) },
    { "onchange", DontDelete, (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsDOMWindowOnchange), (intptr_t)setJSDOMWindowOnchange THUNK_GENERATOR(0) },
    { "onclick", DontDelete, (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsDOMWindowOnclick), (intptr_t)setJSDOMWindowOnclick THUNK_GENERATOR(0) },
    { "oncontextmenu", DontDelete, (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsDOMWindowOncontextmenu), (intptr_t)setJSDOMWindowOncontextmenu THUNK_GENERATOR(0) },
    { "ondblclick", DontDelete, (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsDOMWindowOndblclick), (intptr_t)setJSDOMWindowOndblclick THUNK_GENERATOR(0) },
   …………… 
    { "onkeypress", DontDelete, (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsDOMWindowOnkeypress), (intptr_t)setJSDOMWindowOnkeypress THUNK_GENERATOR(0) },
    { "onkeyup", DontDelete, (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsDOMWindowOnkeyup), (intptr_t)setJSDOMWindowOnkeyup THUNK_GENERATOR(0) },
    { "onload", DontDelete, (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsDOMWindowOnload), (intptr_t)setJSDOMWindowOnload THUNK_GENERATOR(0) },
………
}


从JSDOMWindowTableValues我们看到了我们所熟悉的history,document,navigator等属性,也看到了onload,onclick等event。

每一个JSDOMWindowTableValues的项由5部分组成:"document"->属性的名称;DontDelete | ReadOnly->属性的相关flag; (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsDOMWindowDocument)->getter函数;(intptr_t)0->setter函数; THUNK_GENERATOR(0) 用于 jit。执行window.document.xxx就会触发getter函数;执行window.document = 123;就会触发setter函数,由于document 的setter函数为null,所以这句js脚本会抛出异常。

具体的setter和getter的实现与具体功能相关,这里就不专门解释了。例如window.navigator最终调用:

JSValue jsDOMWindowNavigator(ExecState* exec, JSValue slotBase, const Identifier&)
{
    JSDOMWindow* castedThis = static_cast<JSDOMWindow*>(asObject(slotBase));
    if (!castedThis->allowsAccessFrom(exec))
        return jsUndefined();
    UNUSED_PARAM(exec);
    DOMWindow* imp = static_cast<DOMWindow*>(castedThis->impl());
    JSValue result = toJS(exec, castedThis->globalObject(), WTF::getPtr(imp->navigator()));
    return result;
}


而其中的imp->navigator()则是调用D:\project\WebKit\Source\WebCore\page\DOMWindow.cpp中的:

Navigator* DOMWindow::navigator() const
{
    if (!m_navigator)
        m_navigator = Navigator::create(m_frame);
    return m_navigator.get();
}


另外,很多人有一个疑问是:调用相关方法或获取相关属性时,为什么写不写window都可以,例如下面脚本是可以正常执行的:

<html>
<head>
<title>test1234</title>
<script>
onload = function() {
    alert(1234);
}
</script>
</head>
<body>
dsfdsf
</body>
</html>


答案是上面的脚本其实是最直接的方式,因为js脚本执行时的thisValue就为JSDOMWindowShell,也即window对象,而JSDOMWindow有一个属性叫window,它返回的就是当前的JSDOMWindowShell,所以js脚本中的window.onload其实是thisValue.window.onload.


在DOMWindow.idl中,有一些注释是// DOM Level 0,// DOM Level 2 AbstractView Interface等,相关的解释可以参考wiki(http://en.wikipedia.org/wiki/Document_Object_Model#Levels_of_DOM):

After the release of ECMAScript, W3C began work on a standardized DOM. The initial DOM standard, known as "DOM Level 1," was recommended by W3C in late 1998. About the same time, Internet Explorer 5.0 shipped with limited support for DOM Level 1. DOM Level 1 provided a complete model for an entire HTML or XML document, including means to change any portion of the document. Non-conformant browsers such as Internet Explorer 4.x and Netscape 4.x were still widely used as late as 2000.

DOM Level 2 was published in late 2000. It introduced the "getElementById" function as well as an event model and support for XML namespaces and CSS. DOM Level 3, the current release of the DOM specification, published in April 2004, added support for XPath and keyboard event handling, as well as an interface for serializing documents as XML.


作者:chief1985 发表于2011/9/12 23:30:51 原文链接
阅读:8942 评论:3 查看评论

赤手空拳如何成就百万富翁?——网络营销之八(第五招:百度知道(腾讯问问))

$
0
0

   “百度知道”(腾讯问问)实在是个好东东,无论你在百度上搜索什么类型的关键词,“百度知道”的排名永远都是最好的,尤其是一些包含“什么”、“怎么办”、“如何”、“为什么”的长尾词,你可以试一试找任何一个关键词,然后在这个词的前面加上“为什么”或“怎么办”看看排在第一的是不是“百度知道”。

 

1、百度知道和百度文库有什么区别?

  拉拉:百度知道和百度文库有什么区别呢?

  蒋老师:实际上百度知道就是一个问答,而百度文库则是一个知识库。前者侧重于有人提问你来回答,而后者则是你自己设计的知识库。

 

2、为什么要进行百度知道营销?

  拉拉:有必要进行百度知道营销吗?

  蒋老师:从网络营销的角度,百度知道是一个非常不错的东东,不仅你可以通过回答网友的问题,而且你可以多设置几个帐号自问自答。这是因为:

  ①通过百度知道带来一定的流量

  ②通过百度知道做品牌推广

  ③通过百度知道增加反向链接:这个是有技巧的,否则你的回答是不会成功的。

 

3、百度知道有没有什么技巧?

  拉拉:百度知道有什么技巧吗?

  蒋老师:总喜欢找技巧,没有技巧就是最大的技巧,请参考:

  ① 技巧之一,申请10个百度账号

  百度知道是一个很好企业网络营销平台,对于知道提问与回答及采纳要求也很严格。做知道营销,账号肯定是必不可少,发多了就会被禁止甚至是封号,申请10个百度知道账号,一方面可以防止百度知道工作人员认为在作弊,一方面可以方便自己灵活使用知道进行操作,另外还需要下载四个浏览器,目前最常用的浏览器:火狐、sogou、ie、360浏览器、google浏览器等。主要作用是方便自己进行操作,四个浏览器可以登陆4个账号,有助于提升工作效律。

  ② 技巧二,使用表格记录好

  必须记录好每天知道工作日记,主要记录提问的账号、问答、地址、状态、是否采纳答案、顶知道、发布时间等,把这些统统计在表格上,方便自己管理知道营销效果。另外还需要记录问题关键词,通过关键词长尾词长尾词分析工具分析出的词。

  ③ 技巧之三,提交百度知道问题

  譬如,问答为:“黄金定投与基金定投有什么区别?”,使用账号为:hello_gold, 然后把该问题的链接与账号使用一个表格进行记录,一方面防止提问问题不知道提问账号,一方面防止提问问题找不到。

  ④ 技巧之四,群里找人回答问题

  知道营销最大的难题就是,IP限制,账号限制,可以提问的问题整理好,然后把问题的链接及已经编写好的问题答案分别发给群里的10位好友,或者企业好友、朋友、亲人等,让他们帮忙提交,这样做保证了账号及ip的不重复,降低百度平台对于作弊现象怀疑度。

  ⑤ 技巧之五,采纳答案

  一旦问题提交答案后,切不可立即采纳,据经验发现,整个过程如时间太短,容易被百度察觉,别急等三天以后再回来采纳答案。

  ⑥ 技巧之六,多种类型问题轮换提交

  切记不要使用同一个账号,提问的问题太过于相似,也很容易出事,所使用的账号不能只提问关于企业行业的问题,也可以去回答或者提问其它行业的问题或者回答,一方面可以升级获取经验增长值,一方面可以降低百度知道平台工作人员对作弊或者利用知道进行企业品牌营销打击度。  

  建议做法是周一提交问答,周四提交,周日采纳最佳答案,

  ⑦ 技巧之七,顶贴也要讲求方法

  进行顶帖子的时间,可以一天顶几个或者隔几天顶几次,切记不要一天给知道进行顶几十个,这样很容易引起百度反感。可以通过qq好友、企业好友、亲戚朋友、分别把链接发给他们,让他们帮助自己帖子知道。

  ⑧ 技巧之八,知道团队

  把最常用几个知道的账号,尽量把等级升高一点,加入知道团队,一方面可以了解百度知道最新动态或者一些操作规则与内情。

  ⑨ 技巧之九,总结

  每隔一个星期或者一个月要对自己的知道企业网络营销进行一汇总的报告,哪些知道做得比较好,哪些知道做得不好,哪些知道可以挖掘大量企业长尾关键词,都必须进行一个汇总报告,找出做知道营销不足之处进行改进,只要不断总结与学习,才能更好做好知道企业网络营销。

 

4、百度知道需注意的几个问题。

  拉拉:有什么需要注意的吗?

  蒋老师:最需要注意的问题就是你放外链千万要小心行事,否则可能会被封号。

  ① 百度知道问题中必须要有关键词。

  ② 回答者的等级越高,百度审核通过率就越高。

  ③ 好评数量越大,知道排名越高。这一点最重要。

  ④ 百度知道问题解决时间对排名影响较小。

  ⑤“相关内容”数量越多越好,相关问题越多说明这个页面的通用性更高,能够解决更多人的问题。

  ⑥ 标题里包含关键词即可,不要刻意增加关键词密度

  ⑦ 通过百度知道内链建设,增加我们问题在百度知道里“相关问题”里面的出现次数,出现的次数越多越好。

  ⑧ 在百度知道里面做外链效果不如百度内链接好。

作者:jianghuiquan 发表于2011/9/13 19:57:01 原文链接
阅读:2810 评论:0 查看评论

赤手空拳如何成就百万富翁?——网络营销之九(第六招:论坛营销)

$
0
0

  论坛营销就是“企业利用论坛这种网络交流的平台,通过文字、图片、视频等方式发布企业的产品和服务的信息,从而让目标客户更加深刻地了解企业的产品和服务。最终达到企业宣传企业的品牌、加深市场认知度的网络营销活动”,就是论坛营销。

 

1、网络营销招数繁多,有必要都学习吗?

  拉拉:经过师傅的指引,这段时间我也看了不少网络营销方面的书籍、网站,感觉东西太多了,有必要都学习吗?

  蒋老师:我的观点是十八般武器,不一定要样样精通,但是知道这十八般武器都是干什么的,还是很有必要的。然后可以根据自己特长选择适合自己的几种武器。

  拉拉:明白了,那就请老师继续说说其他的招式吧,最后我再选择我希望的招式。

  蒋老师:好的,我们继续……

 

2、论坛(贴吧)营销之道

  论坛是互联网诞生之初就存在的形式,历经多年洗礼,论坛作为一种网络平台,不仅没有消失,反而越来越焕发出它巨大的活力。其实人们早就开始利用论坛进行各种各样的企业营销活动,当时论坛成为新鲜媒体的论坛出现时,就有企业在论坛里发布企业产品的一些信息了,其实这也是论坛营销的一种简单的方法。

  论坛营销可以成为支持整个网站推广的主要渠道,尤其是在网站刚开始的时候,是个很好的推广方法。利用论坛的超高人气,可以有效为企业提供营销传播服务。而由于论坛话题的开放性,几乎企业所有的营销诉求都可以通过论坛传播得到有效的实现。论坛营销是以论坛为媒介,参与论坛讨论,建立自己的知名度和权威度,并顺带着推广一下自己的产品或服务。运用的好的话,论坛营销可以是非常有效果的网络营销手段。

  论坛营销的主旨,无疑是讨论营销之道,论坛营销应在多样化的基础上,逐渐培养和形成自己的主流文化或文风。比如,设一些专栏,聘请或培养自己的专栏作家和专栏评论家,就网友广泛关心的话题发言。不是为了说服别人或强行灌输什么,而是引导论坛逐渐形成自己的主流风格。海纳百川,有容乃大。营销论坛,包容多样化的观点,多样化的文风,是营销人强烈自信心的表现。

作者:jianghuiquan 发表于2011/9/13 19:58:38 原文链接
阅读:2620 评论:0 查看评论

赤手空拳如何成就百万富翁?——网络营销之十(第七招:视频营销)

$
0
0

  网络营销的特性是什么?视频营销能给企业的新品推广带来什么惊喜?曾几何时,广告模式不清晰、用户体验差、无版权且质量低劣,内容与产品不匹配等等疑问一直成为品牌客户选择视频网站的绊脚石。

 

  然而2010年是视频行业实现跨越式发展的一年,以土豆网“内容为王”为代表的中国网络视频行业彻底颠覆了单纯的视频分享网站角色,土豆网以用户分享为基础,集热播影视剧的版权内容和自制内容三合一的内容策略,构架起了全方位媒体公司的定位。 

 

  拉拉:视频营销这个我不陌生,咱看的电视剧中经常插播广告或者字幕广告都是,网络视频有没有特别之处?

  蒋老师:的确如此,我们经常接触电视,所以对于常规的视频广告再熟悉不过了,而网络视频营销更是在此基础之上加上了N多创意,具体你可以看一下前些日子推出的一系列视频营销的经典案例中领略一下其独特魅力! 

 

  正是由于有着丰富的娱乐内容和强势的整合创意能力,土豆网推出了一大批诸如:“诺基亚中国创造、美之源一笑赢千金、百威自制剧植入、大众汽车蓝创未来”等品牌经典案例。当营销的娱乐化为时代的风向标时,不由得让人们想起美国著名管理学者斯科特·麦克凯恩曾经说过的一句话—“一切行业都是娱乐业。”娱乐化营销已成为这个时代商业运营的一个标志性特征,而如何对娱乐化精神进行有效开发、利用、发挥则成为品牌广告主面临的新挑战。

 

  在短短两个月的时间内,“趣喝美汁源、一笑赢千金”取得了惊人的成绩,丝毫不输当热门电视真人秀栏目。第三方数据公司艾瑞的研究报告显示,“趣喝美汁源、一笑赢千金”在网民中的知名度达到31.9%,与同期的电视真人秀节目相比,仅次于通过多渠道宣传,在湖南卫视和青海卫视套播的《快乐男声》和《花儿朵朵》,领先于同期播出的其他电视真人秀节目。

 “趣喝美汁源、一笑赢千金”取胜的秘诀在哪里?土豆网首席营销官王祥芸揭开了谜底:“网络整合营销有4I原则,即:Interesting趣味原则、Interests利益原则、Interaction互动原则、Individuality个性原则。这次“趣喝美汁源、一笑赢千金”的成功很好地诠释了土豆网对于网络整合营销的把控能力”。
作者:jianghuiquan 发表于2011/9/13 20:00:19 原文链接
阅读:2703 评论:0 查看评论

赤手空拳如何成就百万富翁?——网络营销之十一(第八招:微博营销)

$
0
0

  微博营销以微博作为营销平台,每一个听众(粉丝)都是潜在营销对象,每个企业利用更新自己的微型博客向网友传播企业、产品的信息,树立良好的企业形象和产品形象。每天的更新的内容就可以跟大家交流,或者有大家所感兴趣的话题,这样就可以达到营销的目的,这样的方式就是新兴推出的微博营销。

 

1、我怎么感觉微博上的发言都是乱七八糟?

  拉拉:蒋老师,受您的熏陶,为了让自己多接触高科技,也在新浪网上注册了一个“围脖”,怎么感觉都是乱七八糟的?

  蒋老师:恭喜你前进了一大步,愿意与新兴的东东接触了。的确,这就像你参加了一个宴会,如果没有主题,你会感觉乱轰轰的,但是一旦你融入一个圈子,你会发现这个圈子讲的股票,那边几个美女讲的美容,是不是听出一些门道,微博就是这样,大家都有发言权,所以如果你讲的好,这时候大家就听你的多,如果别人讲的好,大家都听别人的。在微博上发言新浪叫“发布”,你可以想象一个新闻发布会;在腾讯上叫“广播”。而你的听众新浪叫“粉丝”;腾讯叫“听众”。

  我引用互联网上的一段话:“你的粉丝超过一百,你就好像是本内刊;超过一千,你就是个布告栏;超过一万,你就好像是本杂志;超过十万,你就是一份都市报;超过一百万,你就是一份全国性报纸;超过一千万,你就是电视台,超过一亿,你就是CCTV了。”,这段话的原创没有考证,但是却道出了微博的真谛。

 

2、微博如何与营销联系在一起呢?

  拉拉:这又怎么能和网络营销联系在一起呢?

  蒋老师:微博营销以微博作为营销平台,每一个听众(粉丝)都是潜在营销对象,每个企业利用更新自己的微型博客向网友传播企业、产品的信息,树立良好的企业形象和产品形象。每天的更新的内容就可以跟大家交流,或者有大家所感兴趣的话题,这样就可以达到营销的目的,这样的方式就是新兴推出的微博营销。

  在现实中,我们常常把线上活动和线下活动结合在一起,更容易取得成功。

 

3、微博营销与博客营销有什么区别?

  拉拉:蒋老师,微博与博客的区别仅仅是发布的文字多少不一样吗?

  蒋老师:二者的区别发布的文字多少不一样的确是区别之一(微波每次只能发布   140字以内),但重点体现如下:

  (1)时效性的不同:微博的时效性非常重要,听众更关心今天或者现在发生了什么事情,而对三天前的信息则少有人去关注,有点像即时新闻;而博客对时效性则要求不强,读者只要发现对其有用,多久都可能阅读。

  (2)门槛不同:微博侧重于哪儿、什么时间发生了什么有趣(有价值)的事情,行文可以短小精悍,降低了发布者的文字功底的门槛;而博客则是对某一领域或产品的认知程度,这取决于博主的专业知识与文笔。

  (3)获取方式不同:微博可以随时、随地用电脑、手机很方便的获取微博信息;而博客由于都是文章,相对使用手机则阅读较为不便。

 

4、微博营销有哪些误区呢?

  拉拉:我明白了,要尽可能多的发展自己的粉丝,然后行之有效地进行营销,那么有没有什么误区可以借鉴呢?

  蒋老师:你不妨从以下几方面加以注意:

  ① 一味追求听众(粉丝)数量

  表现:每天都在拼命地发展自己的粉丝,不管是互粉还是有奖活动或者花钱购买僵尸粉……总之,像炒股票一样,涨则忧,跌则悲。

  对策:僵尸粉再多都是都是枉然,旨在寻找那些潜在的目标用户才是根本目的。这就需要:首先要对企业和产品进行定位;其次,聆听与企业相关(包括竞争对手)的信息,听听他们是如何评价企业的产品,是批评的声音还是赞的声音;第三,筛选目标用户,进行关注,尝试为赞的声音表示感谢,批评的声音帮助他们解决问题;第四,竞争对手情况的了解,取长补短;第五,要量更要质。

  ② 我的地盘我作主,不愿互动

  表现:众多微博主把自己的微博当成自己的电台、广告页,再也不用担心以前在别人的群中发广告被踢的后果,真正体会了自己当家作主的优越感,浑然忘记了为人民服务的根本宗旨。面对粉丝的疑问、求助和质疑无动于衷。众多粉丝也只能选择一个“关注自己”的民主博主,远离你的独裁统治。

  对策:发赤裸裸的广告从来都是低级营销策略,做顾问式营销,帮助用户解决问题;用心聆听粉丝在讨论什么,是抱怨还是表扬你的产品或服务,加入他们的对话;

  ③ 缺乏团队与坚持

  表现:为数不少的企业刚开始也是信心百倍,但由于没有专人负责,最终沦落为只是赶了一个时髦而已,导致最终企业微博的荒废,粉丝有需求也无人管、无人问的状态。

  对策:如果不可能组建一支团队,至少要有一个人对微博营销富有激情、愿意和对话的人来做这件事,做到问有答,询有果;长期坚持,不能三天打渔、两天晒网。

  ④ 发布内容与产品(服务)脱节

  表现:看看“冷笑话”做的很成功,而对企业产品或服务感兴趣的粉丝数量甚少,于是开始发布一些不着边际的内容,企图通过哗众取宠来吸引粉丝。

  对策:企业制定社会化媒体营销的内容策略十分重要,不可因为听众一时数量较少而采取不靠谱的方法去增加粉丝。

 

5、如何制定社会化媒体——微博内容策略?

  拉拉:我们前面都说了“内容为王”这个我懂,那如何制定内容,是不是有好的策略呢?

  蒋老师:微博只是你和用户交流的工具,不要工具本身当成是社会化媒体营销,通过这些社会化媒体工具建立起自己的品牌社区才是问题本身。既然社会化媒体网站是交流的工具,那么一定要“言之有物”,那么社会化媒体营销的内容策略就显得尤为重要,以下建议仅供参考:

  ①了解你的受众

  你的企业博客内容又要给谁看?弄清楚自己的内容是写给谁看的,他们有什么特性这个问题之后,接下去的社会化媒体营销才不会迷失方向。

   ②定义你内容的主旋律

   了解了你的受众之后,你就要设法和他们交流,不要只是例行公事的发布一些自己产品的信息,你应该试着解决一些用户的真正需求,给他们提供一些自己力所能及的帮助,比如你是一家咖啡馆,你可以给用户提供一些关于咖啡的知识,让用户觉得你的内容不仅仅是一些促销广告,而是一个知识源泉。

   ③制定有规律的更新频率

   你不必每天更新内容,但必须要制定一个更新计划,多久更新一次,每次更新多少内容。更新频率过少,用户说不定就把你忘记了,而更新频率过高,用户也许就会感到烦,特别是在微博上,要严格遵循一个合理的更新频率。

   ④制定一个内容更新计划

   我发现有很多微博上面的内容都是很随意发的,对于企业来说,如果像韩寒只发布一个“喂”字的微博就有点不妥了,企业应该制定出自己的内容更新计划,什么时候该发布什么样的内容?这一点应该像报纸学习,报纸上每天的内容都是有规划的,每个周末还有一个休闲副刊什么的。

   ⑤让内容有“连载”的味道

   有很多优秀的博客,每周都会有一篇总结性的文章,比如,本周10大twitter趋势等内容,让用户在每一周的某个时候都想回来看看,这一点像听单田芳评书“欲知后事如何,且听下回分解”的味道。

   ⑥做一些专访

   你可以访问你的用户,也可以请一些业内人士来和你的受众分享一些东西,而对于专访也需要一个清晰的计划,早早的制定一个采访名单。

   ⑦让内容形式多样化

   不要一成不变的只是文字,可以更多一点照片,视频,音乐,之前我看到一个企业博客他的一篇介绍自己产品的博文就是一个漫画,非常的吸引眼球,用户对于这种内容还乐于和朋友分享,何乐而不为呢?

   ⑧定期发布一些行业的白皮书(调查报告)

   如果有能力的话,就自己所在的行业发布一些调查报告,或者是一些行业白皮书,让自己在这个行业树立更权威的地位。

   ⑨关注用户的评论和反馈

   有很多企业博客,垃圾评论没有人去删除,一些用户的求助或者建议也没人去搭理,这就没有起到和用户交流的目的。一些企业的新浪微博也是,只是当成一个信息发布工具,对于其中用户的评论或者建议都没有及时去关注,这一点在社会化媒体营销中尤为重要。

   ⑩学会倾听

   最后一点,也是最重要的一点,如果你觉得自己不知道说什么的时候,先听听你的用户是怎么说的,你的竞争对手是怎么说的,你所在行业的业内人士又是怎么说的。社会化媒体营销从倾听开始。

作者:jianghuiquan 发表于2011/9/13 20:05:06 原文链接
阅读:3018 评论:0 查看评论

赤手空拳如何成就百万富翁?——网络营销之十二(第九招:移动互联网)

$
0
0

  移动互联网的蓬勃发展和宏伟的发展蓝图为企业描绘了一个崭新的未来世界,面对如此庞大的用户群体,在移动互联网上面存在什么样的营销机会和模式?如何利用移动互联网进行营销,自然成为企业值得去思考的问题。

 

1、为什么说,前十年是互联网的时代,后十年同开启了移动互联时代?

  拉拉:听说“前十年是互联网的时代,后十年同开启了移动互联时代”如何理解呢?

  蒋老师:以网易1997年创建伊始(新浪创建于1998年),已经发展了十多个年头,从蹒跚起步到上市,几大门户网站甚是风光了多年,但随着移动互联时代的开启,传统的门户网站遭受到了严重的考验与发展瓶颈。

 

  有很多数据在显示移动互联网市场的巨大机会:截止到2010年,全世界有50亿部手机,占总人口73%,而全球的电脑数量则只有12亿台;在全球40亿部正在使用的手机中,有10.8亿部为智能手机,占比达27%;每天手机中第三方应用程序下载量是3000万次,每秒达347次;截至2011年4月,中国9亿移动通信用户,3G用户达6757万,手机上网用户达3.03亿。很多机构预测显示,5年后手机将可能成为接入互联网的首选方式。

 

  随着iPhone、iPad、Android等移动终端的火热,伴随着用户的构成和行为习惯的改变以及新应用程序的大量涌现,移动互联网开始成为新的营销信息传播的载体。

 

   移动互联网消除了时空维度对信息传播的限制,实现了传播的随时性、随地性。手机和平板电脑等移动终端,伴随于人们在各个空间移动中的碎片化时间,可以实现信息的实时性传播,与需要在客厅、卧室、办公室或者汽车等固定地点接收信息的其他媒体相比,其信息发布与信息接收之间的时间之差更小,基本做到即时发布、即时接收,不仅可以实时获得信息,同时也可以实时向别人传递信息。

 

   同样在中国,新浪微博数据显示,2010年底 38%新浪微博来自移动终端,而至今这一数字已超过50%。这自然就在移动互联网上产生了实时化的营销传播机会,任何时间、任何地点、任何对象、任何信息、任何方式将成为信息传播的新观念。

 

   所以我们有理由说:前十年是互联网的时代,后十年同开启了移动互联时代!

 

2、移动互联的优势?

  拉拉:移动互联有哪些优势呢?

  蒋老师:移动互联网消除了不同媒介之间的隔断,实现了媒介大融合,使信息传播走向全媒介化。手机和平板电脑等数字化多媒体终端,既接收音频、视频,又接收图文、数据,这赋予了移动终端强大的媒介融合能力,人们可以借助文字、图片、图像、声音的任何一种或者几种的组合来开展信息传播活动,移动媒体、移动广播,移动电视、移动网站、移动SNS、移动电子商务等不同的形态实现新的跨界组合,让移动互联网成为一个具有全媒介元素的传播平台。

 

3、移动营销有什么策略?

  拉拉:针对移动营销有什么新策略吗?

  蒋老师:除掉纯粹的广告,移动互联网上,存在营销创意的无限想象空间。

利用手机应用、位置服务、手机支付、虚拟购物等形式,都可以帮助企业打开一扇新的营销之窗。

   譬如在IPHONE(IPAD)之苹果商店的APP应用、移动联通手机定制应用等、小米手机等都是具体的体现与应用。

  当然腾讯的钱包也是非常不错的一个应用!

 

【结束语】

  其实网络营销没有什么捷径,人家告诉你这个东西很简单,很容易。其实八成都需要很高的执行力,至于如何去修炼执行力,我想只有两个字“坚持”。只要你深谙一种方法,坚持执行下去,肯定会得到你想要的结果。正巧你的目的非常明确的话,那你的坚持将更加有目的性,过程中的些许成功能刺激你的满足感,你也就欣然的一路走下去了。

 

  当然,网络营销的招式不止这些,还有更多,在此仅列举部分常见招式,以起到抛砖引玉的作用。

 

   在此也非常感谢CSDN,让我这个非纯技术的文章在此得以展示,给我们技术人另外一种视角看世界!

 

  最后祝大家在网络营销的道路上一路顺利,获得你想要的结果,成为真正意义上的百万富翁!

 

  做一位懂管理、懂营销、懂技术、懂生活、懂理财的五懂新人而奋斗!

作者:jianghuiquan 发表于2011/9/13 20:10:49 原文链接
阅读:3245 评论:0 查看评论

【iOS-Cocos2d游戏开发之十】添加粒子系统特效并解决粒子特效与Layer之间的坐标问题;

$
0
0

 李华明Himi 原创,转载务必在明显处注明:
转载自【黑米GameDev街区】 原文链接: http://www.himigame.com/iphone-cocos2d/472.html

        一直以来Himi特别想在游戏中使用粒子系统,但是之前做J2me与Android中发现使用粒子做的效果都会造成游戏运行内存的一个负担,所以一直很遗憾,那么在iOS游戏开发中,可以说必须要使用粒子啦,还是苹果硬件给力;看过我一开始刚写cocos2d博文的时候我就说过因为cocos2d的粒子编辑器很给力,所以也是我选用cocos2d引擎的一部分原因;            

粒子编辑器名称:ParticleDesigner,界面截图如下:

   

       前两天粒子编辑器购买成功(很便宜,不到10美金,比图片打包工具TP便宜多了),耍了两天后有些后悔,因为这个粒子编辑器虽然看界面像是开发编辑器的作者提供了很多的特效一样,其实都是cocos2d中例子罢了,但是反过来想,人家给做成可视编辑省去的时间远远超值于编辑器的钱了!咳咳,又扯远了,回到正题;

      至于编辑器或者在cocos2d中添加粒子特效的方法,网上N多资料和教程,这里就不多说了,下面简单说今天重点的问题;

      为了让大家更清楚的知道问题所在,我新建一个cocos2d的项目:

      1. 首先在屏幕中绘制字符串(将一个CCLabelTTF对象添加到当前Layer中);

		CCLabelTTF *label = [CCLabelTTF labelWithString:@"Welcome--Himi" fontName:@"Marker Felt" fontSize:64];
		CGSize size = [[CCDirector sharedDirector] winSize];
		label.position =  ccp( size.width /2 , size.height/2 );
		[self addChild: label];

      2.然后我将使用粒子编辑器生成的一个类似陨石导出一个名为“himi.plist”粒子文件(其中包含粒子的所有属性),将此文件导入项目中并添加到当前layer中;

        //添加一个粒子特效
        CCParticleSystem *tempSystem = [ARCH_OPTIMAL_PARTICLE_SYSTEM particleWithFile:@"himi.plist"];
        //tempSystem.positionType=kCCPositionTypeRelative;//备注1
        tempSystem.positionType=kCCPositionTypeFree;
        tempSystem.position=ccp(100,100);  
        [self addChild:tempSystem];

这里的备注1,大家留意下,后面会详细说,这里先无视;到此,整个layer中添加了一个字符串和一个粒子特效!运行项目 截图如下:



这里大概说下,粒子我设定的是一直循环播放的,是通过编辑器修改的参数,OK,下面开始到重点了:

    现在我使用一个选择器 [self scheduleUpdate]; ,让当前的layer的X坐标不断+3,代码如下:

-(void)update:(ccTime)time{
    self.position=ccp(self.position.x+3,self.position.y);
}

这里多嘴一句,在cocos2d坐标的修改尽可能的使用整体赋值,比如更改坐标,虽然这里只是更改x轴,但是我仍然对整个position进行的修改;

然后大家都应该知道,当前的layer上的粒子特效和CCLabelTTF都会跟着移动,但是对不起,这里出现问题,运行截图如下:



    这里大家肯定很奇怪,粒子效果应该是垂直的,但是实际却不是,其实这个问题真的很细节,因为一般粒子特效都是用于很短的时间进行播放-消失;

这里大概说下,其实特效的的发射器也就是粒子的起始座标其实确实一直跟着layer移动了,但是创建出来后的粒子却一直显示在创建时候的座标进行自身运动,并没有按照我们想象的移动方式进行整体移动;

    讲了这么多其实这个问题一句代码就解决了,那就是最上面创建粒子特效的代码的备注1,

 //tempSystem.positionType=kCCPositionTypeRelative;//备注1

这个positionType其实就是设置当前粒子特效中所有粒子的位置类型,默认情况下是自由模式,代码如下:

kCCPositionTypeFree

那么备注1的,kCCPositionTypeRelative  是相对模式,所以只要这里对粒子进行设置位置类型为相对模式即可,问题解决;


           OK,童鞋们该说了,这么容易描述清楚的问题与解决方案直接说不就好了 =。  =我也这么想的,但是这里我需要说两点:

   1.如果博文上来就针对问题,巴拉巴拉的说一堆,那么对于还没接触粒子这一块的童鞋而言,一点用没有,完全是丈二的和尚,xxxx你懂的~

   2.讲的这么多,还是我老说的一句话:细节决定成败;其实细节也如同基础,基础不牢,何谈提高?!

    好啦,这一篇就到这里,继续忙了;现在时刻:凌成1.24,哎、最近的博文都是夜里发,第二天谁也看不到,=。  =



作者:xiaominghimi 发表于2011/9/14 1:30:15 原文链接
阅读:21951 评论:17 查看评论

NEW BLOG!

$
0
0

可能NOIP2017后就会把博客的重心转移了

当然CSDN也会同步更新,毕竟为了访问量qwq
新博客地址

http://www.yjjr.org/

很酷炫的名字和画面

作者:qwerty1125 发表于2017/11/8 17:52:28 原文链接
阅读:195 评论:0 查看评论

Vue 脱坑记 - 查漏补缺(汇总下群里高频询问的xxx及给出不靠谱的解决方案)

$
0
0

前言

发现群里有些问题的提问重复率太高了,每次都去回答,回答的贼烦.
这里做一个大体的汇总,废话不多说,直接开始
给出方案,不是手把手..若是连问题和解决都看不懂的..应该去补充下基础知识

问题汇总

Q:安装超时(install timeout)

方案有这么些:

  • cnpm : 国内对npm的镜像版本
/*
cnpm website: https://npm.taobao.org/
*/

npm install -g cnpm --registry=https://registry.npm.taobao.org


// cnpm 的大多命令跟 npm 的是一致的,比如安装,卸载这些

yarn 和 npm 改源大法

使用 nrm 模块 : www.npmjs.com/package/nrm
npm config : npm config set registry https://registry.npm.taobao.org
yarn config : yarn config set registry https://registry.npm.taobao.org

更多内容请访问 https://juejin.im/post/59fa9257f265da43062a1b0e

这篇文章汇总了大量新手在群里的提问,回答非常全面,建议新手阅读。涉及版权问题,没有全文转载。

作者:FungLeo 发表于2017/11/8 17:55:58 原文链接
阅读:218 评论:0 查看评论

NOIP前 基础数学模板

$
0
0
/*
	created by scarlyw
	基础数学模板 
*/
#include <bits/stdc++.h>

//最大公约数 & 最小公倍数 
namespace gcd_lcm() {
//最大公约数 O(logn)
inline int gcd(int a, int b) {
	return b ? gcd(b, a % b) : a;
}

//最小公倍数 (logn)
inline int lcm(int a, int b) {
	return a * b / gcd(a, b);
}
}

//线筛 
namespace s_seive {

//线筛质数 和 莫比乌斯函数(容斥函数) O(n)
int prime[MAXN], miu[MAXN]; 
int prime_cnt, n;
bool not_prime[MAXN];
inline void seive(int n) {
	not_prime[1] = true, miu[1] = 1;
	for (int i = 2; i <= n; ++i) {
		if (!not_prime[i]) prime[++prime_cnt] = i, miu[i] = -1;
		for (int j = 1; j <= prime_cnt && prime[j] * i <= n; ++j) {
			not_prime[i * prime[j]] = true;
			if (i % prime[j] == 0) {
				miu[i * prime[j]] = 0;
				break;
			}
			miu[i * prime[j]] = -miu[i];
		}
	}
}
}

//质因子分解
namespace compose {
	
//暴力分解 O(sqrt(n))
inline void get_pactor() {
	std::cin >> n, std::cout << n << "=";
	for (int i = 2; i <= sqrt(n); ++i) {
		while (n % i == 0) {
			if (n == i) std::cout << i, exit(0);
			else std::cout << i << "*", n /= i;
		}
	}
	if (n != 1) std::cout << n;
}
}

//扩展欧几里得 
namespace extand_gcd() {

//扩欧 O(logn)最后的(x % b + b) % b是满足ax = 1(mod b)的最小x 
long long a, b, x, y, temp;
inline void ext_gcd(long long a, long long b) {
	if (b == 0) x = 1, y = 0;
	else ext_gcd(b, a % b), temp = x, x = y, y = temp - a / b * y; 
}
}

//快速幂 & 快速乘 
namespace mul_pow {

//O(log n)快速乘 
long long n, m, mod;
inline long long mod_mul(long long a, long long b) {
	long long ans = 0;
	for (; b; b >>= 1, a = (a + a) % mod)
		if (b & 1) ans = (ans + a) % mod;
	return ans;
}

//O(1)快速乘 
inline long long mod_mul(long long a, long long b) {
	return (a * b - (long long)((long double)a / mod * b) * mod + mod) % mod;
}

//O(logn)快速幂 
inline long long mod_pow(long long a, long long b) {
	long long ans = 1;
	for (; b; b >>= 1, a = mod_mul(a, a))
		if (b & 1) ans = mod_mul(ans, a);
	return ans;
}
}

//组合数 
namespace combination {

//O(n ^ 2)组合数递推,方便取模 
const int MAXN = 1000 + 10;
const int mod = 1000000000 + 7;
int c[MAXN][MAXN];
inline void get_c(int n) {
	for (int i = 0; i <= n; ++i) c[i][0] = 1, c[i][i] = 1;
	for (int i = 2; i <= n; ++i)
		for (int j = 1; j < i; ++j)
			c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
}

//O(n)预处理阶乘和阶乘逆元,然后O(1)询问组合数 
int fac[MAXN], inv[MAXN];
inline void get_c(int n) {
	fac[0] = 1;
	for (int i = 1; i <= n; ++i) fac[i] = (long long)fac[i - 1] * i % mod;
	inv[n] = mod_pow(fac[n], mod - 2);
	for (int i = n - 1; i >= 0; --i) 
		inv[i] = (long long)inv[i + 1] * (i + 1) % mod;
}

inline int c(int n, int m) {
	return (long long)fac[n] * inv[m] % mod * inv[n - m] % mod;
}
}

//进制转换 
namespace radix_change {

//十进制转任意进制 
inline void radix_10_to_a(int num, int radix) {
	static int sum[100], cnt;
	while (num) sum[++cnt] = num % radix, num /= radix;
	for (int i = cnt; i >= 1; --i) 
		if (sum[i] >= 10) std::cout << (char)(sum[i] + 'A' - 10);
	else std::cout << sum[i];
	return 0;
}

//任意进制转十进制 
inline void radix_a_to_10(int radix, char *s) {
	static int cnt, y;
	cnt = strlen(s + 1);
	for (int i = 1, y = 0; i <= cnt; ++i) {
		if (isdigit(s[i])) y = y * radix + s[i] - '0';
		else y = y * radix + s[i] - 'A' + 10;
	}
	std::cout << y;
}
}

//高精度 
namespace big_integer {

 
const int MAXN = 200 + 10;
const int DIGIT = 1000000000;
//压9位的简单高精度类 
struct big_integer {
	long long a[MAXN];
	int len;
	
	//将big_integer初始化为long long or string代表的数字 
	big_integer(long long s = 0) {
		len = 0, memset(a, 0, sizeof(a));
		do a[++len] = s % DIGIT, s /= DIGIT;
		while (s);
	}
	
	big_integer(char *s) {
		len = 0, memset(a, 0, sizeof(a));
		int s_len = strlen(s + 1);
		long long t = 0, k = 1;
		for (int i = s_len; i >= 1; --i) {
			t = t + (s[i] ^ '0') * k, k = ((k << 2) + k << 1);
			if (k == DIGIT) a[++len] = t, k = 1, t = 0;
		}
		if (t != 0) a[++len] = t;
	}
	
	//重载方括号b[i] = b.a[i]; 
	inline long long & operator [] (const int &c) {
		return a[c];
	}
	
	//重载方括号b[i] = b.a[i]; 
	inline const long long & operator [] (const int &c) const {
		return a[c];
	}
	
	//重载 <  
	inline bool operator < (const big_integer &b) const {
		if (len != b.len) return len < b.len;
		for (int i = len; i >= 1; --i) 
			if (a[i] != b[i]) return a[i] < b[i];
		return false;
	}
	
	//高精 + 高精,重载 +,返回高精和 
	inline big_integer operator + (const big_integer &b) const {
		big_integer c;
		for (int i = 1; i <= len || i <= b.len; ++i)
			c[i] += a[i] + b[i], c[i + 1] += c[i] / DIGIT, c[i] %= DIGIT;
		c.len = std::max(len, b.len) + 2;
		while (c[c.len] == 0) c.len--;
		return (c.len) ? (0) : (c.len = 0), c;
	}
	
	//高精 * 高精,重载 *,返回高精积 
	inline big_integer operator * (const big_integer &b) const {
		big_integer c;
		for (int i = 1; i <= len; ++i)
			for (int j = 1; j <= b.len; ++j) {
				int pos = i + j - 1;
				c[pos] += a[i] * b[j], c[pos + 1] += c[pos] / DIGIT;
				c[pos] %= DIGIT;
			}
		c.len = len + b.len + 3;
		while (c[c.len] == 0) c.len--;
		return (c.len) ? (0) : (c.len = 1), c;
	}
	
	//高精 - 高精,重载 -,返回高精差,默认减数 > 被减数 
	inline big_integer operator - (const big_integer &b) const {
		big_integer c;
		for (int i = 1; i <= len; ++i)
			c[i] += a[i] - b[i], (c[i] < 0) ? (c[i] += DIGIT, c[i + 1]--) : 0;
		c.len = len;
		while (c[c.len] == 0) c.len--;
		return (c.len) ? (0) : (c.len = 1), c;
	}
	
	//高精 / 单精,重载 /,返回高精商 
	inline big_integer operator / (const long long b) const {
		big_integer c;
		long long t = 0;
		for (int i = len; i >= 1; --i)
			c[i] = (a[i] + t * DIGIT) / b, t = (a[i] + t * DIGIT) % b;
		c.len = len;
		while (c[c.len] == 0) c.len--;
		return (c.len) ? (0) : (c.len = 1), c;
	}
	
	//高精 % 单精, 重载%,返回单精 
	inline big_integer operator % (const long long b) const {
		return *this - *this / b * b;
	}
	
	//输出 
	inline void write() const {
		printf("%lld", a[len]);
		for (int i = len - 1; i >= 1; --i) printf("%09lld", a[i]);
	}
} ;

//重载cin, cin >> (big_integer)a, 直接读入一个字符串,然后将a赋值为字符串 
inline std::istream & operator >> (std::istream &a, big_integer &b) {
	static char s[MAXN << 4];
	scanf("%s", s + 1), b = s;
	return a;
}

//重载cout, cout << (big_integer)b, 输出b,利用write() 
inline std::ostream & operator << (std::ostream &a, const big_integer &b) {
	b.write();
	return a;
}
}

//高斯消元
namespace gauss {

const int MAXN = 100 + 10;
int n;
double f[MAXN][MAXN], ans[MAXN];
//f[i][j]表示第i个等式中第j个变量的系数,f[i][n + 1]表示第i个等式的值 
inline void solve() {
	for (int i = 1; i <= n; ++i) {
		int pos = -1;
		for (int j = i; j <= n; ++j) 
			if (f[j][i] != 0) {
				pos = j;
				break ;
			}
		std::swap(f[pos], f[i]);
		for (int j = i + 1; j <= n; ++j) {
			double t = f[j][i] / f[i][i];
			for (int k = i; k <= n + 1; ++k)
				f[j][k] -= t * f[i][k];
		}
	}
	for (int i = n; i >= 1; --i) {
		ans[i] = f[i][n + 1] / f[i][i];
		for (int j = 1; j < i; ++j)
			f[j][n + 1] -= f[j][i] * ans[i];
	}
	for (int i = 1; i <= n; ++i) std::cout << int(ans[i] + 0.5) << " ";
}
}

//矩阵乘法 & 矩阵快速幂
namespace matrix {

const int MAXN = 200 + 10;
const int mod = 1000000000 + 7;
//矩阵类 
struct matrix {
	int a[MAXN][MAXN];
	int n;
	matrix() {}
	matrix(int n) : n(n) {
		for (int i = 1; i <= n; ++i)
			for (int j = 1; j <= n; ++j)
				a[i][j] = 0;
	}
	
	//重载 * , 矩阵乘法 
	inline matrix operator * (const matrix &b) const {
		matrix ans(n);
		for (int i = 1; i <= n; ++i)
			for (int k = 1; k <= n; ++k)
				for (int j = 1; j <= n; ++j)
					ans.a[i][j] = (ans.a[i][j] + a[i][k] * b.a[k][j]) % mod;
		return ans;
	}
	
	//重载 ^ ,矩阵快速幂 
	inline matrix operator ^ (int b) const {
		matrix ans(n), a = *this;
		for (int i = 1; i <= n; ++i) ans.a[i][i] = 1;
		for (; b; b >>= 1, a = a * a) 
			if (b & 1) ans = ans * a;
		return ans;
	}
};
}

作者:scar_lyw 发表于2017/11/8 19:05:31 原文链接
阅读:170 评论:0 查看评论

react native学习笔记16——存储篇(1)AsyncStorage

$
0
0

前言

React Native中常用的数据本地化存储的方法有如下几种:

  • AsyncStorage:以键值对的形式存储的轻量存储器,只能存储字符串数据。
  • SQLite:一种轻型的数据库,多用于移动端开发,原生应用开发中比较常见。
  • Realm:新兴的移动端数据存储方法,使用简单、跨平台、性能优越功能强大。

本文主要主要介绍第一种——AsyncStorage。

AsyncStorage介绍

AsyncStorage是react native提供的轻量存储器,类似Android中的SharePreference, iOS中的NSUserDefault,以键值对的形式存储将信息存储在本地,它是一个简单的、未加密的、异步的、持久化的Key-Value 存储系统,对于App来说是全局性的。官方建议使用时在AsyncStorage的基础上做一层抽象封装,而不是直接使用AsyncStorage。
AsyncStorage的JS代码是对原生实现的一个简易封装,提供一个更清晰的JS API、返回真正的错误对象,以及简单的单项对象操作函数。每个方法都会返回一个Promise对象。

下面是使用AsyncStorage存储数据、获取数据的简单示例。
存储数据:

try {
  await AsyncStorage.setItem('samplekey', 'lalala.');
} catch (error) {
  // Error saving data
}

获取数据:

try {
  const value = await AsyncStorage.getItem('samplekey');
  if (value !== null){
    // We have data!!
    console.log(value);
  }
} catch (error) {
  // Error retrieving data
}

常用的方法

  1. 获取键所对应的值:static getItem(key: string, callback:(error, result))
    根据键来获取值,获取的结果会放在回调函数中。

  2. 设置键-值对:static setItem(key: string, value: string, callback:(error))
    根据键来设置值。

  3. 移除键-值对:static removeItem(key: string, callback:(error))
    根据键来移除项。

  4. 合并键-值对:static mergeItem(key: string, value: string, callback:(error))
    合并现有值和输入值。

  5. 清除所有的数据:static clear(callback:(error))
    清除所有的内容。

  6. 获取所有的键:static getAllKeys(callback:(error, keys))
    获取所有的键。

  7. 清除进行中的查询操作:static flushGetRequests()
    清除所有进行中的查询操作。

  8. 获取多个键对应的值:static multiGet(keys, callback:(errors, result))
    获取多项,其中 keys 是字符串数组,比如:['k1', 'k2']

  9. 设置多项键-值对:static multiSet(keyValuePairs, callback:(errors))
    设置多项,其中 keyValuePairs 是字符串的二维数组,比如:[['k1', 'val1'], ['k2', 'val2']]

  10. 删除多项键-值对static multiRemove(keys, callback:(errors))
    删除多项,其中 keys 是字符串数组,比如:['k1', 'k2']

  11. 合并多项键-值对static multiMerge(keyValuePairs, callback:(errors))
    多个键值合并,其中 keyValuePairs 是字符串的二维数组,比如:[['k1', 'val1'], ['k2', 'val2']]

使用实例

下面示例中主要通过 AsyncStorage.getItemAsyncStorage.setItemAsyncStorage.removeItem实现AsyncStorage的增删查,如果对同一个key用setItem设置不同的值,后一次设置的值会覆盖前一次的值,以此来实现对数据的修改更新。

import React, { Component } from 'react';
import {
    StyleSheet,
    View,
    Text,
    Dimensions,
    Button,
    AsyncStorage
} from 'react-native';

const {width, height} = Dimensions.get('window');
var data = "";
const AsyncStorageKey = "ASE_";
export default class AsyncStorageExample extends Component {

    constructor(props) {
        super(props);
        this.state = {
            data:""
        };
    }

    setData(text){
        AsyncStorage.setItem(AsyncStorageKey,text,()=>{
            this.setState({
                data:text,
            });
        });
    }

    delData(){
        AsyncStorage.getItem(AsyncStorageKey,(error,text)=>{
            if(text=== null ){
                alert(AsyncStorageKey +"没有对应的值");
            }else{
                //移除键-值对
                AsyncStorage.removeItem(AsyncStorageKey,()=>{
                    this.setState({
                        data:"",
                    },()=>{
                        alert('删除成功');
                    });
                });
            }
        });
    }

    render() {
        return (
            <View style={styles.container}>
                <Button onPress={this.setData.bind(this,"我用 AsyncStorage存了一条信息")} style={styles.itemView} title="存储数据">
                </Button>

                <Button onPress={this.delData.bind(this)} style={styles.itemView} title="删除数据" >
                </Button>

                <Text style={{paddingTop:40}}>
                    AsyncStorage存储的值是:
                </Text>
                <Text>
                    {this.state.data}
                </Text>
            </View>
        );
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        backgroundColor: '#f2f2f2',
        paddingTop:20,
    },
    itemView:{
        backgroundColor:'grey',
        height:44,
        width:width,
        justifyContent:'center',
        marginTop:10,
    },
    itemText:{
        fontSize:15,
        color:'#ffffff',
        textAlign:'left',
        marginLeft:20,
    },
});
作者:teagreen_red 发表于2017/11/8 19:18:04 原文链接
阅读:235 评论:0 查看评论

leetcode题解-125. Valid Palindrome && 680. Valid Palindrome II

$
0
0

这是两道判断字符串是否为回文的题目,先来看第一道:

Given a string, determine if it is a palindrome, considering only alphanumeric characters and ignoring cases.

For example,
"A man, a plan, a canal: Panama" is a palindrome.
"race a car" is not a palindrome.

Note:
Have you consider that the string might be empty? This is a good question to ask during an interview.

For the purpose of this problem, we define empty string as valid palindrome.

本题是对于一个字符串(可能含有各种符号)判断其中的字母和数字是否可以构成回文字符串,不考虑大小写之间的区别。题目很简单,使用java内置的函数即可判断一个字符是否为字母或者数字,如果不是的话接着判断下一个字符即可:

    //45%
    public static boolean isPalindrome(String s) {
        s = s.toLowerCase();
        char [] res = s.toCharArray();
        int i=0, j=res.length-1;
        while(i <= j){
            //不是字母或者数字则遍历下一个
            while(i<=j && !Character.isLetterOrDigit(res[i])) i++;
            while(i<=j && !Character.isLetterOrDigit(res[j])) j--;
            if(res[i] != res[j])
                return false;
            else{
                i++;
                j--;
            }
        }
        return true;
    }

此外,我们还可以使用正则表达式的方法来解决,但是效率比较低只能击败8%的用户:

    //8.8%
    public boolean isPalindrome1(String s) {
        String actual = s.replaceAll("[^A-Za-z0-9]", "").toLowerCase();
        String rev = new StringBuffer(actual).reverse().toString();
        return actual.equals(rev);
    }

然后还在discuss中看到了一个效率很高的代码,思想都是一样的,不过这里作者使用数组来实现判断一个字符是否为字母或者数字,效率很高,可以击败99。5%的用户,代码如下所示:

    //99.5%
    private static final char[]charMap = new char[256];
    static{
        for(int i=0;i<10;i++){
            charMap[i+'0'] = (char)(1+i);  // numeric
        }
        for(int i=0;i<26;i++){
            charMap[i+'a'] = charMap[i+'A'] = (char)(11+i);  //alphabetic, ignore cases
        }
    }

    public boolean isPalindrome2(String s) {
        char[]pChars = s.toCharArray();
        int start = 0,end=pChars.length-1;
        char cS,cE;
        while(start<end){
            cS = charMap[pChars[start]];
            cE = charMap[pChars[end]];
            //如果不是字母或者数字则返回0
            if(cS!=0 && cE!=0){
                if(cS!=cE)return false;
                start++;
                end--;
            }else{
                if(cS==0)start++;
                if(cE==0)end--;
            }
        }
        return true;
    }

接下来看第二道题目:

Given a non-empty string s, you may delete at most one character. Judge whether you can make it a palindrome.

Example 1:
Input: "aba"
Output: True
Example 2:
Input: "abca"
Output: True
Explanation: You could delete the character 'c'.
Note:
The string will only contain lowercase characters a-z. The maximum length of the string is 50000.

本题是看资格只有小写字母的字符串中去掉任意一个字符是否可以成为回文字符串,方法也比较简单,就是逐个判断,当出现不想等到时候就尝试删除两个中的一个,然后在判断剩下的字符串是否可以形成回文字符串即可。代码如此下所示:

    public boolean validPalindrome1(String s) {
        char[] res = s.toCharArray();
        int l = -1, r = s.length();
        while (++l < --r)
            if (res[l] != res[r]) return isPalindromic(res, l, r+1) || isPalindromic(res, l-1, r);
        return true;
    }

    public boolean isPalindromic(char[] res, int l, int r) {
        while (++l < --r)
            if (res[l] != res[r]) return false;
        return true;
    }
作者:liuchonge 发表于2017/11/8 19:21:29 原文链接
阅读:230 评论:0 查看评论

Java8 I/O源码-RandomAccessFile

$
0
0

今天学习RandomAccessFile。

RandomAccessFile,用来随机访问文件的类。它支持对随机访问文件的读取和写入。这里的随机并不意味着不可控制,而是意味着可以访问文件内容的任意位置。

随机访问文件是如何实现的呢?

随机访问文件是通过“文件指针”来实现的。文件指针可看做指向文件中任意位置的光标或索引。该文件指针可以通过getFilePointer方法读取,通过seek方法设置。输入操作从文件指针开始读取字节,并随着对字节的读取而前移此文件指针。如果随机访问文件以读取/写入模式创建,则输出操作也可用;输出操作从文件指针开始写入字节,并随着对字节的写入而前移此文件指针。写入文件末尾之后的输出操作导致该数组扩展。

源码太长,放在最后,有兴趣的可以向后看。

demo

import java.io.File;
import java.io.RandomAccessFile;

import org.junit.Test;

import java.io.IOException;

/**
 * RandomAccessFile demo
 */
public class RandomAccessFileTest {

    private static final File file = new File("randomAccessFileTest.txt");

    private void prepare() {
        // 若文件存在,则删除该文件。目的是避免干扰。
        if (file.exists())
            file.delete();
    }

    // 测试seek、getFilePointer、length方法。打印结果为
    /**
     * 偏移量的设置超出文件末尾不会改变文件的长度。
     * getFilePointer():54
     * length():54
     * getFilePointer():111111
     * length():54
     * 只有在偏移量的设置超出文件末尾的情况下对文件进行写入才会更改其长度。
     */
    @Test
    public void testSeek() {
        prepare();
        try {
            RandomAccessFile raf = new RandomAccessFile(file, "rw");

            raf.writeChars("abcdefghijklmnopqrstuvwxyz\n");

            System.out.println("偏移量的设置超出文件末尾不会改变文件的长度。");
            System.out.println("getFilePointer():" + raf.getFilePointer());
            System.out.println("length():" + raf.length());
            raf.seek(111111);
            System.out.println("getFilePointer():" + raf.getFilePointer());
            System.out.println("length():" + raf.length());

            System.out.println("只有在偏移量的设置超出文件末尾的情况下对文件进行写入才会更改其长度。");

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //测试testSetLength方法。打印结果为
    /**
     * SetLength会改变文件大小,并没有改变下一个要写入的内容的位置。
     * getFilePointer():54
     * length():54
     * getFilePointer():54
     * length():111111
     */
    @Test
    public void testSetLength() {
        prepare();
        try {
            RandomAccessFile raf = new RandomAccessFile(file, "rw");

            raf.writeChars("abcdefghijklmnopqrstuvwxyz\n");

            System.out.println("SetLength会改变文件大小,并没有改变下一个要写入的内容的位置。");
            System.out.println("getFilePointer():" + raf.getFilePointer());
            System.out.println("length():" + raf.length());
            raf.setLength(111111l);
            System.out.println("getFilePointer():" + raf.getFilePointer());
            System.out.println("length():" + raf.length());

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 向文件中写入内容
     */
    @Test
    public void testWrite() {
        prepare();
        try {
            RandomAccessFile raf = new RandomAccessFile(file, "rw");

            raf.writeChars("abcdefg\n");
            raf.writeBoolean(true);
            raf.writeByte(0x51);
            raf.writeChar('h');
            raf.writeShort(0x3c3c);
            raf.writeInt(0x66);
            raf.writeLong(0x123456789L);
            raf.writeFloat(1.1f);
            raf.writeDouble(1.111);
            raf.writeUTF("潘威威的博客");
            raf.writeChar('\n');
            //追加内容
            raf.seek(raf.length());
            raf.write("追加的内容".getBytes());

            raf.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //读取文件并打印。打印结果为
    /**
     * readChar():a
     * read(byte[],int,len):
     */
    @Test
    public void testRead() {
        try {
            RandomAccessFile raf = new RandomAccessFile(file, "r");
            System.out.println("readChar():" + raf.readChar());

            raf.skipBytes(2);//跳过两个字节,即跳过了'b'字符

            byte[] buf = new byte[10];
            raf.read(buf, 0, buf.length);
            System.out.println("read(byte[],int,len):" + (new String(buf)));

            raf.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

源码

下面是观看源码的总结。

RandomAccessFile并没有继承InputStream或者OutputStream,而是实现了DataOutput和DataInput。这说明它可以操作Java基础数据类型,而且既可读,也可写。

打开文件的访问模式有四种:

  • “r”——以只读方式打开。调用结果对象的任何write方法都将导致抛出IOException。
  • “rw”——打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。
  • “rws”——打开以便读取和写入,对于 “rw”,还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备。
  • “rwd”——打开以便读取和写入,对于 “rw”,还要求对文件内容的每个更新都同步写入到底层存储设备。

一般使用前两个模式。

RandomAccessFile中读写Java基本数据类型的方法实现和DataInputStream与DataOutputStream极其相似,可以参考Java8 I/O源码-DataInputStream与DataOutputStream一文了解这些方法的实现。

/**
 * RandomAccessFile并没有继承InputStream或者OutputStream,而是实现了DataOutput和DataInput。
 * 这说明它可以操作Java基础数据类型,而且既可读,也可写。
 * 
 */
public class RandomAccessFile implements DataOutput, DataInput, Closeable {

    //文件描述符
    private FileDescriptor fd;
    //文件通道
    private FileChannel channel = null;
    //标识此文件是否既可以读又可以写
    private boolean rw;

    /**
     * 引用文件的路径
     */
    private final String path;

    //关闭锁
    private Object closeLock = new Object();
    //标识是否关闭
    private volatile boolean closed = false;

    //不同读写模式对应的值
    private static final int O_RDONLY = 1;//只读模式
    private static final int O_RDWR =   2;//读写模式
    private static final int O_SYNC =   4;
    private static final int O_DSYNC =  8;

    /**
     * 创建从中读取和向其中写入(可选)的随机访问文件流。
     * 将创建一个新的FileDescriptor对象来表示到文件的连接。
     * mode参数指定用以打开文件的访问模式。允许的值及其含意如RandomAccessFile(File,String)构造方法所指定的那样。
     * 
     * 如果存在安全管理器,则使用name作为其参数调用其checkRead方法,以查看是否允许对该文件进行读取访问。
     * 如果该模式允许写入,那么还使用name作为安全管理器的参数来调用其checkWrite方法,以查看是否允许对该文件进行写入访问。
     *
     * @param      name   文件名
     * @param      mode   访问模式,参考RandomAccessFile(File,String)构造方法
     */
    public RandomAccessFile(String name, String mode)
        throws FileNotFoundException
    {
        this(name != null ? new File(name) : null, mode);
    }

    /**
     * 创建从中读取和向其中写入(可选)的随机访问文件流,该文件由File参数指定。
     *
     * mode参数指定用以打开文件的访问模式。允许的值及其含意为:
     * "r"——以只读方式打开。调用结果对象的任何write方法都将导致抛出IOException。
     * "rw"——打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。
     * "rws"——打开以便读取和写入,对于 "rw",还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备。
     * "rwd"——打开以便读取和写入,对于 "rw",还要求对文件内容的每个更新都同步写入到底层存储设备。
     * 
     * 如果存在安全管理器,则使用name作为其参数调用其checkRead方法,以查看是否允许对该文件进行读取访问。
     * 如果该模式允许写入,那么还使用name作为安全管理器的参数来调用其checkWrite方法,以查看是否允许对该文件进行写入访问。
     */
    public RandomAccessFile(File file, String mode)
        throws FileNotFoundException
    {
        //获取文件名
        String name = (file != null ? file.getPath() : null);
        int imode = -1;
        //如果模式为只读模式
        if (mode.equals("r"))
            imode = O_RDONLY;
        else if (mode.startsWith("rw")) {
            imode = O_RDWR;
            rw = true;
            //还要根据具体的情况判断是rws还是rwd模式
            if (mode.length() > 2) {
                if (mode.equals("rws"))
                    imode |= O_SYNC;
                else if (mode.equals("rwd"))
                    imode |= O_DSYNC;
                else
                    imode = -1;
            }
        }
        //如果模式不合法,抛出异常
        if (imode < 0)
            throw new IllegalArgumentException("Illegal mode \"" + mode
                                               + "\" must be one of "
                                               + "\"r\", \"rw\", \"rws\","
                                               + " or \"rwd\"");
        //如果存在安全管理器,则使用name作为其参数调用其checkRead方法,以查看是否允许对该文件进行读取访问。
        //如果该模式允许写入,那么还使用name作为安全管理器的参数来调用其checkWrite方法,以查看是否允许对该文件进行写入访问。
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkRead(name);
            if (rw) {
                security.checkWrite(name);
            }
        }
        //如果文件名为null,抛出异常
        if (name == null) {
            throw new NullPointerException();
        }
        //如果文件路径不可用
        if (file.isInvalid()) {
            throw new FileNotFoundException("Invalid file path");
        }
        //创建一个新的FileDescriptor对象来表示到文件的连接。
        fd = new FileDescriptor();
        fd.attach(this);
        path = name;
        //以指定的模式打开指定文件
        open(name, imode);
    }

    /**
     * 返回与此流关联的不透明文件描述符对象。
     */
    public final FileDescriptor getFD() throws IOException {
        if (fd != null) {
            return fd;
        }
        throw new IOException();
    }

    /**
     * 返回与此文件关联的唯一FileChannel对象。
     */
    public final FileChannel getChannel() {
        synchronized (this) {
            if (channel == null) {
                channel = FileChannelImpl.open(fd, path, true, rw, this);
            }
            return channel;
        }
    }

    /**
     * 以指定模式打开指定文件
     * 
     * native关键字说明其修饰的方法是一个原生态方法,方法对应的实现不是在当前文件,而是在用其他语言(如C和C++)实现的文件中。
     * Java语言本身不能对操作系统底层进行访问和操作,但是可以通过JNI接口调用其他语言来实现对底层的访问。
     * JNI是Java本机接口(Java Native Interface),是一个本机编程接口,
     * 它是Java软件开发工具箱(Java Software Development Kit,SDK)的一部分。
     * JNI允许Java代码使用以其他语言编写的代码和代码库。
     * Invocation API(JNI的一部分)可以用来将Java虚拟机(JVM)嵌入到本机应用程序中,
     * 从而允许程序员从本机代码内部调用Java代码。
     * 所以想要了解open0方法的具体实现只能去查看JVM源码了。
     * 
     */
    private native void open0(String name, int mode)
        throws FileNotFoundException;

    // wrap native call to allow instrumentation
    /**
     * 参考open0(String name, int mode)
     */
    private void open(String name, int mode)
        throws FileNotFoundException {
        open0(name, mode);
    }

    // 'Read' primitives

    /**
     * 从此文件中读取一个数据字节。
     * 以整数形式返回此字节,范围在0到255(0x00-0x0ff)。如果尚无输入可用,将阻塞此方法。
     * 尽管RandomAccessFile不是InputStream的子类,但此方法的行为与 InputStream的InputStream.read()方法完全一样。
     *
     * @return   下一个数据字节,如果已到达文件的末尾,则返回-1。
     */
    public int read() throws IOException {
        return read0();
    }

    /**
     * 从此文件中读取一个数据字节。
     * 
     * 关于native的介绍请参考open0(String name, int mode)
     * /
    private native int read0() throws IOException;

    /**
     * Reads a sub array as a sequence of bytes.
     * @param b the buffer into which the data is read.
     * @param off the start offset of the data.
     * @param len the number of bytes to read.
     * @exception IOException If an I/O error has occurred.
     */
    private native int readBytes(byte b[], int off, int len) throws IOException;

    /**
     * 将最多len个数据字节从此文件读入byte数组。
     * 在至少一个输入字节可用前,此方法一直阻塞。
     * 尽管RandomAccessFile不是InputStream的子类,
     * 但此方法的行为与InputStream的InputStream.read(byte[], int, int)方法完全一样。
     */
    public int read(byte b[], int off, int len) throws IOException {
        return readBytes(b, off, len);
    }

    /**
     * 将最多b.length个数据字节从此文件读入byte数组。
     */
    public int read(byte b[]) throws IOException {
        return readBytes(b, 0, b.length);
    }

    /**
     * 将b.length个字节从此文件读入byte数组,并从当前文件指针开始。
     * 在读取到请求数量的字节之前,此方法将从该文件重复读取。
     * 在读取了请求数量的字节、检测到流的末尾或者抛出异常前,此方法一直阻塞。
     * 
     * 参考readFully(byte b[], int off, int len)
     */
    public final void readFully(byte b[]) throws IOException {
        readFully(b, 0, b.length);
    }

    /**
     * 将b.length个字节从此文件读入byte数组,并从当前文件指针开始。
     * 在读取到请求数量的字节之前,此方法将从该文件重复读取。
     * 在读取了请求数量的字节、检测到流的末尾或者抛出异常前,此方法一直阻塞。
     */
    public final void readFully(byte b[], int off, int len) throws IOException {
        int n = 0;
        //在读取到请求数量的字节之前,此方法将从该文件重复读取。
        do {
            int count = this.read(b, off + n, len - n);
            if (count < 0)
                throw new EOFException();
            n += count;
        } while (n < len);
    }

    /**
     * 尝试跳过输入的n个字节以丢弃跳过的字节。
     * 此方法可能跳过一些较少数量的字节(可能包括零)。
     * 这可能由任意数量的条件引起;在跳过n个字节之前已到达文件的末尾只是其中的一种可能。
     * 此方法从不抛出EOFException。返回跳过的实际字节数。
     * 如果n为负数,则不跳过任何字节。
     *
     * @param      n  要跳过的字节数。
     * @return     跳过的实际字节数。
     */
    public int skipBytes(int n) throws IOException {
        long pos;
        long len;
        long newpos;

        if (n <= 0) {
            return 0;
        }
        //获取文件指针当前位置
        pos = getFilePointer();
        len = length();
        newpos = pos + n;
        if (newpos > len) {
            newpos = len;
        }
        //将指针设置到新位置
        seek(newpos);

        /* 返回跳过的实际字节数 */
        return (int) (newpos - pos);
    }

    // 'Write' primitives

    /**
     * 向此文件写入指定的字节。
     * 从当前文件指针开始写入。
     */
    public void write(int b) throws IOException {
        write0(b);
    }

    /**
     * 向此文件写入指定的字节。
     * 从当前文件指针开始写入。
     * 
     * 关于native的介绍请参考open0(String name, int mode)
     */
    private native void write0(int b) throws IOException;

    /**
     * 将b.length个字节从指定byte数组写入到此文件,并从当前文件指针开始。
     * 
     * 关于native的介绍请参考open0(String name, int mode)
     */
    private native void writeBytes(byte b[], int off, int len) throws IOException;

    /**
     * 将b.length个字节从指定byte数组写入到此文件,并从当前文件指针开始。
     */
    public void write(byte b[]) throws IOException {
        writeBytes(b, 0, b.length);
    }

    /**
     * 将 len 个字节从指定 byte 数组写入到此文件,并从偏移量 off 处开始。
     */
    public void write(byte b[], int off, int len) throws IOException {
        writeBytes(b, off, len);
    }

    // 'Random access' stuff

    /**
     * 返回此文件中的当前偏移量。
     */
    public native long getFilePointer() throws IOException;

    /**
     * 设置到此文件开头测量到的文件指针偏移量,在该位置发生下一个读取或写入操作。
     * 偏移量的设置可能会超出文件末尾。
     * 偏移量的设置超出文件末尾不会改变文件的长度。只有在偏移量的设置超出文件末尾的情况下对文件进行写入才会更改其长度。
     */
    public void seek(long pos) throws IOException {
        if (pos < 0) {
            throw new IOException("Negative seek offset");
        } else {
            seek0(pos);
        }
    }

    private native void seek0(long pos) throws IOException;

    /**
     * 返回此文件的长度。
     */
    public native long length() throws IOException;

    /**
     * 设置此文件的长度。
     * 如果 length 方法返回的文件的现有长度大于 newLength 参数,则该文件将被截短。
     * 在此情况下,如果 getFilePointer 方法返回的文件偏移量大于 newLength,那么在返回此方法后,该偏移量将等于 newLength。
     * 如果 length 方法返回的文件的现有长度小于 newLength 参数,则该文件将被扩展。在此情况下,未定义文件扩展部分的内容。
     */
    public native void setLength(long newLength) throws IOException;

    /**
     * 关闭此随机访问文件流并释放与该流关联的所有系统资源。
     * 关闭的随机访问文件不能执行输入或输出操作,而且不能重新打开。
     * 如果此文件具有一个关联的通道,那么该通道也会被关闭。
     */
    public void close() throws IOException {
        synchronized (closeLock) {
            if (closed) {
                return;
            }
            closed = true;
        }
        if (channel != null) {
            channel.close();
        }

        fd.closeAll(new Closeable() {
            public void close() throws IOException {
               close0();
           }
        });
    }

    //
    //  下面都是读写Java基本数据类型的方法。这些方法是借鉴
    //  DataInputStream and DataOutputStream的.如果感兴趣可以参考 《Java8 I/O源码-DataInputStream与DataOutputStream》一文。
    // 链接为http://blog.csdn.net/panweiwei1994/article/details/78266266。
    //

    public final boolean readBoolean() throws IOException {
        int ch = this.read();
        if (ch < 0)
            throw new EOFException();
        return (ch != 0);
    }

    public final byte readByte() throws IOException {
        int ch = this.read();
        if (ch < 0)
            throw new EOFException();
        return (byte)(ch);
    }

    public final int readUnsignedByte() throws IOException {
        int ch = this.read();
        if (ch < 0)
            throw new EOFException();
        return ch;
    }

    public final short readShort() throws IOException {
        int ch1 = this.read();
        int ch2 = this.read();
        if ((ch1 | ch2) < 0)
            throw new EOFException();
        return (short)((ch1 << 8) + (ch2 << 0));
    }

    public final int readUnsignedShort() throws IOException {
        int ch1 = this.read();
        int ch2 = this.read();
        if ((ch1 | ch2) < 0)
            throw new EOFException();
        return (ch1 << 8) + (ch2 << 0);
    }

    public final char readChar() throws IOException {
        int ch1 = this.read();
        int ch2 = this.read();
        if ((ch1 | ch2) < 0)
            throw new EOFException();
        return (char)((ch1 << 8) + (ch2 << 0));
    }

    public final int readInt() throws IOException {
        int ch1 = this.read();
        int ch2 = this.read();
        int ch3 = this.read();
        int ch4 = this.read();
        if ((ch1 | ch2 | ch3 | ch4) < 0)
            throw new EOFException();
        return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
    }

    public final long readLong() throws IOException {
        return ((long)(readInt()) << 32) + (readInt() & 0xFFFFFFFFL);
    }

    public final float readFloat() throws IOException {
        return Float.intBitsToFloat(readInt());
    }

    public final double readDouble() throws IOException {
        return Double.longBitsToDouble(readLong());
    }

    public final String readLine() throws IOException {
        StringBuffer input = new StringBuffer();
        int c = -1;
        boolean eol = false;

        while (!eol) {
            switch (c = read()) {
            case -1:
            case '\n':
                eol = true;
                break;
            case '\r':
                eol = true;
                long cur = getFilePointer();
                if ((read()) != '\n') {
                    seek(cur);
                }
                break;
            default:
                input.append((char)c);
                break;
            }
        }

        if ((c == -1) && (input.length() == 0)) {
            return null;
        }
        return input.toString();
    }

    public final String readUTF() throws IOException {
        return DataInputStream.readUTF(this);
    }

    public final void writeBoolean(boolean v) throws IOException {
        write(v ? 1 : 0);
        //written++;
    }

    public final void writeByte(int v) throws IOException {
        write(v);
        //written++;
    }

    public final void writeShort(int v) throws IOException {
        write((v >>> 8) & 0xFF);
        write((v >>> 0) & 0xFF);
        //written += 2;
    }

    public final void writeChar(int v) throws IOException {
        write((v >>> 8) & 0xFF);
        write((v >>> 0) & 0xFF);
        //written += 2;
    }

    public final void writeInt(int v) throws IOException {
        write((v >>> 24) & 0xFF);
        write((v >>> 16) & 0xFF);
        write((v >>>  8) & 0xFF);
        write((v >>>  0) & 0xFF);
        //written += 4;
    }

    public final void writeLong(long v) throws IOException {
        write((int)(v >>> 56) & 0xFF);
        write((int)(v >>> 48) & 0xFF);
        write((int)(v >>> 40) & 0xFF);
        write((int)(v >>> 32) & 0xFF);
        write((int)(v >>> 24) & 0xFF);
        write((int)(v >>> 16) & 0xFF);
        write((int)(v >>>  8) & 0xFF);
        write((int)(v >>>  0) & 0xFF);
        //written += 8;
    }

    public final void writeFloat(float v) throws IOException {
        writeInt(Float.floatToIntBits(v));
    }

    public final void writeDouble(double v) throws IOException {
        writeLong(Double.doubleToLongBits(v));
    }

    @SuppressWarnings("deprecation")
    public final void writeBytes(String s) throws IOException {
        int len = s.length();
        byte[] b = new byte[len];
        s.getBytes(0, len, b, 0);
        writeBytes(b, 0, len);
    }

    public final void writeChars(String s) throws IOException {
        int clen = s.length();
        int blen = 2*clen;
        byte[] b = new byte[blen];
        char[] c = new char[clen];
        s.getChars(0, clen, c, 0);
        for (int i = 0, j = 0; i < clen; i++) {
            b[j++] = (byte)(c[i] >>> 8);
            b[j++] = (byte)(c[i] >>> 0);
        }
        writeBytes(b, 0, blen);
    }

    //使用 modified UTF-8 编码以与机器无关的方式将一个字符串写入该文件。
    //该功能是通过DataOutputStream.writeUTF方法来完成的
    public final void writeUTF(String str) throws IOException {
        DataOutputStream.writeUTF(str, this);
    }

    private static native void initIDs();

    private native void close0() throws IOException;

    static {
        initIDs();
    }
}

关于RandomAccessFile就讲到这里,想了解更多内容请参考

END.

作者:panweiwei1994 发表于2017/11/8 20:48:40 原文链接
阅读:2 评论:0 查看评论

FP-growth算法

$
0
0

demo 代码:传送门

引言

上次分享Apriori算法时,我们有提到Apriori算法在每次增加频繁项集的大小时,会重新扫描整个数据集。当数据集很大时,这会显著降低频繁项集发现的速度。而本次分享的FP-growth(frequent patten)算法就能高效地发现频繁项集。
那么在现实生活中,是否存在应用FP-growth算法的产品呢?答案是存在的,如下图所示:
why
上图中,我们在Google搜索引擎里输入why这个单词,搜索引擎会自动给我们一些推荐查询词项,这些查询词项来自互联网上经常出现的词对。查找的方法正是接下来我们要分享的FP-growth算法。

FP-growth与Apriori的区别

我们先从宏观上来看看两个算法的区别:

FP-growth算法只需要对数据库进行两次扫描,而Apriori算法对每个潜在的频繁项集都会扫描数据集判定给定模式是否频繁,因此FP-growth算法的速度要比Apriori算法快。在小规模数据集上,这不是什么问题,但当处理更大数据集时,FP-growth通常性能要比Apriori好两个数量级以上。
Apriori是发现频繁项集与关键规则的算法,但是FP-growth并不能用于发现关联规则。

FP-growth基本流程

FP-growth只会扫描数据集两次,它在发现频繁项集的基本过程如下:

1.构建FP树
2.从FP树中挖掘频繁项集

FP树

我们先来看个例子,下图有一份数据样例:
tid-example
这份数据左侧代表事务ID,右侧代表事务中的元素项。那么根据上述数据样例生成的FP树如下所示:
fp-tree
与搜索树不同的是,一个元素项可以在一棵FP树中出现多次。其中树节点给出的是集合中单个元素及其在序列中的出现次数,路径会给出该序列出现的次数。带箭头的链接叫做节点链接,表示相似项之间的链接。

构建FP树的工作流程

那么我们来详细说下该算法的工作流程。

为构建FP树,需要对原始数据集扫描两遍。第一遍对所有元素项的出现次数进行计数。并应用上次分享的Apriori原理,如果某个元素是不频繁的,那么包含该元素的超集也是不频繁的,所以不需要考虑这些超集。因此数据集的第一次扫描是用来统计元素出现的频率的。第二次扫描就只考虑剩余的频繁元素。

构建FP树

第二次扫描FP树就需要构建FP树了。因为FP树比较复杂,因此创建一个类来实现保存树的每个节点。
代码如下:

class treeNode:
    def __init__(self, nameValue, numOccur, parentNode):
        self.name = nameValue
        self.count = numOccur
        self.nodeLink = None
        self.parent = parentNode      #needs to be updated
        self.children = {} 

    def inc(self, numOccur):
        self.count += numOccur

    def disp(self, ind=1):
        print('  '*ind, self.name, ' ', self.count)
        for child in self.children.values():
            child.disp(ind+1)

上面的程序给出了FP树种节点的类的定义。除了上图FP树之外,还需要建立一个头指针表用来快速访问FP树中一个给定类型的所有元素。如下图所示:
headTable
上图中的Header Table即头指针表,这里使用一个字典来作为数据结构存储头指针表。
接下来第二次扫描数据集并构建FP树之前,需要将每个集合的元素进行排序,从而保证相同项只会表现一次。
FP构建函数如下所示:

def createTree(dataSet, minSup=1): #create FP-tree from dataset but don't mine
    headerTable = {}
    #go over dataSet twice
    for trans in dataSet:#first pass counts frequency of occurance
        for item in trans:
            headerTable[item] = headerTable.get(item, 0) + dataSet[trans]
    for k in headerTable.keys():  #remove items not meeting minSup
        if headerTable[k] < minSup: 
            del(headerTable[k])
    freqItemSet = set(headerTable.keys())
    #print 'freqItemSet: ',freqItemSet
    if len(freqItemSet) == 0: return None, None  #if no items meet min support -->get out
    for k in headerTable:
        headerTable[k] = [headerTable[k], None] #reformat headerTable to use Node link 
    #print 'headerTable: ',headerTable
    retTree = treeNode('Null Set', 1, None) #create tree
    for tranSet, count in dataSet.items():  #go through dataset 2nd time
        localD = {}
        for item in tranSet:  #put transaction items in order
            if item in freqItemSet:
                localD[item] = headerTable[item][0]
        if len(localD) > 0:
            orderedItems = [v[0] for v in sorted(localD.items(), key=lambda p: p[1], reverse=True)]
            updateTree(orderedItems, retTree, headerTable, count)#populate tree with ordered freq itemset
    return retTree, headerTable #return tree and header table

def updateTree(items, inTree, headerTable, count):
    if items[0] in inTree.children:#check if orderedItems[0] in retTree.children
        inTree.children[items[0]].inc(count) #incrament count
    else:   #add items[0] to inTree.children
        inTree.children[items[0]] = treeNode(items[0], count, inTree)
        if headerTable[items[0]][1] == None: #update header table 
            headerTable[items[0]][1] = inTree.children[items[0]]
        else:
            updateHeader(headerTable[items[0]][1], inTree.children[items[0]])
    if len(items) > 1:#call updateTree() with remaining ordered items
        updateTree(items[1::], inTree.children[items[0]], headerTable, count)

def updateHeader(nodeToTest, targetNode):   #this version does not use recursion
    while (nodeToTest.nodeLink != None):    #Do not use recursion to traverse a linked list!
        nodeToTest = nodeToTest.nodeLink
    nodeToTest.nodeLink = targetNode

上述代码中包含三个函数。第一个函数createTree()是用来构建FP树。可以从代码中看到两个嵌套for循环代表遍历数据集两次。updateTree()及updateHeader()都是用来更新FP树及头指针表的。
实验结果如下:

simpDat = fpGrowth.loadSimpDat()
simpDat
[['r', 'z', 'h', 'j', 'p'],
 ['z', 'y', 'x', 'w', 'v', 'u', 't', 's'],
 ['z'],
 ['r', 'x', 'n', 'o', 's'],
 ['y', 'r', 'x', 'z', 'q', 't', 'p'],
 ['y', 'z', 'x', 'e', 'q', 's', 't', 'm']]
initSet = fpGrowth.createInitSet(simpDat)
initSet
{frozenset({'z'}): 1,
 frozenset({'h', 'j', 'p', 'r', 'z'}): 1,
 frozenset({'s', 't', 'u', 'v', 'w', 'x', 'y', 'z'}): 1,
 frozenset({'n', 'o', 'r', 's', 'x'}): 1,
 frozenset({'p', 'q', 'r', 't', 'x', 'y', 'z'}): 1,
 frozenset({'e', 'm', 'q', 's', 't', 'x', 'y', 'z'}): 1}
importlib.reload(fpGrowth)
myFPtree,myHeaderTab = fpGrowth.createTree(initSet,3)
myFPtree.disp()
   Null Set   1
     z   5
       r   1
       x   3
         s   2
           t   2
             y   2
         t   1
           r   1
             y   1
     x   1
       s   1
         r   1

从一棵FP树中挖掘出频繁项集

这里挖掘频繁项集的思路与Apriori算法类似,首先从单元素项开始,然后在此基础上逐步构建更大的集合。因为整个挖掘频繁项集的过程是围绕FP树进行的,因此不再需要原始数据集了。
从FP树中抽取频繁项集的三个基本步骤如下:

  • 从FP树中获得条件模式基;
  • 利用条件模式基,构建一个条件FP树;
  • 迭代重复步骤(1)步骤(2),直到树包含一个元素项为止。

条件模式基

条件模式基是以所查找元素项为结尾的路径集合。每一条路径其实都是一条前缀路径。下图给出了每个频繁项的所有前缀路径。
prefix-path
从上图我们可以发现获得前缀路径的方式:利用先前创建的头指针,来遍历并回溯。

条件FP树

对于每一个频繁项,都要创建一棵条件FP树。然后在构建条件FP的过程中,再将不满足最小支持度的元素项去掉。具体代码如下:

def mineTree(inTree, headerTable, minSup, preFix, freqItemList):
    bigL = [v[0] for v in sorted(headerTable.items(), key=lambda p: p[1])]#(sort header table)
    for basePat in bigL:  #start from bottom of header table
        newFreqSet = preFix.copy()
        newFreqSet.add(basePat)
        #print 'finalFrequent Item: ',newFreqSet    #append to set
        freqItemList.append(newFreqSet)
        condPattBases = findPrefixPath(basePat, headerTable[basePat][1])
        #print 'condPattBases :',basePat, condPattBases
        #2. construct cond FP-tree from cond. pattern base
        myCondTree, myHead = createTree(condPattBases, minSup)
        #print 'head from conditional tree: ', myHead
        if myHead != None: #3. mine cond. FP-tree
            #print 'conditional tree for: ',newFreqSet
            #myCondTree.disp(1)            
            mineTree(myCondTree, myHead, minSup, newFreqSet, freqItemList)

运行效果:

importlib.reload(fpGrowth)
myFPtree,myHeaderTab = fpGrowth.createTree(initSet,3)
myFPtree.disp()
   Null Set   1
     z   5
       r   1
       x   3
         s   2
           t   2
             y   2
         t   1
           r   1
             y   1
     x   1
       s   1
         r   1
importlib.reload(fpGrowth)
freqItems = []
fpGrowth.mineTree(myFPtree,myHeaderTab,3,set([]),freqItems)
finalFrequent Item:  {'r'}
condPattBases : r {frozenset({'z'}): 1, frozenset({'x', 's'}): 1, frozenset({'t', 'x', 'z'}): 1}
head from conditional tree:  None
finalFrequent Item:  {'s'}
condPattBases : s {frozenset({'x', 'z'}): 2, frozenset({'x'}): 1}
head from conditional tree:  {'x': [3, <fpGrowth.treeNode object at 0x000001DEEEFA65C0>]}
conditional tree for:  {'s'}
   Null Set   1
     x   3
finalFrequent Item:  {'x', 's'}
condPattBases : x {}
head from conditional tree:  None
finalFrequent Item:  {'t'}
condPattBases : t {frozenset({'x', 'z', 's'}): 2, frozenset({'x', 'z'}): 1}
head from conditional tree:  {'x': [3, <fpGrowth.treeNode object at 0x000001DEEEFA0240>], 'z': [3, <fpGrowth.treeNode object at 0x000001DEEEFA0278>]}
conditional tree for:  {'t'}
   Null Set   1
     x   3
       z   3
finalFrequent Item:  {'t', 'x'}
condPattBases : x {}
head from conditional tree:  None
finalFrequent Item:  {'t', 'z'}
condPattBases : z {frozenset({'x'}): 3}
head from conditional tree:  {'x': [3, <fpGrowth.treeNode object at 0x000001DEEEFA5128>]}
conditional tree for:  {'t', 'z'}
   Null Set   1
     x   3
finalFrequent Item:  {'t', 'x', 'z'}
condPattBases : x {}
head from conditional tree:  None
finalFrequent Item:  {'x'}
condPattBases : x {frozenset({'z'}): 3}
head from conditional tree:  {'z': [3, <fpGrowth.treeNode object at 0x000001DEEEFA5358>]}
conditional tree for:  {'x'}
   Null Set   1
     z   3
finalFrequent Item:  {'x', 'z'}
condPattBases : z {}
head from conditional tree:  None
finalFrequent Item:  {'y'}
condPattBases : y {frozenset({'t', 'x', 'z', 's'}): 2, frozenset({'t', 'x', 'z', 'r'}): 1}
head from conditional tree:  {'t': [3, <fpGrowth.treeNode object at 0x000001DEEEFA5F28>], 'x': [3, <fpGrowth.treeNode object at 0x000001DEEEFA5F60>], 'z': [3, <fpGrowth.treeNode object at 0x000001DEEEFA5F98>]}
conditional tree for:  {'y'}
   Null Set   1
     t   3
       x   3
         z   3
finalFrequent Item:  {'t', 'y'}
condPattBases : t {}
head from conditional tree:  None
finalFrequent Item:  {'y', 'x'}
condPattBases : x {frozenset({'t'}): 3}
head from conditional tree:  {'t': [3, <fpGrowth.treeNode object at 0x000001DEEEFB4080>]}
conditional tree for:  {'y', 'x'}
   Null Set   1
     t   3
finalFrequent Item:  {'t', 'y', 'x'}
condPattBases : t {}
head from conditional tree:  None
finalFrequent Item:  {'y', 'z'}
condPattBases : z {frozenset({'t', 'x'}): 3}
head from conditional tree:  {'t': [3, <fpGrowth.treeNode object at 0x000001DEEEFB4C50>], 'x': [3, <fpGrowth.treeNode object at 0x000001DEEEFB4C88>]}
conditional tree for:  {'y', 'z'}
   Null Set   1
     t   3
       x   3
finalFrequent Item:  {'t', 'y', 'z'}
condPattBases : t {}
head from conditional tree:  None
finalFrequent Item:  {'y', 'z', 'x'}
condPattBases : x {frozenset({'t'}): 3}
head from conditional tree:  {'t': [3, <fpGrowth.treeNode object at 0x000001DEEEF8C860>]}
conditional tree for:  {'y', 'z', 'x'}
   Null Set   1
     t   3
finalFrequent Item:  {'t', 'y', 'z', 'x'}
condPattBases : t {}
head from conditional tree:  None
finalFrequent Item:  {'z'}
condPattBases : z {}
head from conditional tree:  None

总结

FP-growth算法是一种发现数据集中频繁模式的有效方法。可以使用FP-growth在多种文档中查找频繁单词。频繁项集的生成有着许多应用,比如购物交易、医学诊断及大气研究等。

作者:u010665216 发表于2017/11/8 21:09:50 原文链接
阅读:4 评论:0 查看评论

纪念碑谷2第一章(教学)

$
0
0

hadoop 2.x简单介绍

$
0
0

hadoop

Hadoop是什么?Hadoop是一个开发和运行处理大规模数据的软件平台,是Appach的一个用java语言实现开源软件框架,实现在大量计算机组成的集群中对海量数据进行分布式计算.

大数据的四个特征:

海量的数据规模,多样的数据类型,快速的数据流转,数据价值的体现。

hadoop2x的核心模块

Hadoop Comon:

为其他Hadoop模块提供基础设施

Hadoop HDFS

一个高可靠,高吞吐量的分布式文件系统

Hadoop MapReduce:

一个分布式的离线并行计算框架

Hadoop YARN:

一个新的MapReduce框架,任务调度与资源管理

HDFS

Hadoop的设计思想受到Google公司的GFS设计思想的启示,基于一种开源的理念实现的分布式分布式文件系统。HDFS的设计基础与目标如下。

1)硬件错误(Hardware Failure)是常态,因而需要数据冗余技术。

2)流失数据访问(Streaming Data Access),即数据批量读取而非随机读写,Hadoop擅长做数据分析而不是事务处理。

3)大规模数据集(Large Data Sets)

4)简单一致性模型(Simple Coherency Model),即为了降低系统复杂度,对文件采用一次性写多次读的逻辑设计,也就是文件一经写入,关闭,就再不要修改。

5)“Moving Computation is Cheaper than Moving Data”,通俗理解,程序采用“数据就近”原则分配节点执行。

6)Portability Across Heterogeneous Hardware and Software Platforms,即有着很强的可扩展性。



HDFS体系结构

HDFS体系结构如图1所示,它采用主从结构,Namenode属于主段,Datanode属于从端。

Namenode

1)管理文件系统的命名空间。

2)记录 每个文件数据快在各个Datanode上的位置和副本信息。

3)协调客户端对文件的访问。

4)记录命名空间内的改动或者空间本省属性的改动。

5)Namenode 使用事务日志记录HDFS元数据的变化。使用映像文件存储文件系统的命名空间,包括文件映射,文件属性等。

从社会学来看,Namenode是HDFS里面的管理者,发挥者管理、协调、操控的作用。

Datanode

1)负责所在物理节点的存储管理。

2)一次写入,多次读取(不修改)。

3)文件由数据库组成,一般情况下,数据块的大小为64MB。

4)数据尽量散步到各个节点。

从社会学的角度来看,Datanode是HDFS的工作者,发挥按着Namenode的命令干活,并且把干活的进展和问题反馈到Namenode的作用。

客户端如何访问HDFS中一个文件呢?具体流程如下。

1)首先从Namenode获得组成这个文件的数据块位置列表。

2)接下来根据位置列表知道存储数据块的Datanode。

3)最后访问Datanode获取数据。

注意:Namenode并不参与数据实际传输。

数据存储系统,数据存储的可靠性至关重要。HDFS是如何保证其可靠性呢?它主要采用如下机理。

1)冗余副本策略,即所有数据都有副本,副本的数目可以在hdfs-site.xml中设置相应的复制因子。

2)机架策略,即HDFS的“机架感知”,一般在本机架存放一个副本,在其它机架再存放别的副本,这样可以防止机架失效时丢失数据,也可以提供带宽利用率。

3)心跳机制,即Namenode周期性从Datanode接受心跳信号和快报告,没有按时发送心跳的Datanode会被标记为宕机,不会再给任何I/O请求,若是Datanode失效造成副本数量下降,并且低于预先设置的阈值,Namenode会检测出这些数据块,并在合适的时机进行重新复制。

4)安全模式,Namenode启动时会先经过一个“安全模式”阶段。

5)校验和,客户端获取数据通过检查校验和,发现数据块是否损坏,从而确定是否要读取副本。

6)回收站,删除文件,会先到回收站/trash,其里面文件可以快速回复。

7)元数据保护,映像文件和事务日志是Namenode的核心数据,可以配置为拥有多个副本。

8)快照,支持存储某个时间点的映像,需要时可以使数据重返这个时间点的状态。

Mapreduce

是一个计算框架,既然是做计算的框架,那么表现形式就是有个输入(input),mapreduce操作这个输入(input),通过本身定义好的计算模型,得到一个输出(output),这个输出就是我们所需要的结果。

MapReduce将计算过程分为两个阶段:Map和Reduce Map阶段并行处理输入数据,Reduce阶段对Map结果进行汇总。

Shuffle链接Map和Reduce两个阶段: Map Task将数据写到本地磁盘,Reduce Task从每个Map Task上读取一份数据

仅适合离线批处理:具有良好的容错性和扩展性,适合简单的批处理任务

缺点明显: 启动开销大,过多使用磁盘导致效率低下等

YARN

YARN是资源管理系统,理论上支持多种资源,目前支持CPU和内存两种资源

YARN产生背景
    直接源于MRv1在几个方面的缺陷
            扩展性受限
            单点故障
            难以支持MR之外的计算
    多计算框架各自为战,数据共享困难
            MR:离线计算框架
            Storm:实时计算框架
            Spark:内存计算框架
YARN设计目标
    通用的统一资源管理系统
        同时运行长应用程序和短应用程序
    长应用程序
        通常情况下,永不停止运行的程序
        Service、HTTP Server等
    短应用程序
        短时间(秒级、分钟级、小时级)内会运行结束的程序
        MR job、Spark Job等

YARN基本架构



ResourceManager
整个集群只有一个,负责集群资源的统一管理和调度
详细功能
        处理客户端请求
        启动/监控ApplicationMaster
        监控NodeManager
        资源分配与调度
NodeManager
整个集群有多个,负责单节点资源管理和使用
详细功能
        单个节点上的资源管理和任务管理
        处理来自ResourceManager的命令
        处理来自ApplicationMaster的命令
ApplicationMaster
每个应用有一个,负责应用程序的管理
详细功能
        数据切分
        为应用程序申请资源,并进一步分配给内部任务
        任务监控与容错
Container
对任务运行环境的抽象
描述一系列信息
        任务运行资源(节点、内存、CPU)
        任务启动命令
        任务运行环境
YARN运行过程


YARN容错性
ResourceManager
        存在单点故障;
        正在基于ZooKeeper实现HA。
NodeManager
        失败后,RM将失败任务告诉对应的AM;
        AM决定如何处理失败的任务。
ApplicationMaster
        失败后,由RM负责重启;
        AM需处理内部任务的容错问题;
        RMAppMaster会保存已经运行完成的Task,重启后无需重新运行。
YARN调度框架
    双层调度框架
        RM将资源分配给AM
        AM将资源进一步分配给各个Task
    基于资源预留的调度策略
        资源不够时,会为Task预留,直到资源充足
        与“all or nothing”策略不同(Apache Mesos)
YARN资源调度器
    多类型资源调度
        采用DRF算法(论文:“Dominant Resource Fairness: Fair Allocation of Multiple Resource Types”)
        目前支持CPU和内存两种资源
    提供多种资源调度器
        FIFO
        Fair Scheduler
        Capacity Scheduler
    多租户资源调度器
        支持资源按比例分配
        支持层级队列划分方式
        支持资源抢占

YARN资源隔离方案
    支持内存和CPU两种资源隔离
        内存是一种“决定生死”的资源
        CPU是一种“影响快慢”的资源
    内存隔离
        基于线程监控的方案
        基于Cgroups的方案
    CPU隔离
        默认不对CPU资源进行隔离
        基于Cgroups的方案
YARN支持的调度语义
    支持的语义
        请求某个特定节点/机架上的特定资源量
        将某些节点加入(或移除)黑名单,不再为自己分配这些节点上的资源
        请求归还某些资源
    不支持的语义
        请求任意节点/机架上的特定资源量
        请求一组或几组符合某种特质的资源
        超细粒度资源
        动态调整Container资源
作者:liaodehong 发表于2017/11/8 21:29:21 原文链接
阅读:192 评论:0 查看评论

Android 8.0 解决OkHttp问题:A connection to xxx was leaked. Did you forget to close a response body?

$
0
0

在Android中,我们访问网络时,最简单的方式类似与:

HttpURLConnection connection = null;
try {
    //xxxxx为具体的网络地址
    URL url = new URL("xxxxx");
    connection = (HttpURLConnection) url.openConnection();

    connection.connect();
    //进行一些操作
    ...............
} catch (IOException e) {
    e.printStackTrace();
} finally {
    if (connection != null) {
        connection.disconnect();
    }
}

最近在8.0的手机里跑类似上述代码时,突然发现会概率性地打印类似如下的log:

A connection to xxxxxx was leaked. Did you forget to close a response body?

仔细check了一下代码,发现connection用完后,已经disconnect了,
怎么还会打印这种让人觉得不太舒服的代码?

为了解决这个问题,在国内外的网站上找了很久,
但都没能找到真正可行的解决方案。

无奈之下,只好硬撸了一边源码,总算是找到了问题的原因和一个解决方案。
因此,在本片博客中记录一下比较重要的地方。


Android的源码中,我们知道URL的openConnection函数的底层实现依赖于OkHttp库,
对于这部分的流程,我之后专门写一篇文档记录一下。

现在我们需要知道的是:
OkHttp库中的创建的Http链接为RealConnection对象。
为了达到复用的效果,OkHttp专门创建了ConnectionPool对象来管理所有的RealConnection。
这有点像线程池会管理所有的线程一样。

当我们创建一个新的RealConnection时,会调用ConnectionPool的put函数:

void put(RealConnection connection) {
    assert (Thread.holdsLock(this));
    if (connections.isEmpty()) {
        //执行一个cleanupRunnable
        executor.execute(cleanupRunnable);
    }
    //将新的connection加入池子中
    connections.add(connection);
}

现在,我们来看看cleanupRunnable会干些啥:

private Runnable cleanupRunnable = new Runnable() {
    @Override public void run() {
        while (true) {
            //容易看出,其实就是周期性地执行cleanup函数
            long waitNanos = cleanup(System.nanoTime());
            if (waitNanos == -1) return;
            if (waitNanos > 0) {
                long waitMillis = waitNanos / 1000000L;
                waitNanos -= (waitMillis * 1000000L);
                synchronized (ConnectionPool.this) {
                    try {
                        ConnectionPool.this.wait(waitMillis, (int) waitNanos);
                    } catch (InterruptedException ignored) {
                    }
                }
            }
        }
    }
};

cleanup函数的真面目如下:

long cleanup(long now) {
    //记录在使用的connection
    int inUseConnectionCount = 0;

    //记录空闲的connection
    int idleConnectionCount = 0;

    //记录空闲时间最长的connection
    RealConnection longestIdleConnection = null;

    //记录最长的空闲时间
    long longestIdleDurationNs = Long.MIN_VALUE;

    synchronized (this) {
    for (Iterator<RealConnection> i = connections.iterator(); i.hasNext(); ) {
        RealConnection connection = i.next();

            // If the connection is in use, keep searching.
            // 轮询每一个RealConnection
            if (pruneAndGetAllocationCount(connection, now) > 0) {
                inUseConnectionCount++;
                continue;
            }

            idleConnectionCount++;

            //找到空闲时间最长的RealConnection
            long idleDurationNs = now - connection.idleAtNanos;
            if (idleDurationNs > longestIdleDurationNs) {
                longestIdleDurationNs = idleDurationNs;
                longestIdleConnection = connection;
            }
        }

        //空闲时间超过限制或空闲connection数量超过限制,则移除空闲时间最长的connection
        if (longestIdleDurationNs >= this.keepAliveDurationNs
                || idleConnectionCount > this.maxIdleConnections) {
            // We've found a connection to evict. Remove it from the list, then close it below (outside
            // of the synchronized block).
            connections.remove(longestIdleConnection);
        } else if (idleConnectionCount > 0) {
            // A connection will be ready to evict soon.
            //返回下一次执行cleanup需等待的时间
            return keepAliveDurationNs - longestIdleDurationNs;
        } else if (inUseConnectionCount > 0) {
            // All connections are in use. It'll be at least the keep alive duration 'til we run again.
            // 返回最大可等待时间
            return keepAliveDurationNs;
         } else {
            // No connections, idle or in use.
            return -1;
         }
    }

    //特意放到同步锁的外面释放,减少持锁时间
    Util.closeQuietly(longestIdleConnection.getSocket());
    return 0;
}

通过cleanup函数,不难看出该函数主要的目的就是:
逐步清理connectionPool中已经空闲的RealConnection。

现在唯一的疑点就是上文中的pruneAndGetAllocationCount函数了:

/**
 * Prunes any leaked allocations and then returns the number of remaining live allocations on
 * {@code connection}. Allocations are leaked if the connection is tracking them but the
 * application code has abandoned them. Leak detection is imprecise and relies on garbage
 * collection.
 */
private int pruneAndGetAllocationCount(RealConnection connection, long now) {
    //获取使用该RealConnection的对象的引用
    List<Reference<StreamAllocation>> references = connection.allocations;
    for (int i = 0; i < references.size(); ) {
        Reference<StreamAllocation> reference = references.get(i);

        //引用不为null,说明仍有java对象持有它
        if (reference.get() != null) {
            i++;
            continue;
        }

        //没有持有它的对象,说明上层持有RealConnection已经被回收了
        // We've discovered a leaked allocation. This is an application bug.
        Internal.logger.warning("A connection to " + connection.getRoute().getAddress().url()
                + " was leaked. Did you forget to close a response body?");

        //移除引用
        references.remove(i);
        connection.noNewStreams = true;

        // If this was the last allocation, the connection is eligible for immediate eviction.
        //没有任何引用时, 标记为idle,等待被cleanup
        if (references.isEmpty()) {
            connection.idleAtNanos = now - keepAliveDurationNs;
            return 0;
        }
    }

    return references.size();
}

从上面的代码可以看出,pruneAndGetAllocationCount发现没有被引用的RealConnection时,
就会打印上文提到的leaked log。

个人猜测,如果开头的代码执行完毕后,GC先回收HttpURLConnection(非直接持有)等持有RealConnection的对象,后回收RealConnection。
且在回收HttpURLConnection后,回收RealConnection前,刚好执行了pruneAndGetAllocationCount,就可能会打印这种log。
这也是注释中提到的,pruneAndGetAllocationCount依赖于GC。

不过从代码来看,这并没有什么问题,Android系统仍会回收这些资源。

在文章开头的代码中,最后调用的HttpURLConnection的disconnect函数。
该函数仅会调用StreamAllocation的cancel函数,且最终调用到RealConnection的cancel函数:

public void cancel() {
    // Close the raw socket so we don't end up doing synchronous I/O.
    Util.closeQuietly(rawSocket);
}

可以看出,该方法仅关闭了socket,并没有移除引用,不会解决我们遇到的问题。


经过不断地尝试和阅读源码,我发现利用下述方式可以解决这个问题:

HttpURLConnection connection = null;
try {
    //xxxxx为具体的网络地址
    URL url = new URL("xxxxx");
    connection = (HttpURLConnection) url.openConnection();

    connection.connect();
    //进行一些操作
    ...............
} catch (IOException e) {
    e.printStackTrace();
} finally {
    if (connection != null) {
    try {
        //主动关闭inputStream
        //这里不需要进行判空操作
        connection.getInputStream().close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        connection.disconnect();
    }
}

当我们主动关闭HttpURLConnection的inputStream时,将会先后调用到StreamAllocation的noNewStreams和streamFinished函数:

public void noNewStreams() {
    deallocate(true, false, false);
}

public void streamFinished(HttpStream stream) {
    synchronized (connectionPool) {
        if (stream == null || stream != this.stream) {
            throw new IllegalStateException("expected " + this.stream + " but was " + stream);
        }
    }
    //调用deallocate
    deallocate(false, false, true);
}

//连续调用两次,第1、3个参数分别为true
private void deallocate(boolean noNewStreams, boolean released, boolean streamFinished) {
    RealConnection connectionToClose = null;
    synchronized (connectionPool) {
        if (streamFinished) {
            //第二次,stream置为null
            this.stream = null;
        }

        if (released) {
            this.released = true;
        }

        if (connection != null) {
            if (noNewStreams) {
                //第一次,noNewStreams置为true
                connection.noNewStreams = true;
            }

            //stream此时为null, 其它两个条件满足一个
            if (this.stream == null && (this.released || connection.noNewStreams)) {
                //就可以执行release函数
                release(connection);
                if (connection.streamCount > 0) {
                    routeSelector = null;
                }

                //idle的RealConnection可以在下文被关闭
                if (connection.allocations.isEmpty()) {
                    connection.idleAtNanos = System.nanoTime();
                    if (Internal.instance.connectionBecameIdle(connectionPool, connection)) {
                        connectionToClose = connection;
                    }
                }
                connection = null;
            }
        }
    }

    if (connectionToClose != null) {
        Util.closeQuietly(connectionToClose.getSocket());
    }
}

//最后看看release函数
private void release(RealConnection connection) {
    for (int i = 0, size = connection.allocations.size(); i < size; i++) {
        Reference<StreamAllocation> reference = connection.allocations.get(i);
        //移除该StreamAllocation对应的引用
        //解决我们遇到的问题
        if (reference.get() == this) {
            connection.allocations.remove(i);
            return;
        }
    }
    throw new IllegalStateException();
}

到此,我们终于知道出现该问题的原因及对应的解决方案了。

上述代码省略了HttpURLConnection及底层OkHttp的许多流程,
仅给出了重要的部分,后续我会专门写一篇博客来补充分析这部分代码。


这个问题说实话,个人感觉并不是很重要,
但想真正明白原理,还是需要细致阅读源码的。
一旦真正搞懂,确实有点GAI爷歌里的感觉:
一往无前虎山行,拨开云雾见光明。

作者:Gaugamela 发表于2017/11/8 21:31:57 原文链接
阅读:222 评论:0 查看评论

从零开始前端学习[45]:js中的所谓的事件类型,鼠标事件,表单事件,键盘事件以及系统事件

$
0
0

js中的所谓的事件类型

  1. 鼠标事件
  2. 表单事件
  3. 键盘事件
  4. 系统事件

提示:
博主:章飞_906285288
博客地址:http://blog.csdn.net/qq_29924041


鼠标事件

可以想象一下, 我们平时在使用鼠标的时候都是有哪些操作的,单击?双击?移动?亦或者按下,抬起,移入,移出等等一系列的事件,下面就主要针对鼠标的事件进行一点点小测试

事件类型 相关函数
点击事件 onclick
移入/移出 onmouseover/onmouseout
移入/移出 onmouseenter/onmouseleave
双击事件 ondbclick
鼠标按下事件 onmousedown
鼠标弹起事件 onmousedown
鼠标移动事件 onmousemove
鼠标抬起事件 onmouseup

上面主要有两个需要注意的地方,一个就是ondbclick,是鼠标双击事件,还有鼠标的移入移出是有两个对称函数的,一个就onmouseenter/onmouseleave,另外一个就是onmouseover和onmouseout,他们的使用都是对称的。

代码如下所示:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <meta charset="UTF-8"><!--申明当前网页的编码集UTF-8-->
  <meta name="Generator" content="EditPlus®">   <!--编辑器的名称-->
  <meta name="Author" content="作者是谁">       
  <meta name="Keywords" content="关键词">
  <meta name="Description" content="描述和简介">
  <style type="text/css">                                        
        body,dl,dd,dt,p,h1,h2,h3,h4,h5,h6{ margin: 0;}
        ul,ol{margin: 0; list-style: none; padding: 0;}
        a{ text-decoration: none; }
        *{ margin: 0; padding: 0; }
        .main{width: 700px;margin: 50px auto;box-shadow: 0 0 10px 0px blue;padding: 10px}
        p{width: 150px;height: 150px;box-shadow: 0 0  10px 0px deeppink;text-align: center;line-height: 150px;margin: 10px}
  </style>
</head>
<body>
  <div class="main">
    <p id="mouse_event_1">1</p>
    <p id="mouse_event_2">2</p>
    <p id="mouse_event_3">3</p>
  </div>

<script>
  var mouse_event_1 = document.getElementById("mouse_event_1");
  var mouse_event_2 = document.getElementById("mouse_event_2");
  var mouse_event_3 = document.getElementById("mouse_event_3");
  mouse_event_1.onclick = function () {
      mouse_event_1.innerHTML = "onclick Event";
  }
//  mouse_event_1.onmouseover = function () {
//      mouse_event_1.innerHTML = "onmouseover";
//  }
//  mouse_event_1.onmouseout = function () {
//      mouse_event_1.innerHTML = "onmouseout";
//  }
  mouse_event_1.onmouseleave = function () {
      mouse_event_1.innerHTML = "onmouseleave";
  }
  mouse_event_1.onmouseenter = function () {
      mouse_event_1.innerHTML = "onmouseenter";
  }
  mouse_event_2.onmousedown = function () {
      mouse_event_2.innerHTML = "onmousedown"
  }
  mouse_event_2.onmouseup = function () {
      mouse_event_2.innerHTML = "onmouseup"
  }
  mouse_event_3.ondblclick = function () {
      mouse_event_3.innerHTML = "ondbclick"
  }
</script>
</body>
</html>

这里写图片描述


表单事件

之前我们学了很多表单相关的东西,input等标签,那这个表单事件是什么??也就是对这些表单元素提交相关操作的时候,所需要的一些事件信息,那这些表单事件有哪些分类呢???
如下所示:

  • 元素失去焦点事件
  • 元素获得焦点事件
  • 当用户改变表单内容的事件
  • 重置按钮被点击之后的事件
  • 提交按钮被点击之后的事件

如下表所示:

事件类型 相关函数
元素失去焦点 onblur
元素获得焦点 onfocus
用户改变表单的内容 onchange
用户点击重置按钮 onreset
提交按钮被点击 onsubmit
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <meta charset="UTF-8"><!--申明当前网页的编码集UTF-8-->
  <meta name="Generator" content="EditPlus®">   <!--编辑器的名称-->
  <meta name="Author" content="作者是谁">       
  <meta name="Keywords" content="关键词">
  <meta name="Description" content="描述和简介">
  <style type="text/css">                                        
        body,dl,dd,dt,p,h1,h2,h3,h4,h5,h6{ margin: 0;}
        ul,ol{margin: 0; list-style: none; padding: 0;}
        a{ text-decoration: none; }
        *{ margin: 0; padding: 0; }
    .main{width: 800px;margin: 40px auto;box-shadow: 0 0 10px 0 deeppink}
    form{margin: auto}
    p{height: 50px;text-align: center;}
    .main #text{width: 300px;height: 40px}
    .main #btn_submit{width: 50px;height: 40px}
    .main #btn_reset{width: 50px;height: 40px}
  </style>
</head>
<script>
    function myFunction(){
        var x = document.getElementById("text");
        x.value=x.value.toUpperCase();
    }
</script>
<body>
  <div class="main">
    <form action="">
      <p id="output">output</p>
      <input id="text" type="text" onchange="myFunction()">
      <input id="btn_submit" type="submit">
      <input id="btn_reset" type="reset">
    </form>
  </div>
  <script>
    var text = document.getElementById("text");
    var btn_submit = document.getElementById("btn_submit");
    var btn_reset = document.getElementById("btn_reset");
    var output = document.getElementById("output");
    text.onblur = function () {
        output.innerHTML = "onblur"
    }
    text.onfocus = function () {
        output.innerHTML = "onfocus";
    }
    text.onchange = function () {
        output.innerHTML = "onchange";
    }
    btn_submit.onclick = function () {
        console.log("onsubmit");
    }
    btn_reset.onreset = function () {
        console.log("onreset");
    }

  </script>
</body>
</html>

注意以上,如果测试onchange的时候,需要将其他的都给注释掉,只留下onchange对应的表单相关的信息,因为会造成冲突
还有就是表单的onreset和onsubmit,因为表单提交或者重置后浏览器会清除掉相关信息,这个时候也不会有打印,一般这两个事件并不会经常使用到,因为他们都可以使用onclick来进行代替


键盘事件

键盘事件,顾名思义,也就是跟键盘相关的一些事件
主要有以下几个分类:

事件类型 相关函数
onkeydown 某个键盘的键被按下
onkeypress 某个键盘的键被按下或者按住
onkeyup 某个键盘的键被松开

键盘事件可能使用的不太多,暂时不赘述


系统事件

对于系统事件,主要是有以下几个分类

事件类型 相关函数
onload 某个页面或图像被完成加载,整个html页面,只允许有一次onload的过程
onresize 窗口或框架被调整尺寸的时候
onselect 文本被选中的时候的事件
onerror 当加载文档或图像时发生某个错误,这个就是文档加载路径出现了问题的时候

关于以上几个系统事件,其实使用都是比较简单的,这个可以结合css里面的resize等属性来进行联合使用监听。在这里也不赘述。

作者:qq_29924041 发表于2017/11/8 21:47:04 原文链接
阅读:204 评论:0 查看评论

BZOJ2208 [Jsoi2010]连通数

$
0
0

标签:tarjan,bitset,拓扑排序

Description


Input

输入数据第一行是图顶点的数量,一个正整数N。 接下来N行,每行N个字符。第i行第j列的1表示顶点i到j有边,0则表示无边。

Output

输出一行一个整数,表示该图的连通数。

Sample Input

3
010
001
100

Sample Output

9

HINT

对于100%的数据,N不超过2000。

 

分析:

Tarjan缩点重新构图

F[i][j]表示i和j点之间的联通情况,然后按照拓扑序从后往前整体取或,最后用每个联通块的大小来更新答案

Code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define dep(i,a,b) for(int i=a;i>=b;i--)
#define ll long long
#define mem(x,num) memset(x,num,sizeof x)
#ifdef WIN32
#define LL "%I64d"
#else
#define LL "%lld"
#endif
using namespace std;
inline ll read()
{
	ll f=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
const int maxn=2006;
int n,cnt=0,scc,top=0,tim,lim,ans=0;
int head[maxn],h[maxn],r[maxn],dfn[maxn],low[maxn],que[maxn],inque[maxn];
int belong[maxn],hav[maxn],mark[maxn][maxn];
char ch[maxn];
struct edge{int to,next;}e[4000006],ed[4000006];
#define reg(x) for(int i=head[x];i;i=e[i].next)
#define v e[i].to
#define newreg(x) for(int i=h[x];i;i=ed[i].next)
#define newv ed[i].to
void tarjan(int x)
{
	int now=0;
	dfn[x]=low[x]=++tim;que[++top]=x;inque[x]=1;
	reg(x)
	    if(!dfn[v]){tarjan(v);low[x]=min(low[x],low[v]);}
	    else if(inque[v])low[x]=min(low[x],dfn[v]);
	if(low[x]==dfn[x]){
	    scc++;
		while(now!=x){
		    now=que[top--];
			inque[now]=0;belong[now]=scc;hav[scc]++;
		}
	}
}

void rebuild()
{
	cnt=0;
	rep(k,1,n)
	    reg(k)
	        if(belong[k]!=belong[v])ed[++cnt]=(edge){belong[k],h[belong[v]]},h[belong[v]]=cnt,r[belong[k]]++;
}
int main()
{
	n=read();
	lim=n/30+1;
	rep(i,1,n){
		scanf("%s",ch);
		rep(j,1,n)
		    if(ch[j-1]-'0')e[++cnt]=(edge){j,head[i]},head[i]=cnt;
    }
    rep(i,1,n)
        if(!dfn[i])tarjan(i);
    rebuild();
    top=0;int now;
    rep(i,1,n)mark[belong[i]][i/30+1]|=(1<<(i%30));
    rep(i,1,scc)if(!r[i])que[++top]=i;
    while(top){
    	now=que[top--];
    	newreg(now){
    		rep(j,1,lim)mark[newv][j]|=mark[now][j];
    	    r[newv]--;
    	    if(!r[newv])que[++top]=newv;
    	}
    }
    rep(i,1,scc)
        rep(j,1,n)
            if((mark[i][j/30+1])&(1<<(j%30)))ans+=hav[i];
    cout<<ans<<endl;
    return 0;
}


作者:qwerty1125 发表于2017/11/8 21:51:48 原文链接
阅读:149 评论:0 查看评论

[Unity 热更新]tolua集成

$
0
0

LuaFramework采用了pureMVC,并集成了多个第三方库,对于项目而言,我们可以直接使用tolua集成到项目,也可以使用LuaFramework删掉pureMVC相关内容集成到项目,使用自己项目的游戏框架。下面为使用tolua集成到项目中可能遇到的问题整理:

1、lua的后缀是不被支持打包进assertbundle的,一般把 .lua后缀 变为.lua.txt 或者 .lua.bytes 进行打包。

2、LuaFramework打包Lua到AssetBundle时,是采用单个目录下的打包成一个AssetBundle,这里可以改成自己喜欢的方式。

3、Lua文件的AssetBundle加载,使用LuaFramework采用的加载方式即可。

4、Lua代码加密问题,lua本身可以使用luac将脚本编译为字节码(bytecode)从而实现加密。

5、LuaFramework实现的部分脚本可以利用起来:
①LuaBehaviour.cs //便于调用Lua代码中的Awake();Start();Update()……
②LuaLoader.cs //Lua的AssetBundle加载
③LuaManager.cs
④LuaHelper.cs //给Lua提供调用C#接口
⑤Util.cs //工具类

6、编辑器的使用,之前有推荐IntelliJ+EmmyLua开发的方式,我们可以导出相关c#文件到Lua,便于开发,提高开发效率。

7、配置表的读取:即从配表到Lua文件的转换过程,github上有许多工具。

8、LuaFramework集成了pbc,sproto等第三方库,tolua本身只包含cjson,protoc-gen-lua等,对于需要支持协议热更新的项目,需要选择一个把proto文件转换到Lua的工具:pbc和pblua(protoc-gen-lua)都各有优缺点(lua的支持问题[有反馈不支持lua53的],协议嵌套问题等)。

9、自己编译底层库,这个过程还是相当繁琐的……

作者:husheng0 发表于2017/11/9 1:07:48 原文链接
阅读:2 评论:0 查看评论
Viewing all 35570 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>