<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>空雪小屋 &#187; 未分类</title>
	<atom:link href="http://blog.sorayuki.net/?cat=1&#038;feed=rss2" rel="self" type="application/rss+xml" />
	<link>http://blog.sorayuki.net</link>
	<description>现在域名是 blog.sorayuki.net ~</description>
	<lastBuildDate>Tue, 29 Nov 2022 08:27:41 +0000</lastBuildDate>
	<language>zh-CN</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=4.0</generator>
	<item>
		<title>自制SNTP服务器</title>
		<link>http://blog.sorayuki.net/?p=200</link>
		<comments>http://blog.sorayuki.net/?p=200#comments</comments>
		<pubDate>Sun, 30 Dec 2012 06:37:54 +0000</pubDate>
		<dc:creator><![CDATA[空雪梦见]]></dc:creator>
				<category><![CDATA[未分类]]></category>

		<guid isPermaLink="false">http://blog.sorayuki.net/?p=200</guid>
		<description><![CDATA[其实主要目的呢，是为了玩玩Boost.asio函数库。 最近在看Windows并发编程指南，说到IO完成端口、 &#8230; <a href="http://blog.sorayuki.net/?p=200" class="more-link">继续阅读<span class="screen-reader-text">自制SNTP服务器</span> <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>其实主要目的呢，是为了玩玩Boost.asio函数库。<br />
最近在看Windows并发编程指南，说到IO完成端口、线程池什么的。然后我想到Boost.asio说内部实现用的是IO完成端口，并且它也可以搞多线程，于是决定试试看。</p>
<p>因为SNTP协议是相对比较简单的嘛，所以就用它来做实验。按照RFC1361写了个简单的服务器，经过测试确实Windows和Linux下都能编译都能运行。目前只开了一个线程给它跑，不过个人感觉效率很好速度很快。</p>
<p>然后其实本来是打算给Windows那个Internet时间同步用的，但是搞到最后Windows也不认它，总是同步失败。 我都用NTPTOOL工具去测服务器了，发现很正常啊，不知道Windows是不是用了自己的NTP协议还是怎么的。总之最后我又给写了个Windows用的NTP客户端。</p>
<p>服务器启动的时候会先到一台NTP服务器上去获取时间，计算和当前机器上的时间的差，然后以后向客户端提供时间的时候都会用这个差作为偏移去校正。之所以这么做，是因为OpenVZ虚拟化环境下的我那台VPS不让改系统时间（汗）</p>
<p>这个步骤暂时没提供任何设置，要去掉只能改代码。本来就是实验嘛所以也不会关注那么多。</p>
<p>服务器和客户端的代码都在这个压缩包里（同一个文件里），有兴趣的欢迎交流w 还有一些出错的时候是没有输出提示的，原因：懒（殴</p>
<p><a href="http://blog.sorayuki.net/wp-content/uploads/2012/12/sorasntp.zip">sorasntp</a></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.sorayuki.net/?feed=rss2&#038;p=200</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>在C++中进行字符串编码转换</title>
		<link>http://blog.sorayuki.net/?p=185</link>
		<comments>http://blog.sorayuki.net/?p=185#comments</comments>
		<pubDate>Mon, 22 Oct 2012 08:44:24 +0000</pubDate>
		<dc:creator><![CDATA[空雪梦见]]></dc:creator>
				<category><![CDATA[未分类]]></category>
		<category><![CDATA[程序设计]]></category>

		<guid isPermaLink="false">http://blog.sorayuki.net/?p=185</guid>
		<description><![CDATA[最近做了个Jubeat Analyzer的谱面转成Yubiosi谱面的工具。在制作工具的过程中，遇到了一个问题 &#8230; <a href="http://blog.sorayuki.net/?p=185" class="more-link">继续阅读<span class="screen-reader-text">在C++中进行字符串编码转换</span> <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>最近做了个Jubeat Analyzer的谱面转成Yubiosi谱面的工具。在制作工具的过程中，遇到了一个问题，就是源谱面文件中字符串的编码问题。</p>
<p>Jubeat Analyzer是日本人造的软件，然后它保存的谱面又没有用Unicode，所以在中文操作系统上看就会乱码。对于编码转换问题，其实C++是可以直接支持的。对于一个支持输出宽字符的文件流，把区域设置到japanese就可以了，像这样：</p>
<pre>std::locale jpLoc("japanese");
std::wfstream fs("c:\\nanika.txt", std::ios::in);
fs.imbue(jpLoc);</pre>
<p>如果问题这么简单就解决的话就好了……</p>
<p>问题是，源文件中不仅仅包含ShiftJIS编码的部分，而且还包含GBK编码的部分。比如某些地方的注释，某些 m="音乐文件名" 的地方。因为如果音乐文件名用的是ShiftJIS，那么在中文系统下就没办法正常读取音乐了。</p>
<p>这种情况下，来一个imbue不能解决问题。因为它读到ShiftJIS不能表示的字符的时候，这个流就给你eof掉了……</p>
<p>既然这样，那我就一行一行读，不同情况不同处理。自己转成Unicode。好吧，怎么转</p>
<p>用MultiByteToWideChar固然简单，但是我想要用C++标准库去做而不是调用系统API。因为你看，wfstream都可以读取ShiftJIS文件到wstring，那么说明内部一定有什么代码实现编码转换的。我想办法调用这编码就行了。<span id="more-185"></span></p>
<p>因为宽字符的stream不是可以用imbue去设置区域吗，所以第一个想到的是</p>
<pre>std::wstringstream wss;
wss.imbue(jpLoc);
wss &lt;&lt; line;
std::wstring wline = wss.str();</pre>
<p>可惜这样子根本不行。</p>
<p>那它内部到底调用了什么东西啊。那我用调试器一步一步跟踪，step in 进去，看看它到底干了什么。写了如下代码：</p>
<pre>std::locale jpLoc("japanese");
std::wfstream fs("c:\\nanika.txt", std::ios::in);
fs.imbue(jpLoc);
std::wstring wsline;
getline(fs, wsline);</pre>
<p>然后开始调试跟踪。一步步深入， 当调用堆栈变成这样子的时候：</p>
<pre>在VS2012里，
std::basic_filebuf&lt;wchar_t,std::char_traits&lt;wchar_t&gt; &gt;::uflow() line 501 C++
std::basic_filebuf&lt;wchar_t,std::char_traits&lt;wchar_t&gt; &gt;::underflow() line 460 C++
std::basic_streambuf&lt;wchar_t,std::char_traits&lt;wchar_t&gt; &gt;::sgetc() line 153 C++
std::getline&lt;wchar_t,std::char_traits&lt;wchar_t&gt;,std::allocator&lt;wchar_t&gt; &gt;(std::basic_istream&lt;wchar_t,std::char_traits&lt;wchar_t&gt; &gt; &amp;&amp; _Istr, std::basic_string&lt;wchar_t,std::char_traits&lt;wchar_t&gt;,std::allocator&lt;wchar_t&gt; &gt; &amp; _Str, const wchar_t _Delim) line 414 C++
std::getline&lt;wchar_t,std::char_traits&lt;wchar_t&gt;,std::allocator&lt;wchar_t&gt; &gt;(std::basic_istream&lt;wchar_t,std::char_traits&lt;wchar_t&gt; &gt; &amp; _Istr, std::basic_string&lt;wchar_t,std::char_traits&lt;wchar_t&gt;,std::allocator&lt;wchar_t&gt; &gt; &amp; _Str) line 485 C++</pre>
<p>发现了转换的代码。 长这样的：</p>
<pre>_Pcvt-&gt;in(_State,
&amp;*_Str.begin(), &amp;*_Str.begin() + _Str.size(), _Src,
&amp;_Ch, &amp;_Ch + 1, _Dest)</pre>
<p>参数略难懂。虽然输入输出缓冲区的首位指针是很容易懂啦，那其他动东西呢……</p>
<p>果然还是上网去搜，搜出来它是这样说的，</p>
<p>_State是保存转换的状态。这个in函数，它是const的，这样的话就没办法记录转换的状态了。于是它用了这个_State来保存转换的状态。状态是啥？比如一个ShiftJIS的转换器，你送汉字的第一个字节进去， 它自然转不出来什么东西。那它就保存下来，然后等第二个字节。这个_State就是给它保存状态的。</p>
<p>再然后，_Src和_Dest，其实是保存了转换的进度。正常情况下它是会全部转完，但是如果转一半它遇上了什么错误，就会停下来。这个时候_Src和_Dest就能看到它转到哪里了。</p>
<p>然后于此对应的也有一个out函数。功能正好相反（一个是转过去，一个是转回来）。</p>
<p>好，那现在就是那个_Pcvt，我要怎么得到。因为它似乎构造函数还是析构函数的是不给用户用的，正常方法来构造肯定不行，它应该有留什么路吧。上网搜搜，发现标准库里面有这么一个函数给你取得这个对象的：</p>
<pre>const std::codecvt&lt;wchar_t, char, int&gt;&amp; std::use_facet&lt; std::codecvt&lt;wchar_t, char, int&gt; &gt;(const std::locale&amp;)</pre>
<p>对象拿到以后，就可以自己试验一下那种做法了：</p>
<pre>#include &lt;iostream&gt;
#include &lt;locale&gt;
#include &lt;vector&gt;
#include &lt;string&gt;

using namespace std;

int main()
{
    locale jpLoc("japanese");
    typedef std::codecvt&lt;wchar_t, char, std::mbstate_t&gt; MyCodeCvt;
    const MyCodeCvt&amp; cvt = std::use_facet&lt;MyCodeCvt&gt;(jpLoc);

    std::wstring wt(L"日本語");
    std::vector&lt;char&gt; buf(wt.length() * 2 + 1);
    const wchar_t* inpProgress;
    char* outProgress;
    mbstate_t state = 0;

    cvt.out(state,
        &amp;wt[0], &amp;wt[0] + wt.length(), inpProgress,
        &amp;buf[0], &amp;buf[0] + buf.size(), outProgress);
    if (inpProgress != &amp;wt[0] + wt.length())
        std::cerr &lt;&lt; "convert failed!" &lt;&lt; std::endl;
    else {
        *outProgress = 0;

        std::string jpStr(&amp;buf[0]);
        std::cout &lt;&lt; jpStr &lt;&lt; std::endl;
    }
    return 0;
}</pre>
<p>うふふ♥</p>
<p>补充：</p>
<p>1、如果用locale jpLoc("japanese")构造雕像的话，再fs.imbue(jpLoc)塞到wfstream里面，这样输出数字的时候它也会给你用特殊写法，比如fs &lt;&lt; 1000就给你"1,000"了。只要它管字符串编码转换而不管其他的，用locale jpLoc("japanese", std::locale::ctype)来构造。ctype指的是只管字符</p>
<p>2、C++的新标准里面似乎有个std::wstring_convert来给你干这事。更方便</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.sorayuki.net/?feed=rss2&#038;p=185</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
