May 2 2009

เลือกเครื่องมือให้ถูกงาน: C++ ปะทะ C#

Category: .NET | Nativem3rLinEz @ 04:42

งานที่ผมต้องทำต่อไปใน day job มันค่อนจะเกี่ยวกับ C++/Unix เยอะหน่อย อยากมีประสบการณ์กับพวกมันมากกว่านี้ก็เลยตั้งใจลองเขียนโปรแกรมเล่นๆขึ้นมาดู

โปรแกรมที่ต้องการจะเขียนจะมีหน้าที่คือดึง Update ของคนๆหนึ่งมาจาก Twitter แสดงบนหน้าจอ (ใน Console) แล้วแยกเฉพาะส่วนที่เป็นลิงค์ใน Tweet อันนั้นมาแสดงคู่กันด้วย

นั่งทำตั้งแต่บ่ายแก่ๆ มาเรื่อยๆ เพิ่งมาเสร็จเมื่อกี้ o__o!

สรุปปัญหาที่เจอและสิ่งที่เรียนรู้ดังนี้

เลือกใช้ VIM เป็น Editor อันนี้ลองใช้มาซักระยะแล้ว รู้สึกชอบมากกว่า Emacs นะครับ ;P รู้สึกว่าตัว VIM มัน Simple ดี แล้วแทบจะมีอยู่ทุกที่ หัดทีนึงคุ้มเลยทีเดียว มันก็มีพวก script มาให้ลงเพิ่มได้เยอะแยะนะครับ เช่นตัวช่วย browse โค้ด หรือ theme หรือตัวช่วยทำ auto-complete ที่ใน VIM จะมี feature ชื่อ omni-completion ให้ด้วย (เป็น auto-com แบบ context-sensitive พูดง่ายๆ คือฉลาด) แต่ก็ยังไม่รู้สึก “เหมือนอยู่บ้าน” เท่า Visual Studio

Build โดยใช้ Makefile อันนี้เล่นเอาปวดหัว ตอนแรกคิดว่าพวก automake autoconf จะช่วยทำ Makefile ให้ได้ง่ายๆแบบไม่เปลืองแรง แต่จริงๆพวกนั้นก็ต้องเขียนไฟล์ขึ้นมาเพิ่มเหมือนกัน สุดท้ายเลยลงมือหัดเขียน Makefile เอง (และก็ได้แบบพิการๆแต่ทำงานได้ออกมา)

เรียนรู้ option ของ g++ ปกติแล้วใช้ Visual Studio ก็เอาไฟล์ยัดเข้าโปรเจค แล้วก็กด F5 ก็เอาโปรแกรมมารันเล่นได้แล้ว แต่พอต้องใช้คำสั่ง cmd ในการ compile – link เล่นเอาเหนื่อยเหมือนกัน สรุปว่าใช้ดังนี้

g++ –L path/to/lib1 –L path/to/lib2 –ldynamclib –I path/to/include –o execname source1.o source2.o staticlib.a

หรือถ้าจะสร้าง object ไฟล์อย่างเดียว

g++ –c main.cpp main.o

การใช้ Libraries ภายนอก C++ มันแทบไม่มีอะไรมาให้ใช้เลยนอกจาก STL (Standard Template Library) อยากได้อะไรต้องหาเองหมด ในทีนี้ต้องหา

  • Lib สำหรับการอ่าน XML เลือกใช้ TinyXML
  • Lib สำหรับการทำ HTTP Request เลือกใช้ libcurl
  • Lib สำหรับใช้ Regular Expression อันนี้จริงๆมัน overkill ไปหน่อย แต่อยากใช้ดู เอาส่วนหนึ่งของ Boost มาใช้

การเอาแต่ละอันมารวมกับโปรเจคก็เป็นเรื่องน่าปวดหัวมาก เนื่องจากด้อยประสบการณ์เรื่องพวกนี้เหลือเกิน กว่าจะรวมได้ก็แทบหัวแตก ปัญหาส่วนใหญ่เกิดตอน link

สรุปได้โปรแกรมออกมาสมใจ เสียเวลาไปมหาศาล ภูมิใจมาก T__T แทบจะเป็นโปรแกรม C++ โปรแกรมแรกบน Linux ที่ไม่ใช่ Hello World! (อ่อ ผมดึง update มาจากคุณ @tamir เพราะของตัวเองมีแต่อัพเดทภาษาไทย)

termtweet

อันนี้เป็น Makefile .. คนเขียนเป็นมาเห็นคงดูออกว่านุ้บมาก 55+

CXXFLAGS= -I ~/boost/include/boost-1_38 -I lib/curl/include -lrt

SRCS=main.cpp

OBJS=main.o

TINYOBJS=lib/tinyxml/tinystr.o lib/tinyxml/tinyxml.o lib/tinyxml/tinyxmlerror.o lib/tinyxml/tinyxmlparser.o

LIBS=~/boost/lib/libboost_regex-gcc43-mt-1_38.a /usr/local/lib/libcurl.a

all: termtweet

termtweet: $(OBJS) $(TINYOBJS)
g++ $(CXXFLAGS) -o $@ $(OBJS) $(TINYOBJS) $(LIBS)

tinyxml: $(TINYOBJS)

depend: $(SRCS)
makedepend $(SRCS)

clean:
-rm termtweet *.o *.bak *.swp

อันนี้ Source Code ในภาษา C++

#include <cstdio>
#include <iostream>
#include "lib/tinyxml/tinyxml.h"
#include <curl/curl.h>
#include <string>

#include <boost/regex.hpp>

using namespace std;

static int writer(char *data, size_t size, size_t nmemb,
string *buffer)
{
int result = 0;
if(buffer == NULL) return result;

buffer->append(data, size * nmemb);
result = size * nmemb;
return result;
}

void extract_link(const char *s){
string line(s);
boost::regex pat(".* (http://.*) .*");
boost::smatch matches;
if(boost::regex_match(line, matches, pat))
cout << "link: " << matches[1] << endl;
}

int main(void){
cout << "TermTweet 0.1" << endl;
cout << "Fetching items ..." << endl;

CURL *curl;
CURLcode res;
string buffer;

curl = curl_easy_init();
if(curl){
curl_easy_setopt(curl, CURLOPT_URL, "http://twitter.com/statuses/user_timeline/6380022.rss");
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);

curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writer);

res = curl_easy_perform(curl);

curl_easy_cleanup(curl);
}

if(res != CURLE_OK)
{
cout << "Can't get the RSS feed" << endl;
exit(-1);
}
cout << "Page load completed. Parsing XML .." << endl;

TiXmlElement *root;
TiXmlDocument doc;
doc.Parse(buffer.c_str());
root = doc.FirstChildElement("rss");
if(root != NULL){
TiXmlElement *channel;
channel = root->FirstChildElement("channel");
if(channel != NULL){
TiXmlElement *item;
item = channel->FirstChildElement("item");
while(item != NULL){
TiXmlElement *title;
title = item->FirstChildElement("title");
if(title != NULL){
cout << title->GetText() << endl;
extract_link(title->GetText());
}

item = item->NextSiblingElement("item");
cout << "----------------------------------" << endl;
}
}else{
cout << "Cannot find 'channel'" << endl;
exit(-1);
}
}else{
cout << "Not an RSS" << endl;
exit(-1);
}
cout << "Parse OK" << endl;
return 0;
}



ได้อะไรมาเยอะเหมือนกัน จากการหัดทำโปรเจคแสนถึกอันนี้ (แต่ก็เสียไปเยอะเช่นกัน T_T) ต่อไป … ไฮไลท์ … โปรแกรมคล้ายๆกันใน C# >.< เพิ่งเขียนเมื่อกี้ ไม่ต้องลง Lib เพิ่ม ไม่ต้องทำเบื้อกใดๆทั้งสิ้น ! (เชื่อว่า Java ก็คงใกล้ๆกัน)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Xml;
using System.IO;
using System.Text.RegularExpressions;

namespace SharpTermTweet
{
class Program
{
static void Main(string[] args)
{
string address = "http://twitter.com/statuses/user_timeline/6380022.rss";
WebRequest req = WebRequest.Create(address);
WebResponse res = req.GetResponse();

StreamReader sr = new StreamReader(res.GetResponseStream());
string content = sr.ReadToEnd();

XmlDocument doc = new XmlDocument();
doc.LoadXml(content);
XmlNodeList list = doc.GetElementsByTagName("item");
foreach (XmlNode node in list)
{
Console.WriteLine(node["title"].InnerText);
ExtractLink(node["title"].InnerText);
Console.WriteLine("-----------------------------");
}

}

static void ExtractLink(string line)
{
Regex regex = new Regex(".* (http://[^\\s]*) .*");
Match match = regex.Match(line);
if(match.Groups.Count >= 2)
Console.WriteLine("link: {0}", match.Groups[1]);
}
}
}

สรุปว่า เลือกเครื่องมือให้ถูกงานครับ :)

ปล. เพิ่งจะรู้ว่า Put the Right man to the Right job เป็นสำนวนที่โคดไทยเลย ไม่เชื่อลองไปหาใน Google ดูสิ ส่วนประโยคต้นฉบับจริงๆน่าจะเป็น Right man for the job มากกว่าจากการวิจัยของผม ..

Tags: ,

Comments

1.
กร กร says:

เท่โคตรเลยครับคุณ

2.
m3rlinez m3rlinez says:

ขอมอบเหรียญผู้คอมเม็นต์ดีเด่นให้ @กร ครับ lol

3.
b4lmung b4lmung says:

หล่อมากครับ

4.
blueflame blueflame says:

เดี๋ยว @m3rlinez จะพา @b4lmung ไปเลี้ยงโออิชิแกรนด์ ฐานคอมเมนต์ถูกใจ XD

5.
m3rlinez m3rlinez says:

ขอมอบเหรียญผู้ยุยงดีเด่นให้ @blueflame ครับ Laughing

6.
chakrit chakrit says:

Actually, the C# code can be shorter if you use WebClient.DownloadString and System.Xml.Linq.XDocument instead... and honestly I think VB.NET might actually be even shorter (and more elegant) with Xml literals

Right tools for the right job, right? ... Smile

Anyway its cool you get to work in *nix environment. I think Im gonna be stuck with M$ for a few more years T.T

7.
m3rlinez m3rlinez says:

@chakrit I couldn't inject all those LINQ things into my instinct after all :{ Thanks for the WebClient class anyway. I do this kind of thing a lot.

ขอมอบเหรียญโค้ดริวิวดีเด่นให้ชาคริต :'P

8.
teerapap.c teerapap.c says:

อ่าาาาาาา ตอนนี้คิดถึงหน้าจอดำๆมากกกกกกกก ไม่ได้แตะ unix มาเดือนสองเดือนและ  เริ่มจำคำสั่งใน shell ไม่ค่อยได้แล้วววอะ

9.
m3rlinez m3rlinez says:

คุณอยากได้เหรียญอะไรครับ ??

Add comment


(Will show your Gravatar icon)

biuquote
  • Comment
  • Preview
Loading