haitvkk3'blog, Blog cá nhân, cung cấp thông tin về Dịch vụ SEO, Thiet Ke Web, Maketing online, ..... Yahoo: haitvk3
Thứ Sáu, 23 tháng 11, 2012
[Android] Phần 1: Giới thiệu Android
Understanding Android” là cách mà ta tiếp cận lập trình Android và thấu hiểu kiến trúc hệ thống của nó. Bạn có thể không cần hiểu rõ cấu trúc của một hệ điều hành nhưng bạn vẫn có thể lập trình một ứng dụng trên HDH đó, đây là điều mà nhà sản xuất muốn khi release SDK với một framework có sẵn của họ. Như bạn biết điều này cũng có mặt tốt và xấu. Framework là một tầng cao cấp dành cho lập trình viên, nó đều có giới hạn của nó, bạn có thể chỉ có thể lập trình những ứng dụng phổ biến nhưng không nên tiến tới những ứng dụng cao cấp đi sâu vào hệ thống của HDH. Theo cách của mình, trước khi bắt đầu học Android, chúng ta nên nghiên cứu qua bản thân hệ điều hành Android, chúng ta không cần phải hiểu rõ nó như thể nào, mục đích quan trọng nhất của chúng ta là có cái nhìn chung và toàn diện nhất về Android.
Android là gì:
Android là một hệ điều hành mã nguồn mở, chủ yếu dành cho các thiết bị di động. Nhà phát triển có thể đem mã nguồn mở này về, modify bằng cách thêm code của họ hoặc dùngGoogle’s Java và build ra một hệ điều hành Android riêng cho mình. Đây cũng là cách mà HTC, Motorola, LG, Samsung … đang làm để đưa Android lên thiết bị của họ.
Một số tính năng của HDH Android: Handset layouts, Storage, Connectivity, Messaging, Web Browser, Java Virtual Machine, Media Support, Additional Hardware Support …
Android Platform:
Bao gồm hệ điều hành Android đầy đủ tính năng, các ứng dụng và các tầng trung gian để developer có thể mở rộng, modify hoặc thêm vào component của họ.
android live wallpapers copy Part I: Understanding Android
Picture 4 copy Part I: Understanding Android
Có 4 tầng cơ bản trong HDH Android: Application Framework, Android Runtime, Native Libraries, Linux Kernel … Mỗi tầng làm việc đều nhờ sự giúp đỡ của tầng bên dưới.
Tầng Linux Kernel:
Đây là nhân của hệ điều hành Android, mọi xử lý của hệ thống đều phải thông qua tầng này.
Linux Kernel cung cấp các trình điều khiển thiết bị phần cứng (driver) như: camera, USB, Wifi, Bluetooth, Display, Power Management …
Android dựa trên Linux phiên bản 2.6 lựa chọn các tính năng cốt lõi như bảo mật, quản lý bộ nhớ, quản lý tiến trình, mạng stack, và các trình điều khiển phần cứng. Kernel hoạt động như một lớp trừu tượng giữa phần cứng và phần mềm còn lại của hệ thống.
Picture 6 copy Part I: Understanding Android
Tầng Native Libraries:
Picture 8 copy Part I: Understanding Android
* System C library – có nguồn gốc từ hệ thống thư viện chuẩn C (libc), điều chỉnh các thiết bị nhúng trên Linux.
* Media Libraries – mở rộng từ PacketVideo’s OpenCORE; thư viện hỗ trợ playback và recording của nhiều định dạng video và image phổ biến: MPEG4, H.264, MP3, AAC, AMR, JPG, and PNG
* Surface Manager – quản lý việc hiển thị và kết hợp đồ họa 2D và 3D.
* LibWebCore – Android dùng lại webkit engine cho việc render trình duyệt mặc định của HDH Android browser và cho dạng web nhúng (như HTML nhúng)
* SGL – 2D engine
* 3D libraries – Thư viện 3D dựa trên OpenGL ES 1.0 API, có nâng cấp tăng tốc “hardware 3D acceleration”
* FreeType – render bitmap và vector font
* SQLite – quản lý database của ứng dụng
Tầng Runtime:
Picture 1 copy2 Part I: Understanding Android
Mỗi ứng dụng Android chạy trên một proccess riêng của Dalvik VM (máy ảo). Dalvik được viết để chạy nhiều máy ảo cùng một lúc một cách hiệu quả trên cùng một thiết bị.
Máy ảo Dalvik thực thi các file mang định dạng .dex (Dalvik Excutable), định dạng này là định dạng đã được tối ưu hóa để chỉ chiếm một vùng nhớ vừa đủ xài và nhỏ nhất có thể. VM chạy các class (đã được compile trước đó bởi 1trình biên dịch ngôn ngữ Java), sở dĩ VM chạy đc các class này là nhờ chương trình DX tool đã convert các class sang định dang .dex
Tầng Application Framework:
Picture 2 copy2 Part I: Understanding Android
Đây là tầng mà Google xây dựng cho các developer để phát triển các ứng dụng của họ trên Android, chỉ bằng cách gọi các API có sẵn mà Google đã viết để sử dụng các tính năng của phần cứng mà không cần hiểu cấu trúc bên dưới.
Bằng cách cung cấp một nền tảng phát triển mở, Android cho các nhà phát triển khả năng xây dựng các ứng dụng cực kỳ phong phú và sáng tạo. Nhà phát triển được tự do tận dụng các thiết bị phần cứng, thông tin địa điểm truy cập, các dịch vụ chạy nền, thiết lập hệ thống báo thức, thêm các thông báo để các thanh trạng thái, và nhiều, nhiều hơn nữa.
Tất cả các ứng dụng thường gồm một bộ các dịch vụ và hệ thống cơ bản sau:
*View UI dùng để xây dựng layout của ứng dụng bao gồm: list view, text field, button, dialog, form …
* Content Providers cho phép các ứng dụng có thể truy cập dữ liệu từ các ứng dụng khác (như ứng dụng của ta có thể lấy thông tin Contacts của điện thoại Android), hoặc để chia sẻ dữ liệu của riêng ứng dụng.
* Resource Manager cung cấp cách thức truy cập đến non-code resources như các asset, graphic, image, music, video …
* Notification Manager cho phép tất cả các ứng dụng hiển thị thông báo của mình trên hệ điều hành
* Activity Manager quản lý vòng đời của các ứng dụng.
Ở góc nhìn của người dùng ta có thêm tầng application (là ứng dụng do ta viết), sau đây là sơ đồ tổng quát:
system architecture Part I: Understanding Android
Quy Trình khởi động của HDH Android
Picture 3 copy2 Part I: Understanding Android
Bootloader của thiết bị sẽ load kernel và tiến hành khởi động 3 tiến trình của HDH Android: daemons, Zygote, Runtime:
-Zygote, là một tiến trình để khởi động máy ảo Java .
-Daemons quản lý phần cứng cấp thấp như USB, radio …
-Runtine proccess khởi động “Service Manager”.
Sau đó Runtime proccess yêu cầu Zygote khởi động một máy ảo mới Dalik VM cho việc khởi động “System Server”. Cả 2 tiến trình đầu tiên được khởi động sau đó là graphic va audio outputs (Audio & Surface Manager).
Kế tiếp là các tiến trình khác” Telephony, Bluetooth, Activity Manager, Package Manager …
Tất cả các tiến trình chạy ngầm được tạo ra đều phải đc đăng ký và chịu sự quản lý của Service Manager.
Note: Như vậy sau một đóng thông tin trên, bạn sẽ khó mà hấp thụ hết, nhưng đùng lo chỉ khi nào bắt tay vào viết một ứng dụng thì bạn mới bắt đầu sẽ hiểu rõ chúng có công dụng gì. Cuối cùng bạn chỉ cần ghi nhớ một số thông tin sau:
-Android là một mã nguồn mở của Google dựa trên kernel Linux 2.6 . Các nhà sản xuất phải đem mã nguồn này về và custom , rồi build ra HDH Android trên thiết bi của mình.
-Application Framework là phần mà ta sẽ nghiên cứu chủ yếu khi viết một ứng dụng Android và chúng ta không cần phải hiểu cặn kẽ các tầng khác (có thể quên nó :D)
Reference from http://developer.android.com/guide/basics/what-is-android.html
Cơ bản về biểu thức chính quy ( tiếp theo)
Biểu thức so mẫu (pattern) dùng để mô tả 1 cách tổng quát 1 đối tượng (chuỗi) trong PHP, ví dụ:
preg_replace('/[^a-z0-9]+/i',",$str);
–> hàm này l loại bỏ tất cả các ký tự không phải là chữ (a-z và A-Z) hoặc số (0-9) ra khỏi chuỗi $str
Biểu thức so mẫu dùng rất hiệu quả và nhanh chóng, tuy nhiên theo TG biết thì cũng không nhiều người biết sử dụng cái này(nhất là ở nơi không có trường lớp đào tạo cơ bản về PHP như VN chúng ta) còn số người biết,hiểu rõ thì không training lại nên đã ít nay càng ít người biết sử dụng biểu thức so mẫu. Trong bài này TG chỉ giới thiệu 1 số kiến thức cơ bản và 1 vài ví dụ để dễ hiểu, hy vọng là giúp ít cho anh em.
Cấu trúc: như ví dụ trên thì /[^a-z0-9]+/i là 1 biểu thức so mẫu,
- x có thể là 1 hoặc nhiều ký tự để mô tả dữ liệu được lấy và cũng có thể không có trong biểu thức so mẫu. Một số giá trị có thể có của x:
+ i: không phân biệt chữ hoa, chữ thường
+ s: bao gồm cả các ký tự xuống dòng
+ Ngoài còn có 1 số giá trị khác như: S,U,… nhưng ít được sử dụng
Ta lấy ví dụ cho dễ hình dung:
preg_replace('/[^a-z0-9]/',",'phpBASIC$')
-> php
preg_replace('/[^a-z0-9]/i',",'phpBASIC$')
-> phpBASIC
Ý nghĩa của các ký tự trong biểu thức so mẫu:
/(phpbasic)/ : chuỗi phpbasic
/\(phpbasic\)/ : chuỗi (phpbasic)
/^php(.*)/ : những chuỗi bắt đầu bằng chữ php
/(.*)basic$/ : những chuỗi kết thúc bằng chữ basic
/(.*)/ : tất cả các ký tự nằm trên 1 dòng
/[a-z]/ : tất cả các ký tự a-z
chú ý trong 1 lớp:
^ phủ định:
/[^a-z]/ : những ký tự không phải là a-z
- 1 khoảng:
/[a-d]/ : bao gồm a,b,c,d
/a|b/ : ký tự a hoặc b
( và ) bắt đầu và kết thúc 1 so mẫu con
/^a(c|d)b/ : chuỗi có dạng acb hoặc acd
/ab?c/ : chuỗi có dạng abc hoặc ac
/a*/ : <null>, a, aa, aaa,………….
/a+/ : a,aa,aaa,……………
/a{1,3}/ : a,aa,aaa
Các ký tự đặc biệt khác:
\t : ký tự tab
\d : bất kỳ ký tự số nào
\D : bất kỳ ký tự nào mà không phải là số
\s : bất kỳ ký tự khoảng trắng
\S : bất kỳ ký không phải là khoảng trắng
\w : bất kỳ từ nào
\W : không phải là từ
Biểu thức chính quy(Regular Expressions)
Regular Expressions là gì?
Regular Expressions (Regex) dịch ra tiếng Việt là Biểu thức chính quy. Khái niệm này nằm trong 1 mớ lý thuyết vô cùng đồ sộ và hầm hố . Nhưng ko nên lo lắng, ta có thể hiểu nôm na Regex là 1 cái mẫu (pattern) dùng để mô tả 1 lớp ký tự nào đó.
VD: lazydog là 1 regex. Nó là 1 mẫu đơn giản nhất vì nó so khớp (match) với đoạn text lazydog. 1 match là 1 đoạn text so khớp với mẫu.
VD phức tạp hơn 1 chút: \b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b Đây là mẫu mô tả 1 địa chỉ email. Mẫu này có thể dc dùng để tìm 1 địa chỉ email trong 1 đoạn văn bản, hoặc kiểm tra xem 1 chuỗi có phải là địa chỉ email hợp lệ hay ko.
Regex có thể dc sử dụng với bất kỳ dữ liệu nào mà ta có thể truy cập, thông qua ứng dụng hoặc ngôn ngữ lập trình. Có thể kể đến 1 số ứng dụng xử lý văn bản hỗ trợ regex: PowerGREP, EditPad Pro, RegexBuddy,…
Regular Expression Engines
Regex engine là 1 bộ phận của phần mềm, chuyên để xử lý regex (so khớp mẫu với 1 chuỗi nào đó). Có nhiều regex engine và chúng ko hoàn toàn tương thích với nhau. Cú pháp regex (flavor) của mỗi engine cũng có sự khác nhau. Loạt bài này sẽ tập trung vào cú pháp regex dc sử dụng trong Perl 5, vì nó phổ biến nhất. Rất nhiều engine regex khác giống với engine sử dụng trong Perl 5: engine nguồn mở PCRE (sử dụng trong rất nhiều ngôn ngữ lập trình, như PHP, cái ta cần đây hehe ), thư viện regex .NET,…
OK vậy là ta đã hiểu sơ qua regex là cái gì, giờ để tấn công vào cái địa ngục này, ta cần có vũ khí. Có thể sử dụng 1 số ứng dụng xử lý text ở trên để thực hành các biểu thức regex, hoặc ko thì ta tự tạo lấy 1 cái thô sơ bằng PHP.
Trong PHP, ta có thể sử dụng biểu thức regex thông qua các hàm regex. PHP cung cấp 3 nhóm hàm regex, tên của chúng dc bắt đầu bởi: ereg, mb_ereg và preg. 2 loại đầu sử dụng engine POSIX Extended, còn preg sử dụng engine PCRE (Perl-Compatible). Vậy thì vũ khí của ta đơn giản chỉ là đoạn mã PHP sau:
<?php
$string = 'chuỗi cần áp dụng biểu thức regex';
$pattern = '/biểu thức regex/';
preg_match($pattern, $string, $match);
echo $match[0];
?>
Nó sẽ hiện lên màn hình kết quả so khớp biểu thức regex với chuỗi mà ta áp dụng regex vào. Như thấy ở trên, ta dùng nhóm hàm preg vì nó sử dụng engine PCRE rất giống với engine của Perl, loại engine có cú pháp mà ta đang thảo luận ở đây. Trong loạt bài này, ta sẽ qui ước màu sắc như trong đoạn mã trên: red cho biểu thức regex, green cho chuỗi áp dụng regex, và blue cho kết quả so khớp.
Ký tự thông thường và ký tự đặc biệt
Ký tự thông thường (Literal Characters)
Regex cơ bản nhất chính là biểu thức bao gồm 1 ký tự thông thường, VD: a. Nó sẽ so khớp với thực thể đầu tiên của ký tự đó trong chuỗi. VD nếu có chuỗi: LazyDog is a boy, nó sẽ so khớp với ký tự a sau ký tự L. Regex này cũng có thể so khớp với ký tự a thứ 2 nếu ta điều khiển regex engine tiếp tục tìm kiếm sau khi đã so khớp dc 1 lần.
Cũng như vậy, regex dog sẽ so khớp với dog trong chuỗi LazyDog is not a dog. Regex này bao gồm 1 sêri 3 ký tự thông thường. Engine sẽ hiểu biểu thức này là: tìm d, theo sau bởi o, theo sau bởi g.
Chú ý rằng regex engine mặc định phân biệt chữ hoa và chữ thường. Dog ko so khớp với dog.
Ký tự đặc biệt (Special Characters)
Vì ta cần làm nhiều công việc phức tạp hơn là tìm kiếm 1 đoạn văn bản, cho nên phải trưng dụng 1 vài ký tự để làm những nhiệm vụ đặc biệt. Trong cú pháp regex dc thảo luận ở đây, có 11 ký tự mang ý nghĩa đặc biệt: [ \ ^ $ . | ? * + ( ). Chúng dc gọi là các metacharacter.
Nếu cần dùng các ký tự này với ý nghĩa thông thường, ta phải giải phóng nó bằng \. VD nếu cần so khớp 1+1=2, thì regex đúng sẽ là 1\+1=2. Chú ý rằng 1+1=2 cũng là regex đúng, nên sẽ ko báo lỗi, nhưng nó sẽ ko cho ta kết quả như mong muốn. Regex 1+1=2 sẽ so khớp với 111=2 trong chuỗi 123+111=234, vì dấu + ở đây mang ý nghĩa đặc biệt.
Nếu ta quên ko giải phóng ký tự đặc biệt ở những chỗ nó ko dc phép đứng thì sẽ gặp lỗi. VD: +1
Hầu hết các loại cú pháp regex đều coi { như 1 ký tự thông thường, trừ khi nó là 1 phần của toán tử nhắc lại (repetition operator), VD: {1, 3}. Vì vậy ta ko cần giải phóng ký tự này.
Ta chỉ dùng \ để giải phóng các ký tự đặc biệt, còn các ký tự khác thì ko nên, vì \ cũng là 1 ký tự đặc biệt. \ khi kết hợp với 1 ký tự thông thường sẽ có ý nghĩa đặc biệt, VD: \d sẽ so khớp với 1 chữ số từ 0 - 9.
Tất cả các loại cú pháp regex đều cho phép giải phóng 1 ký tự đặc biệt bằng \. Rất nhiều cú pháp khác còn hỗ trợ kiểu giải phóng \Q... \E. Tất cả các ký tự nằm trong cặp \Q và \E sẽ dc coi như ký tự thông thường. VD: \Q*\d+*\E sẽ so khớp với đoạn văn bản *\d+* . Kiểu cú pháp này dc hỗ trợ bởi JGsoft engine, Perl, PCRE, ...
Ký tự đặc biệt và ngôn ngữ lập trình
Khác với trong ngôn ngữ lập trình, trong regex, ký tự ' và " ko phải là ký tự đặc biệt. Vì vậy, ko cần phải giải phóng nó.
Trong mã nguồn của 1 chương trình, cần luôn ghi nhớ những ký tự nào dc ngôn ngữ lập trình xử lý đặc biệt. Bởi vì những ký tự này sẽ dc trình biên dịch xử lý trước khi dc engine regex xử lý. VD: regex 1\+1=2 phải dc viết thành 1\\+1=2 trong mã nguồn C++. Trình biên dịch C++ sẽ chuyển \\ thành \ trong chuỗi trên, sau đó nó mới dc chuyển đến regex engine. VD khác: đế so khớpc:\temp, cần dùng regex c:\\temp (vì \t trong regex mang ý nghĩa đặc biệt). Và trong mã nguồn C++, regex này cần dc viết là c:\\\\temp. Đúng là địa ngục . Ko bit trong PHP của chúng ta thì thế nào đây hic :'(.
Ký tự ko in được
Có thể dùng các tổ hợp ký tự đặc biệt để đặt các ký tự ko in dc vào regex.
\t cho ký tự tab (ASCII 0x09)
\r cho carriage return (0x0D)
\n cho line feed (0x0A).
\a (bell, 0x07)
\e (escape, 0x1B)
\f (form feed, 0x0C)
\v (vertical tab, 0x0B).
Chú ý rằng Windows text files sử dụng \r\n để kết thúc dòng, còn UNIX text files sử dụng \n.
Có thể dùng cách này để viết bất kỳ ký tự nào nếu biết mã 16 ASCII của ký tự đó trong bảng mã đang dùng. VD trong bảng mã Latin-1, ký tự copyright có mã 0xA9. Vì thế để tìm ký tự này, ta dùng \xA9.
Hầu hết các loại cú pháp regex còn cho phép sử dụng tổ hợp \cA đến \cZ (c cố định, theo sau bởi 1 chữ cái hoa từ A - Z) để biểu thị ký tự điều khiển. VD \cA biểu thị Control+A. \cM biểu thị Control+M, hay carriage return, giống như \r.
Nếu regex engine hỗ trợ Unicode, ta sử dụng \uFFFF thay cho \xFF để biểu thị 1 ký tự Unicode. VD: mã unicode của ký tự đồng euro là 0x20AC. Để đặt nó vào biểu thức regex, ta dùng \u20AC.
Regex Engine làm việc như thế nào?
Hiểu dc cách làm việc của regex engine sẽ giúp ta viết regex tốt hơn, dễ dàng hơn. Nó giúp ta hiểu dc tại sao 1 regex hoạt động ko như mong muốn, và giúp tiết kiệm thời gian phải mò mẫm khi viết các regex phức tạp.
Có 2 loại regex engine: text-directed engines, và regex-directed engines. Loại cú pháp regex mà ta đang thảo luận ở đây thuộc loại regex-directed engines. Loại engine này phổ biến hơn bởi nó có 1 số chức năng rất hữu dụng như: lazy quantifiers, backreferences,...
Có thể dễ dàng kiểm tra xem loại cú pháp đang sử dụng thuộc về engine nào qua việc kiểm tra xem lazy quantifiers và backreferences có dc hỗ trợ ko. Hãy thử dùng biểu thức regex regex|regex not vào chuỗi regex not xem sao. Nếu kết quả so khớp là regex, thì engine đang dùng thuộc loại regex-directed. Nêu kết quả là regex not, thì engine thuộc loại text-directed. Tại sao lại thế thì hồi sau sẽ rõ
Trong các VD ở các bài tiếp theo, ta sẽ phân tích cụ thể cách thức làm việc của regex engine, qua đó giúp sử dụng regex hiệu quả nhất và tránh mắc lỗi.
Regex-directed engine luôn trả về kết quả so khớp bên trái nhất
Thậm chí nếu 1 match tốt hơn có thể dc tìm thấy nếu tiếp tục so khớp. Đây là điều cần ghi nhớ. Regex-directed engine luôn bắt đầu so khớp với ký tự đầu tiên của chuỗi.
Hãy lấy 1 VD đơn giản nhất để minh hoạ: ta dùng regex cat vào chuỗi He captured a catfish for his cat. Engine sẽ bắt đầu so khớp dấu hiện đầu tiên trong regex là c với ký tự đầu tiên của chuỗi làH. Ko khớp. Vì vậy nó tiếp tục lần lượt so khớp với ký tự thứ 2 và 3 là e và space. Đều ko khớp. Đến ký tự thứ 4, c đã khớp với c. Xong, giờ engine bắt đầu so khớp dấu hiệu thứ 2 trong regex làa với ký tự thứ 5 của chuỗi là a. Khớp. Nhưng đến dấu hiệu thứ 3 của regex là t thì ko khớp với ký tự thứ 6 của chuỗi là p. Lúc này engine ngộ ra rằng ko thể tìm ra 1 match bắt đầu từ ký tự thứ 4 của chuỗi. Vì vậy, nó bắt đầu lại công việc từ đầu, từ ký tự thứ 5 của chuỗi. Regex c ko khớp vớia. Cứ tiếp tục như vậy cho đến ký tự thứ 15 của chuỗi, regex c đã khớp với c. Engine lần lượt so khớp các dấu hiệu còn lại trong regex với các ký tự tiếp theo trong chuỗi: a khớp a, t khớp t. Và như vậy 1 match đã dc tìm thấy bắt đầu từ ký tự 15. Engine sẽ trả về kết quả và ngừng luôn, ko tiếp tục tìm xem còn match nào tốt hơn ko (VD: cat ở cuối chuỗi).
Lớp ký tự (Character Classes - Character Sets)
Lớp ký tự
Sử dụng lớp ký tự, ta sẽ khiến regex engine chỉ chọn ra 1 ký tự để so khớp. Để sử dụng, ta đặt các ký tự cần so khớp vào 2 dấu [ và ]. VD: để so khớp ký tự a hoặc e, ta dùng [ae]. Như vậy biểu thức gr[ae]y sẽ khớp với gray hoặc grey.
Lớp ký tự chỉ so khớp với 1 ký tự đơn. Như vậy gr[ae]y sẽ ko khớp với graay, graey,v.v… Thứ tự các ký tự trong lớp ko quan trọng. Kết quả trả về luôn giống nhau.
Để xác định 1 vùng ký tự trong lớp ký tự, ta sử dụng dấu - . VD: [0-9] so khớp với 1 chữ số từ 0 – 9. Có thể sử dụng nhiều vùng ký tự hoặc kết hợp vùng ký tự với ký tự đơn. VD: [0-9a-fA-F] so khớp với 1 chữ số hệ 16, ko phân biệt chữ hoa, thường. [0-9a-fxA-FX] so khớp với 1 chữ số hệ 16 hoặc chữ cái X, ko phân biệt chữ hoa, thường. Cũng như trên, thứ tự các vùng ko quan trọng.
Lớp ký tự phủ định
Đặt dấu ^ sau [ trong lớp ký tự sẽ phủ định lớp ký tự đó. Kết quả là lớp ký tự sẽ so khớp với bất kỳ ký tự nào ko nằm trong lớp ký tự đó. Lớp ký tự phủ định có thể so khớp với cả ký tự line break.
Chú ý rằng lớp ký tự phủ định vẫn phải dc so khớp với 1 ký tự. VD: q[^u] ko phải là "q ko theo sau bởi u" mà là "q theo sau bởi 1 ký tự ko phải u". Vì vậy nó sẽ ko so khớp với q trong chuỗi Iraq, và sẽ so khớp với q và space trong chuỗi Iraq is a country.
Metacharacter trong lớp ký tự
Trong lớp ký tự, các ký tự mang ý nghĩa đặc biệt hay metacharacter chỉ bao gồm: ] \ ^ -. Các metacharacter nói ở phần trước khi đặt trong lớp ký tự sẽ chỉ dc coi như ký tự thông thường, và do đó ko cần phải giải phóng. VD: để tìm ký tự * hoặc +, ta dùng [+*].
Để đặt ký tự \ vào trong lớp ký tự với nghĩa thông thường, cần giải phóng nó bằng 1 ký tự \ khác. VD: [\\x] sẽ khơp với ký tự \ hoặc x. Các ký tự ] ^ - nếu muốn dùng theo nghĩa thông thường cũng phải dc giải phóng bằng \ hoặc đặt nó ở vị trí mà nó sẽ ko có ý nghĩa đặc biệt. Ta nên dùng cách thứ 2 để biểu thức regex trông dễ nhìn hơn như sau:
Với ^, đặt nó ở bất kỳ chỗ nào trừ vị trí ngay sau [ . VD: [x^] sẽ khớp với x hoặc ^.
Với ], đặt nó ngay sau [ hoặc [^ . VD: []x] sẽ khớp với ] hoặc x. [^]x] sẽ khớp với bất kỳ ký tự nào ko phải là ] hoặc x.
Với -, đặt nó ngay sau [ hoặc [^ , hoặc ngay trước ]. VD: cả [-x] và [x-] đều so khớp với - hoặc x.
Có thể sử dụng tất cả các ký tự ko in dc trong lớp ký tự giống như dùng chúng ngoài lớp ký tự. VD: [$\u20AC] sẽ khớp với $ hoặc ký tự đồng euro (với giả định cú pháp regex đang dùng hỗ trợ unicode).
JGsoft engine, Perl và PCRE còn hỗ trợ kiểu \Q…\E trong lớp ký tự để giải phóng 1 chuỗi ký tự. VD: [\Q[-]\E] sẽ khớp với [ hoặc - hoặc ].
Cú pháp regex của POSIX lại xử lý \ trong lớp ký tự như 1 ký tự thông thường. Đồng nghĩa với việc ta ko thể dùng \ để giải phóng ] ^ -. Để làm việc này ta chỉ còn cách đặt chúng vào các vị trí như trình bày ở trên. Ngoài ra điều này cũng đồng nghĩa với việc các cú pháp tắt (shorthand, VD: \d) ko còn hiệu lực.
Lớp ký tự viết tắt (Shorthand Character Classes)
\d là dạng tắt của [0-9].
\w dc gọi là "ký tự từ" (word character). Chính xác những ký tự nào dc khớp với nó thay đổi tuỳ theo mỗi loại cú pháp regex. Trong tất cả các loại cú pháp, nó sẽ bao gồm [A-Za-z]. Trong hầu hết các loại cú pháp, nó cũng bao gồm cả dấu _ và chữ số.
\s dc gọi là "ký tự trắng" (whitespace character). Nó khớp với ký tự nào thì cũng tùy thuộc vào từng loại cú pháp. Trong kiểu cú pháp thảo luận ở đây, nó bao gồm [\t]. Nghĩa là \s sẽ khớp vớispace hoặc tab. Trong hầu hết cú pháp , nó cũng bao gồm cả ký tự carriage return hoặc line feed, nghĩa là [\t\r\n]. Một số cú pháp khác lại bao gồm thêm cả các ký tự ko in dc hiếm khi dùng như vertical tab hoặc form feed.
Các lớp ký tự viết tắt có thể dc dùng cả trong lẫn ngoài cặp []. VD: \s\d khớp với 1 ký tự trắng theo sau bởi 1 chữ số. [\s\d] khớp với 1 ký tự đơn là 1 ký tự trắng hoặc 1 chữ số. Khi áp dụng vào chuỗi 1 + 2 = 3, regex thứ 1 sẽ khớp với 2 (space và 2), trong khi regex thứ 2 sẽ khớp với 1. [\da-fA-F] khớp với 1 chữ số hệ 16, giống như [0-9a-fA-F].
Lớp ký tự viết tắt phủ định (Negated Shorthand Character Classes)
\D tương đương [^\d]
\W tương đương [^\w]
\S tương đương [^\s]
Cần thận trọng khi sử dụng dạng viết tắt phủ địng bên trong []. [\D\S] khác với [^\d\s]. Regex thứ 2 sẽ khớp với bất kỳ ký tự nào ko phải là chữ số hoặc ký tự trắng. Còn regex thứ 1 sẽ khớp với bất kỳ ký tự nào ko phải là chữ số hoặc ko phải là ký tự trắng. Và vì chữ số ko phải là ký tự trắng và ký tự trắng ko phải là chữ số cho nên [\D\S] sẽ khớp với bất kỳ ký tự nào, bao gồm cả ký tự trắng và chữ số .
Nhắc lại lớp ký tự (Repeating Character Classes)
Nếu nhắc lại lớp ký tự khi dùng các toán tử nhắc lại ? * + , ta sẽ nhắc lại cả lớp ký tự chứ ko chỉ nhắc lại ký tự mà nó so khớp. VD: regex [0-9]+ sẽ khớp với cả 837 lẫn 222.
Nếu muốn nhắc lại chỉ các ký tự dc so khớp, ta cần dùng tham chiếu ngược (backreferences). ([0-9])\1+ sẽ khớp với 222 chứ ko khớp với 837. Khi áp dùng regex này vào chuỗi 833337, nó sẽ khớp với 3333. Chi tiết hơn thì hồi sau sẽ rõ
Ký tự chấm (Dot)
Ký tự Dot khớp với hầu hết các ký tự
Trong biểu thức regex, dấu . là metacharacter dc sử dụng nhiều nhất, và cũng là ký tự bị sử dụng sai nhiều nhất.
Dấu . khớp với 1 ký tự đơn bất kỳ ngoại trừ ký tự newline. Vì vậy, dấu . tương đương với [^\n](trong UNIX) hoặc [^\r\n] (trong Windows).
Trong Perl, dấu . có thể khớp với cả newline nếu ta dùng chế độ "single-line mode". Để sử dụng chế độ này, ta thêm s vào sau biểu thức regex, VD: m/^regex$/s;
JavaScript và VBScript ko có chế độ nào hỗ trợ Dot so khớp với các ký tự line break. Vì vậy, để so khớp với bất kỳ ký tự nào ta phải dùng [\s\S] thay cho Dot. [\s\S] so khớp với 1 ký tự là ký tự trắng (bao gồm cả các ký tự line break) hoặc ko phải ký tự trắng, nghĩa là nó so khớp với bất kỳ ký tự nào.
Sử dụng Dot 1 cách tiết kiệm
Dấu . là 1 metacharacter đầy uy lực. Nó có thể khớp với bất kỳ ký tự nào, nhưng cũng có thể khớp với ký tự mà ta ko muốn. Những trường hợp như thế có thế rất khó nhận ra.
Hãy lấy 1 VD đơn giản để minh hoạ: giải sử ta muốn tìm 1 chuỗi ngày tháng năm dưới dạng mm/dd/yy, trong đó dấu phân cách ngày tháng năm ta để người dùng tuỳ chọn. Giải pháp nhanh nhất là \d\d.\d\d.\d\d. Trông có vẻ ổn. Nó sẽ khớp 1 chuỗi kiểu như 02/12/03. Vấn đề là 1 chuỗi kiểu như 02512703 cũng dc coi là 1 ngày hợp lệ với regex trên (chấm thứ 1 khớp với 5, chấm thứ 2 khớp với 7).
Giải pháp tốt hơn là: \d\d[- /.]\d\d[- /.]\d\d. Regex này cho phép – hoặc space hoặc . hoặc / làm dấu phân cách ngày tháng năm. Lưu ý rằng dấu . trong lớp ký tự là 1 ký tự thông thường, do đó ko cần phải giải phóng. Nhưng regex này vẫn chưa hoàn hảo, nó sẽ coi 99/99/99 là 1 ngày hợp lệ.
Giải pháp tiếp theo: [0-1]\d[- /.][0-3]\d[- /.]\d\d. Ổn hơn nhưng vẫn chưa hoàn hảo, nó có thể khớp với 19/39/99.
Chất lượng của regex thế nào tuỳ thuộc vào yêu cầu của bạn. VD nếu muốn thẩm định thông tin nhập vào từ người dùng thì regex phải thật hoàn hảo. Còn nếu phân tích 1 file dữ liệu mà bạn đã biết chắc mã nguồn tạo ra file đó theo cách thức như thế nào thì regex có thể ở mức vừa đủ mà thôi.
Sử dụng lớp ký tự phủ định thay cho Dot
Hãy lấy 1 VD để tìm hiểu tại sao. Giả sử ta cần tìm 1 chuỗi bao bởi " ". Nghe có vẻ đơn giản như đang giỡn. Chuỗi này có thể có bao nhiêu ký tự tuỳ thích, do đó ".*" có vẻ ổn. Dấu . khớp với bất kỳ ký tự nào, còn dấu * sẽ cho phép . có thể dc nhắc lại bao nhiêu lần tuỳ thích, kể cả 0 lần. Nếu áp dụng regex này vào chuỗi Put a "string" between double quotes, nó sẽ trả về kết quả đúng như mong đợi: "string". Giờ hãy thử với chuỗi Houston, we have a problem with "string one" and "string two". Please respond. Và kết quả là "string one" and "string two", hỏng zồi. Lý do là vì * vốn có bản tính "tham lam" (greedy). Chi tiết thế nào thì hồi sau sẽ rõ. VD này cho thấy ko nên lạm dụng dấu chấm.
Trong VD tìm ngày ở trên, ta cải tiến regex bằng cách sử dụng lớp ký tự thay cho dấu chấm. Giờ ta cũng làm như vậy. Ta ko muốn có số lượng bất kỳ các ký tự bất kỳ trong cặp dấu " ", mà muốn có số lượng bất kỳ các ký tự ko phải là " hoặc newline trong cặp dấu " ". Do đó regex đúng sẽ là"[^"\r\n]*".
Mỏ neo (anchors)
Neo đầu và cuối chuỗi
Từ đầu đến giờ, ta đã tìm hiểu về các ký tự thông thường và lớp ký tự. Đặt 1 ký tự thông thường hoặc 1 lớp ký tự vào biểu thức regex, ta sẽ bắt regex engine đi so khớp với 1 ký tự đơn.
Mỏ neo thuộc về 1 dạng khác. Chúng ko so khớp với bất kỳ 1 ký tự nào. Thay vào đó, chúng so khớp với 1 vị trí trước, sau hoặc giữa các ký tự. Chúng dc sử dụng để "neo" biểu thức regex vào 1 vị trí để so khớp. Dấu ^ khớp với vị trí ngay trước ký tự đầu tiên trong chuỗi. Áp dụng regex ^a cho chuỗi abc, ta sẽ được a. ^b sẽ ko có kết quả khi so khớp với abc, vì b ko khớp với ký tự ngay sau vị trí bắt đầu của chuỗi, vị trí dc khớp bởi ^.
Tương tự như trên, $ khớp với vị trí ngay sau ký tự cuối cùng của chuỗi. c$ sẽ khớp với c trongabc, trong khi a$ ko khớp.
Thực hành
Khi sử dụng regex trong ngôn ngữ lập trình để kiểm định thông tin nhập vào từ người dùng, sử dụng neo là rất quan trọng. VD nếu ta dùng \d+ để kiểm tra xem người dùng có nhập vào 1 số nguyên hay ko, kết quả trả về vẫn có thể là đúng thậm chí nếu người dùng nhập qsdf4ghjk, bởi\d+ khớp với 4. Regex đúng ở đây phải là ^\d+$. Bởi vì vị trí đầu chuỗi phải dc khớp trước khi \d+dc khớp, và vị trí cuối chuỗi phải dc khớp ngay sau đó, vì vậy chuỗi nhập vào nếu muốn khớp với^\d+$ thì chỉ có thể là 1 chuỗi các chữ số (dấu + là toán tử nhắc lại, dùng để nhắc lại ký tự trước nó 1 hoặc nhiều lần, chi tiết thì hồi sau sẽ rõ )
1 chú ý khác là ta có thể dễ dàng mắc lỗi với ký tự trắng. Trong ngôn ngữ Perl chẳng hạn, khi đọc vào 1 dòng từ 1 file text, ký tự line break cũng sẽ dc lưu vào biến. Do đó trước khi tiến hành kiểm định thông tin nhập vào, ta cần chặt bỏ các ký tự trắng đầu và cuối. ^\s+ khớp với ký tự trắng đầu và \s+$ khớp với ký tự trắng cuối.
Sử dụng ^ và $ để neo đầu và cuối 1 dòng
Nếu chuỗi cần xử lý dc viết trên nhiều dòng, như kiểu first line\nsecond line (\n là ký tự line break), ta có thể muốn làm việc với từng dòng hơn là với cả chuỗi. Do đó, các regex engine dc thảo luận trong loạt bài này còn có thêm chức năng mở rộng ý nghĩa của các mỏ neo. ^ vừa có thể khớp với vị trí đầu chuỗi (trước ký tự f trong VD trên), vừa có thể khớp với vị trí ngay sau mỗi line break (giữa \n và s). Cũng như vậy, $ vừa có thể khớp với vị trí cuối chuỗi (sau ký tự e cuối cùng), vừa có thể khớp với vị trí trước mỗi line break (giữa e và \n).
Trong ngôn ngữ lập trình, ta phải kích hoạt chức năng mở rộng này, dc gọi là chế độ đa dòng (multi-line mode). VD trong Perl, ta làm việc này bằng cách thêm m vào sau đoạn mã regex, VD: m/^regex$/m;
Neo đầu và cuối chuỗi vĩnh cửu
\A sẽ chỉ khớp với vị trí đầu chuỗi, và \Z chỉ khớp với vị trí cuối chuỗi. Chúng ko bao giờ khớp ở vị trí các line break, thậm chí cả khi chế độ "multiline mode" dc kích hoạt. Điều này đúng cho tất cả các loại cú pháp regex dc thảo luận trong loạt bài này.
Match có độ dài 0 (Zero-Length Matches)
Ta đã biết các mỏ neo khớp với 1 vị trí chứ ko phải khớp với 1 ký tự. Điều này cũng có nghĩa là nếu biểu thức regex chỉ bao gồm 1 hoặc nhiều mỏ neo, kết quả so khớp có thể có độ dài 0. Tuỳ vào từng tình huống mà điều này có thể rất hữu dụng hoặc là tai họa. Chẳng hạn ta dử dụng ^\d*$để kiểm tra xem người dùng có nhập vào 1 số hay ko (chú ý ở đây ta dùng * thay cho + trong VD ở trên, * cũng là toán tử nhắc lại, dùng để nhắc lại ký tự trước nó 0 hoặc nhiều lần, chi tiết thì hồi sau sẽ rõ ). Điều này có thể dẫn tới việc chấp nhận 1 chuỗi rỗng là thông tin nhập vào hợp lệ.
Tuy nhiên, chỉ khớp với vị trí cũng có thể rất hữu dụng. VD như trong email, các dòng trong message trích dẫn thường dc bắt đầu bởi dấu > và space. Với chế độ "multi-line mode", ta có thể dùng regex ^ để "tóm" lấy với vị trí đầu của message trích dẫn, và vị trí ngay sau mỗi ký tự newline (đầu mỗi dòng trong message trích dẫn). Nhờ đó, khi sử dụng các hàm hay phương thức của ngôn ngữ lập trình, ta có thể loại bỏ kết quả so khớp (match) và thay thế vào đó chuỗi mà ta muốn (ở đây là chuỗi gồm ký tự > và space). Bởi vì kết quả so khớp ở đây ko chứa ký tự nào cả, cho nên ko có gì bị xoá đi. Nhưng nó chứa vị trí đầu các dòng trong message trích dẫn, do đó chuỗi >space sẽ được đặt vào đúng vị trí đó, điều mà ta muốn. VD trong PHP của chúng ta, đoạn mã này sẽ thêm >space vào đầu chuỗi:
<?php
echo preg_replace('/^/', '> ', 'I love LazyDog');
?>
Chuỗi kết thúc bởi Line Break
Mặc dù $ (khi ko kích hoạt "multi-line mode") và \Z chỉ khớp với vị trí cuối chuỗi, nhưng có 1 ngoại lệ ở đây. Nếu chuỗi dc kết thúc bởi ký tự line break, khi đó \Z và $ sẽ khớp với vị trí ngay trước ký tự line break, thay vì vị trí cuối chuỗi thực sự (sau line break). Tính năng tăng cường này dc đưa ra bởi Perl, và sau đó dc các loại cú pháp khác copy , bao gồm: Java, .NET và PCRE. Trong Perl, khi đọc vào 1 dòng từ file, chuỗi kết quả sẽ dc kết thúc bởi line break. VD khi đọc 1 dòng từ file chứa đoạn text lazydog, kết quả sẽ là lazydog\n. Khi áp dùng regex vào chuỗi này, cả ^[a-z]+$ và\A[a-z]+\Z đều khớp với lazydog.
Nếu muốn khớp với vị trí cuối chuỗi thực sự, ta sử dụng \z. \A[a-z]+\z sẽ ko khớp với lazydog\n. Vì\z khớp với vị trí sau line break, mà line break lại ko dc khớp bởi lớp ký tự [a-z].
Quan sát sự làm việc của Regex Engine
Hãy xem regex engine làm việc như thế nào khi ta khớp biểu thức ^4$ vào chuỗi 749\n486\n4 (\n là ký tự newline) ở chế độ "multi-line mode".
Như bình thường, regex engine bắt đầu với ký tự đầu tiên: 7. Dấu hiệu (token) đầu tiên trong biểu thức regex là ^. Vì dấu hiệu này là dấu hiệu rỗng, nên engine sẽ ko cố khớp nó với 1 ký tự, thay vào đó nó sẽ khớp với 1 vị trí trước ký tự hiện hành. Và ^ dĩ nhiên sẽ khớp dc với vị trí trước 7. Engine sau đó sẽ tiếp tục xử lý dấu hiệu thứ 2 trong regex: 4. Vì dấu hiệu trước đó là rỗng, nên engine sẽ ko nhảy tới ký tự tiếp theo trong chuỗi mà vẫn dừng lại ở 7. 4 là ký tự thông thường, nó ko khớp với 7. Ko có khả năng nào khác ở đây, vì vậy engine phải bắt đầu lại từ đầu với dấu hiệu đầu tiên, và bây giờ xuất phát từ ký tự 4 trong chuỗi. Lần này, ^ ko thể khớp với vị trí trước 4. Vì phía trước vị trí này là 1 ký tự (7), và ký tự đó ko phải là newline. Engine lại làm lại từ đầu, từ ký tự9, và lại thất bại. Lần thử tiếp theo, ở ký tự \n, vẫn thất bại. Vì cũng như trên, phía trước vị trí trước \n là 1 ký tự (9) và 9 ko phải là newline.
Engine tiếp tục làm lại từ đầu ở ký tự 4 (thứ hai) trong chuỗi. Bây giờ ^ đã có thể khớp với vị trí trước 4, vì trước vị trí này là 1 ký tự newline. Xong dấu hiệu thứ nhất, engine xử lý dấu hiệu thứ 2 của regex, 4, nhưng ko nhảy tới ký tự tiếp theo trong chuỗi. 4 khớp với 4. Giờ engine sẽ nhảy sang dấu hiệu thứ ba của regex và nhảy sang ký tự tiếp theo trong chuỗi. Nó cố gắng khớp $ với vị trí trước (chú ý: vẫn là trước) ký tự 8. $ ko thể khớp ở đây, vì vị trí này bị theo sau bởi 1 ký tự, và ký tự này ko phải là newline.
Engine tiếp tục kiên trì làm lại từ đầu. Lần trước, ^ đã dc khớp ở ký tự 4 (thứ hai), vì vậy lần này engine sẽ bắt đầu lại từ ký tự 8. ^ ko khớp với vị trí trước 8. Và cũng như vậy với ký tự 6 và \n.
Lại làm lại, giờ engine cố khớp ^ ở ký tự 4 (thứ ba) của chuỗi. Trước vị trí trước 4 là ký tự \n(newline). Vậy là thành công. Sau đó, engine tiếp tục khớp thành công 4 với 4. Giờ đến dấu hiệu cuối cùng là $, và ký tự cần so khớp bây giờ chính là 1 cái "ký tự"… chả có gì (void) nằm ở cuối chuỗi. Cứ theo nguyên tắc thì $ sẽ check vị trí phía trước cái "ký tự" này, và vị trí này dc theo sau bởi cái "ký tự" chả có gì đó, vậy là OK zồi . Nghĩa là, $ sẽ check chính cái ký tự hiện hành, nếu nó là ký tự newline hoặc "ký tự"… chả có j (hay đúng hơn là chả còn gì để mà check) thì so khớp sẽ thành công. Như vậy ở đây $ đã so khớp thành công.
Thế là cuối cùng, engine cũng đã tìm ra dc 1 match: đó là ký tự 4 cuối cùng trong chuỗi. Nghe có vẻ rắc rối nhưng thực chất hoạt động của engine hết sức logic và đơn giản.
1 quan sát khác
Ở phần trên ta đã thấy ^\d*$ có thể khớp thành công với 1 chuỗi rỗng. Giờ hãy xem tại sao.
Chỉ có 1 "ký tự" duy nhất trong chuỗi rỗng: ký tự void cuối chuỗi (hay là chả có gì ). Dấu hiệu đầu tiên trong regex là ^. Nó khớp với vị trí trước ký tự void này, vì trước vị trí này là 1 ký tự void khác, ký tự void đầu chuỗi . Dấu hiệu tiếp theo của regex là \d*. Engine sẽ cố khớp \d với ký tự void cuối chuỗi, dĩ nhiên là thất bại. Nhưng nhờ có dấu * (nhắc lại ký tự trước nó 0 hoặc nhiều lần) cho nên lần so khớp này vẫn tìm ra dc 1 match (nhưng có độ dài 0). Engine lại tiếp tục tiến đến dấu hiệu cuối cùng $, và ký tự cần so khớp vẫn giữ nguyên, là ký tự void cuỗi chuỗi. Và như ta đã biết ở trên, so khớp này cũng thành công. Do đó, kết quả so khớp toàn bộ regex là 1 chuỗi rỗng, và engine sẽ thông báo là thành công
Tóm tắt:
1. ^ : đại diện cho kí tự đầu tiên của chuỗi.
^a : tìm kí tự a ở đầu chuỗi.
2. $ : đại diện cho kí tự cuối cùng của chuỗi.
a$ : tìm kí tự a ở cuối chuỗi.
3.\ : Hiển thị kí tự đặc biệt như $, *, . , \
4. . : Dấu chấm đại diện cho kí tự bất kì, kể cả dấu cách.
5. [] : Kí hiệu tập hợp. [abc] : tìm tất cả những phần tử a hoặc b hoặc c có trong chuỗi nguồn.
[ab][cd] = Tìm các chuỗi ac, ad, bc, bd.(Giống như nhân phân phối trong toán học vậy)
6. Nếu muốn tìm 1 chuỗi mà các kí tự của chuỗi là liền nhau thì ta dùng dấu -.
Vd : [0-9] : sẽ tìm các chữ số 0,1,2,3,4,5,6,7,8,9.
[a-z] : tìm tất cả các chữ thường.
7. [^a-z] : Dấu mũ đứng trước 1 tập hợp mang ý nghĩa phủ định tập hợp ấy, trong vd này tức là ta sẽ lấy các kí tự trừ các kí tự thường.
8. () : Đại diện cho tập hợp các mẫu con.
Kí tự | : mang ý nghĩa là "hoặc (or)"
vd : (I|you|we) : Tìm các chuỗi con I hoặc you hoặc we.
9. a* : Dấu * đại diện cho số lần xuất hiện của kí tự đứng trước nó từ 0 – n lần.
a* : <null>, a, aa, aaa, aaaa, ….
10. a+ : Tương tự dấu *, kí tự đứng trước dấu cộng có thể xuất hiện từ 1 – n lần.
a+ : a, aa, aaa, …
11. a? : Kí tự đứng trước dấu hỏi có thể xuất hiện từ 0 – 1 lần. Nói cách khác, dấu hỏi đại diện cho "có hoặc không".
12. {} : Đại diện cho số lần lặp lại kí tự đứng trước.
Vd : a{3} = aaa
a{1,3} = a, aa, aaa.
A{3,} = Kí tự xuất hiện 3 hoặc n lần.
13. \w : Đại diện cho các chữ cái, chữ số và dấu _.
\w = [A-z0-9_]
14. \W : Phủ định của \w
15. \s : Lấy tập hợp các khoảng trắng.
16. \S : Phủ định của \s, lấy tất cả các kí tự, trừ khoảng trắng.
17. \d : Đại diện cho các chữ số.
18. \A : Tương đương với dấu ^.
19. \Z : Tương đương với dấu $.
20. Một số biểu thức quan trọng :
(?=<pattern>) : Lấy những kí tự mà kí tự đứng sau nó = <pattern>
(?!<pattern>) : Lấy những kí tự mà kí tự đứng sau nó khác <pattern>
(?<=<pattern>) : Lấy những kí tự đứng sau <pattern>
Thứ Tư, 21 tháng 11, 2012
Tạo phiếu khảo sát trực tuyến với Google Docs

Chủ Nhật, 18 tháng 11, 2012
Thủ thuật & Cú pháp thông dụng tìm kiếm với Google
Hiện nay Google đang chiếm khoảng ~85% lượng người dùng tìm kiếm thông tin trên toàn thế giới (*), có khoảng ~95% người dùng tìm kiếm trên Google ở Việt Nam (**).
Những con số trên cho ta thấy sự bành trướng của gã khổng lồ Google, ta cũng không thể phủ nhận những tiện ích, thông tin mà Google mang lại cho chúng ta.
- Nếu bạn là học sinh, sinh viên: hàng ngày bạn phải dùng Google để tìm kiếm thông tin phục vụ cho quá trình học tập của mình.
- Nếu bạn là người đi làm: hàng ngày bạn dùng Google để tìm kiếm thông tin đối tác, khách hàng…
- Nếu bạn là dân SEO: hàng ngày bạn dùng Google để tìm website để building link, tìm tài liệu…
- Dù bạn là ai thì tôi chắc chắn bạn đã dùng Google để tìm kiếm thông tin (nhất là khi bạn đang đọc bài viết này bằng các tìm kiếm trên Google).
Chúng ta thấy Google là một cỗ máy tìm kiếm hoàn mĩ nhất thị trường Search Engine hiện nay, tuy nhiên, dù rất xuất sắc nhưng không phải mọi kết quả tìm kiếm trả về đều như ý của người sử dụng. Nhưng, khi bạn nắm rõ một số thủ thuật tìm kiếm trên Google, bạn sẽ làm chủ được Google, tiết kiệm thời gian và tìm kiếm các thông tin mình muốn một cách dễ dàng (ngoại trừ thông tin đó không có trên Google ^^).
Chắc bạn đang nôn nóng, thủ thuật gì thế? Có khó không? Có dễ nhớ không?… Rất đơn giản. Bài viết này của tôi sẽ giúp bạn thực hiện việc tìm kiếm các thông tin trên Google dễ hơn bao giờ hết. Nào, ta bắt đầu thôi.
Đầu tiên tôi gọi các cú pháp dùng để tìm kiếm gọi là "Toán tử" (từ chuyên nghành), ta sẽ có các toán tử sau:
Toán Tử "
- Toán tử " được dùng để tìm chính xác một cụm từ.
VD: "thiết kế web" sẽ trả về kết quả những website có chứa chính xác cụm từ trên.
+ Kiểm tra thử trên Google: "thiết kế web"
Toán Tử +
- Toán tử + được dùng kết hợp để tìm cụ thể một từ khóa nào đó và bắt buộc có trong kết quả tìm kiếm. Lưu ý, bạn phải đặt dấu + sát từ khóa, không có khoảng trắng.
VD: "kiếm tiền trên mạng +affiliate" sẽ trả về những website nói về "kiếm tiền trên mạng" và có chữ Affiliate
+ Kiểm tra thử trên Google: kiếm tiền trên mạng +affiliate
Toán Tử -
- Toán tử - được dùng để loại bỏ một kết quả nào đó khỏi kết quả tìm kiếm của bạn. Lưu ý, bạn phải đặt dấu – sát từ khóa, không có khoảng trắng.
VD: "kiếm tiền trên mạng -affiliate" sẽ trả về những website nói về "kiếm tiền trên mạng" và không chữ Affiliate
+ Kiểm tra thử trên Google: kiếm tiền trên mạng -affiliate
Toán Tử ~
- Toán tử ~ được dùng để tìm các kết quả đồng nghĩa với từ khóa của bạn. Thuật ngữ này ít được sử dụng ở Việt Nam.
Toán Tử OR Hoặc |
- Toán tử OR hoặc dấu | được dùng để thay thế giữa hai truy vấn. Dấu | được đặt ở giữa hai từ khóa.
VD: "Du lịch Mỹ|Hoa Kỳ" sẽ trả về những website có chứa nội dung về "Du lịch Mỹ" hoặc "Du lịch Hoa Kỳ"
+ Kiểm tra thử trên Google: Du lịch Mỹ|Hoa Kỳ
Toán Tử ..
- Toán tử .. được dùng để tìm khoảng giữa các con số
VD: "việc làm lương $200..$1000" sẽ trả về những website đăng việc làm có mức lương từ $200 đến $1000
+ Kiểm tra thử trên Google: việc làm lương $200..$1000
Toán Từ *
- Toán từ * được dùng để tìm kiếm rộng, dấu * đại diện cho từ hoặc cụm từ. Dấu * có thể đứng trước, đứng giữa hoặc đứng sau từ khóa cần tìm.
VD: "vì sao * yêu" sẽ trả về những website có nội dung như: vì sao anh yêu em, vì sao đàn ông yêu đường cong của phụ nữ…
+ Kiểm tra thử trên Google: vì sao * yêu
Google chỉ cung cấp những toán tử tìm kiếm cơ bản mà tôi đã liệt kê ở trên tại (http://support.google.com/websearch/bin/answer.py?hl=en&answer=136861). Các toán tử nâng cao dưới đây không được Google liệt kê trên website, vì vậy Google có thể thay đổi bất kỳ khi nào.
Allintext
- Cú pháp allintext:"truy vấn" được dùng để tìm tất cả các từ có trong truy vấn, và CHỈ ở trong nội dung của website (phần text)
VD: "allintext:cách kiếm tiền trên mạng" sẽ trả về các website có chứa tất cả các từ trong nội dung là "cách kiếm tiền trên mạng"
+ Kiểm tra thử trên Google: allintext:cách kiếm tiền trên mạng
Allintitle
- Cú pháp allintitle:"truy vấn" được dùng để tìm tất cả các từ có trong truy vấn, và CHỈ ở trong tiêu đề của website (title)
VD: "allintitle:thủ thuật Google+" sẽ trả về các website có tất cả các từ trong tiêu đề là "thủ thuật Google+"
+ Kiểm tra thử trên Google: allintitle:thủ thuật Google+
Allinurl
- Cú pháp allinurl:"truy vấn" được dùng để tìm tất cả các từ có trong truy vấn, và CHỈ ở URL của website
VD: "allinurl:cach ban hang qua mang" sẽ trả về các website có tất cả các từ trong URL là "cach ban hang qua mang"
+ Kiểm tra thử trên Google: allinurl:cach ban hang qua mang
Cache
- Cú pháp cache:tenwebsite.com được dùng để xem bản cache đã được Google lưu lại
VD: "cache:dangcapweb.com" sẽ trả về bản cache của website dangcapweb.com
Define
- Cú pháp define:"từ khóa" được dùng để tra định nghĩa của các từ, cụm từ. Được dùng trên Google.com, trên Google.com.vn không hiểu cú pháp này. Tôi cũng dùng cú pháp này khi viết bài Thuật ngữ SEO hôm trước.
VD: "define:SEO" sẽ trả về định nghĩa của từ SEO
+ Kiểm tra thử trên Google: define:SEO
Filetype
- Cú pháp filetype:"loại file" được dùng để tìm chính xác loại file: doc, pdf, mp3, zip, xls,…
VD: "Hop dong thue nha + filetype:doc" sẽ trả về những website có chứa file hợp đồng thuê nhà được làm bằng Word
+ Kiểm tra thử trên Google: Hop dong thue nha + filetype:doc
Intext
- Cú pháp intext:"truy vấn" có ý nghĩa tương tự allintext và là con của allintext. Thường được kết hợp với từ khóa trước hoặc sau nó.
Intitle
- Cú pháp intitle:"truy vấn" có ý nghĩa tương tự allintitle và là con của allintitle. Thường được kết hợp với từ khóa trước hoặc sau nó.
Inurl
- Cú pháp inurl:"truy vấn" có ý nghĩa tương tự allinurl và là con của allinurl. Thường được kết hợp với từ khóa trước hoặc sau nó.
Related
- Cú pháp related:tenwebsite.com được dùng để tìm các website có nội dung tương tự.
VD: "related:dangcapweb.com" sẽ trả về các website trong lĩnh vực thiết kế web
+ Kiểm tra thử trên Google: related:dangcapweb.com
Site
- Cú pháp site:tenwebsite.com dùng để tìm tất cả các trang thuộc tenwebsite.com đã được Google Index
VD: "site:igoo.vn" dùng đề tìm tất cả các trang của website igoo.vn
Ngoài ra còn 1 số cú pháp, nhưng chỉ sử dụng tên Google.COM và không mấy phổ biến nên tôi không đề cập đến.
Những cú pháp tìm kiếm nâng cao với google ! Cực hay !
[ intitle: ]
Cú pháp "intitle:" giúp Google giới hạn kết quả tìm kiếm về những trang có chứa từ đó trong tiêu đề. Ví dụ, "intitle: login password" (không có ngoặc kép) sẽ cho kết quả là những link đến những trang có từ "login" trong tiêu đề, và từ "password" nằm ở đâu đó trong trang.
Tương tự, nếu ta muốn truy vấn nhiều hơn một từ trong tiêu đề của trang thì ta có thể dùng "allintitle:" thay cho "intitle" để có kết quả là những trang có chứa tất cả những từ đó trong tiêu đề. Ví dụ như dùng
"intitle: login intitle: password" cũng giống như truy vấn "allintitle: login password".
[ inurl: ]
Cú pháp "inurl:" giới hạn kết quả tìm kiếm về những địa chỉ URL có chứa từ khóa tìm kiếm. Ví dụ: "inurl: passwd" (không có ngoặc kép) sẽ cho kết quả là những link đến những trang có từ "passwd" trong URL.
Tương tự, nếu ta muốn truy vấn nhiều hơn một từ trong URL thì ta có thể dùng "allinurl:" thay cho "inurl" để được kết quả là những URL chứa tất cả những từ khóa tìm kiếm.Ví dụ: "allinurl: etc/passwd" sẽ tìm kiếm những URL có chứa "etc" và "passwd". Ký hiệu gạch chéo ("/") giữa các từ sẽ bị Google bỏ qua.
[ site: ]
Cú pháp "site:" giới hạn Google chỉ truy vấn những từ khóa xác định trong một site hoặc tên miền riêng biệt. Ví dụ: "exploits site:hackingspirits.com" (không có ngoặc kép) sẽ tìm kiếm từ khóa "exploits" trong những trang hiện có trong tất cả các link của tên miền "hackingspirits.com". Không có khoảng trống nào giữa "site:" và "tên miền".
[ filetype: ]
Cú pháp "filetype:" giới hạn Google chỉ tìm kiếm những files trên internet có phần mở rộng riêng biệt (Ví dụ: doc, pdf hay ppt v.v...). Ví dụ : "filetype:doc site:gov confidential" (không có ngoặc kép) sẽ tìm kiếm những file có phẩn mở rộng là ".doc" trong tất cả những tên miền của chính phủ có phần mở rộng là ".gov" và chứa từ "confidential"(bí mật) trong trang hoặc trong file ".doc". Ví dụ . Kết quả sẽ bao gồm những liên kết đến tất cả các file văn bản bí trên các site của chính phủ.
[ link: ]
Cú pháp "link:" sẽ liệt kê những trang web mà có các liên kết đến đến những trang web chỉ định. Ví dụ :
chuỗi "link:www.securityfocus.com" sẽ liệt kê những trang web có liên kết trỏ đến trang chủ SecurityFocus.
Chú ý không có khoảng trống giữa "link:" và URL của trang Web.
[ related: ]
Cú pháp "related:" sẽ liệt kê các trang Web "tương tự" với trang Web chỉ định. Ví dụ :
"related:www.securityfocus.com" sẽ liệt kê các trang web tương tự với trang chủ Securityfocus. Nhớ rằng không có khoảng trống giữa "related:" và URL của trang Web.
[ cache: ]
Truy vấn "cache:" sẽ cho kết quả là phiên bản của trang Web mà mà Google đã lưu lại. Ví dụ:
"cache:www.hackingspirits.com" sẽ cho ra trang đã lưu lại bởi Google's. Nhớ rằng không có khoảng trống giữa "cache:" và URL của trang web.
Nếu bạn bao gồm những từ khác trong truy vấn, Google sẽ điểm sáng những từ này trong văn bản đã được lưu lại.
Ví dụ: "cache:www.hackingspirits.com guest" sẽ cho ra văn bản đã được lưu lại có từ "guest" được điểm sáng.
[ intext: ]
Cú pháp "intext:" tìm kiếm các từ trong một website riêng biệt. Nó phớt lờ các liên kết hoặc URL và tiêu đề của trang.
Ví dụ: "intext:exploits" (không có ngoặc kép) sẽ cho kết quả là những liên kết đến những trang web có từ khóa tìm kiếm là "exploits" trong các trang của nó.
[ phonebook: ]
"phonebook" tìm kiếm thông tin về các địa chỉ đường phố ở Mỹ và số điện thoại. Ví dụ:
"phonebook:Lisa+CA" sẽ liệt kê tất cả các tên người có từ "Lisa" trong tên và ở "California (CA)". Cú pháp này có thể được sử dụng như là một công cụ tuyệt vời của tin tặc trong trường hợp ai đó muốn tìm kiếm thông tin cá nhân cho công việc xã hội.
Truy vấn các site hoặc server dễ bị tấn công sử dụng các cú pháp nâng cao của Google
Những cú pháp truy vấn nâng cao thảo luận ở trên thực sự có thể giúp người ta chính xác hóa các tìm kiếm và có được những gì họ thực sự tìm kiếm.
Bây giờ Google trở thành một máy tìm kiếm thông minh, những người dùng có ác ý không hề bận tâm khai thác khả năng của nó để đào bới những thông tin bí mật từ internet mà chỉ có sự truy cập giới hạn. Bây giờ tôi sẽ thảo luận những kỹ thuật này một cách chi tiết làm thế nào để những người dùng ác tâm đào bới thông tin trên internet sử dụng Google như một công cụ.
Sử dụng cú pháp "Index of " để tìm kiếm các site cho phép duyệt chỉ mục
Một webserver(máy chủ web) cho phép duyệt chỉ mục nghĩa là bất kỳ ai có thể duyệt các thư mục của webserver như các thư mục nội bộ thông thường. Ở đây tôi sẽ thảo luận làm thế nào để sử dụng cú pháp "index of" để có một danh sách các liên kết đến webserver cho phép duyệt thư mục.
Cách này trở thành một nguồn dễ dàng cho việc thu thập thông tin của tin tặc. Tưởng tưởng nếu họ nắm được các file mật khẩu hoặc các file nhạy cảm khác mà bình thưởng không thể thấy được trên internet.
Dưới đây là vài Ví dụ sử dụng để có được quyền truy cập vào rất nhiều thông tin nhạy cảm dễ dàng hơn rất nhiều:
Index of /admin
Index of /passwd
Index of /password
Index of /mail
"Index of /" +passwd
"Index of /" +password.txt
"Index of /" +.htaccess
"Index of /secret"
"Index of /confidential"
"Index of /root"
"Index of /cgi-bin"
"Index of /credit-card"
"Index of /logs"
"Index of /config"
Tìm kiếm các site hoặc server dễ bị tấn công sử dụng cú pháp "inurl:" hoặc "allinurl:"
a. Sử dụng "allinurl:winnt/system32/" (không có ngoặc kép) sẽ liệt kê tất cả các liên kết đến server mà cho phép truy cập đến những thư mục giới hạn như "system32" qua web. Nếu bạn đủ may mắn thì bạn có thể có quyền truy cập đến file cmd.exe trong thư mục "system32". Một khi bạn có quyền truy cập đến file "cmd.exe" và có thể thực thi nó thì bạn có thể tiến lên xa hơn
leo thang quyền của bạn khắp server và làm hại nó.
b. Sử dụng "allinurl:wwwboard/passwd.txt"(không có ngoặc kép) trong
Google search sẽ liệt kê tất cả các liên kết đến server mà dễ bị tấn công vào "tính dễ bị tấn công mật khẩu WWWBoard". Để biết thêm về tính dễ bị tấn công này bạn có thể vào link sau đây:
http://www.securiteam.com/exploits/2BUQ4S0SAW.html
c. Sử dụng "inurl:.bash_history" (không có ngoặc kép) sẽ liệt kê tất cả các liên kết đến server mà cho phép truy cập vào file
".bash_history" qua web. Đây là một file lịch sử dòng lệnh. File này bao gồm danh sách các lệnh được thực thi bởi quản trị viên,
, và đôi khi bao gồm cả thông tin nhạy cảm như mật khẩu
gõ vào bởi quản trị viên. Nếu file này bị làm hại
và nếu nó bao gồm mật khẩu đã mã hóa của hệ thống unix (or *nix)
thì nó có thể dễ dàng bị crack bởi phương pháp "John The
Ripper".
d. Sử dụng "inurl:config.txt" (không có ngoặc kép) sẽ liệt kê tất cả các liên kết đến các máy chủ cho phép truy cập vào file "config.txt"
qua giao diện web. File này bao gồm các thông tin nhạy cảm,
bao gồm giá trị bị băm ra của mật khẩu quản trị và sự xác thực quyền truy cập cơ sở dữ liệu. Ví dụ: Hệ thống quản lý học tập Ingenium
là một ứng dụng Web cho các hệ thống Windows phát triển bởi Click2learn, Inc. Hệ thống quản lý học tập Ingenium
phiên bản 5.1 và 6.1 lưu các thông tin nhạy cảm không an tòan trong file config.txt. Để biết thêm thông tin vào liên kết sau:
Những tìm kiếm tương tự khác dùng "inurl:" hoặc "allinurl:" kết hợp với các cú pháp khác:
inurl:admin filetype:txt
inurl:admin filetype:db
inurl:admin filetype:cfg
inurl:mysql filetype:cfg
inurl:passwd filetype:txt
inurl:iisadmin
inurl:auth_user_file.txt
inurl:orders.txt
inurl:"wwwroot/*."
inurl:adpassword.txt
inurl:webeditor.php
inurl:file_upload.php
inurl:gov filetype:xls "restricted"
index of ftp +.mdb allinurl:/cgi-bin/ +mailto
Tìm kiếm các site hoặc server dễ bị tấn công dùng "intitle:" hoặc "allintitle:"
a. Sử dụng [allintitle: "index of /root"] (không có ngoặc vuông) sẽ liệt kê các liên kết đến các webserver(máy chủ Web) cho phép truy cập vào các thư mục giới hạn như "root" qua giao diện web. Thư mục này đôi khi bao gồm các thông tin nhạy cảm mà có thể dễ dàng tìm được tqua những yêu cầu Web đơn giản.
b. Sử dụng [allintitle: "index of /admin"] (không có ngoặc vuông) sẽ liệt kê các liên kết đến các website cho phép duyệt chỉ mục các thư mục giới hạn như "admin" qua giao diện web. Hầu hết các ứng dụng web đôi khi sử dụng tên như "admin" để lưu quyền admin trong đó. Thư mục này đôi khi bao hàm các thông tin nhạy cảm mà có thể dễ dàng tìm được qua các yêu cầu Web đơn giản.
Những tìm kiếm tương tự dùng "intitle:" hoặc "allintitle:" kết hợp với các cú pháp khác
intitle:"Index of" .sh_history
intitle:"Index of" .bash_history
intitle:"index of" passwd
intitle:"index of" people.lst
intitle:"index of" pwd.db
intitle:"index of" etc/shadow
intitle:"index of" spwd
intitle:"index of" master.passwd
intitle:"index of" htpasswd
intitle:"index of" members OR accounts
intitle:"index of" user_carts OR user_cart
allintitle: sensitive filetype:doc
allintitle: restricted filetype :mail
allintitle: restricted filetype:doc site:gov
Những truy vấn tìm kiếm thú vị khác
Để tìm những site dễ bị tấn công bằng phương pháp Cross-Sites Scripting (XSS):
allinurl:/scripts/cart32.exe
allinurl:/CuteNews/show_archives.php
allinurl:/phpinfo.php
Để tìm những site dễ bị tấn công bằng phương pháp SQL Injection:
allinurl:/privmsg.php
allinurl:/privmsg.php
Thứ Ba, 13 tháng 11, 2012
Nghề lập trình và những sự thật ít người biết tới
Lập trình là một công việc như thế nào? Có phải lập trình viên là những người tối ngày ngồi trước màn hình máy tính? Cứ code giỏi thì sản phẩm sẽ tốt? …
Lập trình là một lĩnh vực mà nếu không phải trong nghề thì thật khó thấu hiểu những nỗi "trần ai" trong đó. Sau đây, kĩ sư công nghệ thông tin David Veksler – chủ trang blog The Rational Mind - sẽ chia sẻ cho chúng ta những sự thật ít ai biết về nghề IT – nghề lập trình:
- Chỉ 10-20% thời gian của toàn bộ dự án được các lập trình viên sử dụng để viết code, và bất kể trình độ, mỗi ngày 1 lập trình viên chỉ viết trung bình từ 10-20 dòng code. Sự khác biệt giữa tay chuyên và gà mờ đó là tay chuyên thì dành 90% thời gian để suy luận, tìm tòi và thử nghiệm các phương án tối ưu nhất cho mình; còn những gà mờ thì dành 90% thời gian để debug, thay đổi vài chỗ trong code, lại debug và mong đến một lúc nào đó chương trình sẽ hoạt động.
- Một lập trình viên tốt cho năng suất gấp 10 một lập trình viên tầm tầm. Một lập trình viên giỏi có thể còn hơn nữa, 20 cho đến 100 lần tùy hoàn cảnh. Đây là một sự thật không hề "gió" và đã được khoa học chứng minh từ tận những năm 60. Còn một lập trình viên tồi thì chung quy hiệu suất bằng không – chẳng làm nên chuyện gì, ngược lại vẽ ra nhiều vấn đề rối tinh rối mù cho người khác phải bận tâm sửa hộ."Một công nhân có kĩ thuật tiện cao siêu có thể giá trị gấp vài lần một công nhân với kĩ thuật trung bình. Nhưng một lập trình viên giỏi thì có giá trị gấp 10,000 lần một lập trình viên với trình độ trung bình." – Bill Gates
- Các lập trình viên giỏi thường không dành nhiều thời gian để code, trái lại những người code liên tục từ lúc này sang lúc khác lại là những người lười biếng, vô tâm, kiêu ngạo và ít chịu tìm tòi nhất. Người lập trình tốt là người biết vận dụng linh hoạt mọi giải pháp mình từng vận dụng qua cho mọi vấn đề họ gặp phải; người lập trình tồi là người viết nên những đoạn code dài dòng, rắc rối, bố cục lung tung và khó để chỉnh sửa. Thực ra quẳng hết mớ code hỏng ra ngoài và bắt đầu lại từ đầu lại là một lựa chọn hữu ích hơn ngồi ì ạch sửa từng chút một.
- Định luật entropy bao hàm mọi vật – entropy là một hàm biểu thị mức độ hỗn loạn của một sự kiện diễn ra trong vô số các khả năng ngẫu nhiên có thể diễn ra. Trong lập trình, thay đổi liên tục sẽ dẫn đến phá vỡ mô hình nguyên bản của thiết kế, dẫn đến sự suy giảm giá trị phần mềm (software rot). Dẫu biết điều này là không thể tránh khỏi, song các lập trình viên không nắm bắt được hình mẫu sơ khai của dự án và biết cách biến nó thành hiện thực sẽ tạo ra những phần mềm thất bại đến nỗi giá trị đã tiêu hao sạch sẽ kể cả trước khi dự án hoàn thành, và đây thường là lý do phổ biến nhất tạo nên các phần mềm thất bại (Lí do phổ biến thứ nhì là tạo ra những giá trị mà khách hàng không cần đến).
- Trong năm 1994, 31% các dự án phần mềm sẽ thất bại "sặc gạch" từ đầu đến chân. Con số này đến năm 2004 thì đã được cải thiện, chỉ còn 15%, song 51% các dự án thì bị thất bại trên phương diện trí mạng của mình.
- Mặc dù hầu hết các phần mềm đều được xây dựng bởi các đội ngũ chứ không riêng gì ai, nhưng đó cũng không phải là một hoạt động mang tính dân chủ. Thường thì chỉ có một người duy nhất chịu trách nhiệm về thiết kế, còn lại cả đội nghĩ cách lấp đầy cách khoảng trống xuất hiện. Kết cấu này giống với một tổ ong hay kiến nhiều hơn.
- Lập trình là việc của những người chăm chỉ, trong đó, đặc biệt là bộ não sẽ phải hoạt động điên cuồng. Những lập trình viên tốt nghĩ về công việc của mình 24/7. Họ viết ra những dòng code tuyệt vời nhất trong bồn tắm hay những giấc mơ. Tại sao? Bởi hầu hết các công việc quan trọng đều không được hoàn thành tại chính nơi làm việc của nó, bản thân Einstein cũng đã tháo gỡ nút thắt trong thuyết tương đối của mình khi mơ. Làm phần mềm không phải đơn giản chỉ cần dành nhiều thời gian làm việc hay thêm người vào là có thể hoàn thành.
Theo The Rational Mind
http://blogdaynauan.blogspot.com/
http://cungnauan.blogspot.com/
http://muivi.blogspot.com/
http://amthuctop1.blogspot.com/
http://blognauanngon.blogspot.com/
http://amthuccuocsong.blogspot.com/
http://doisongamthuc.blogspot.com/
http://amthucvietvn.blogspot.com/
http://nauanngonvn.blogspot.com/
http://amthucbonmuavn.blogspot.com/
http://dinhduongamthuc.blogspot.com/
http://biquyetnaungon.blogspot.com/
http://amthucsuckhoe.blogspot.com/
http://congthucnauanngon.blogspot.com/
http://tapchiamthucvn.blogspot.com/
http://nauanmoingayvn.blogspot.com/
http://amthucvungmien.blogspot.com/
http://nauankhongkho.blogspot.com/
http://monngondaikhach.blogspot.com/
http://chebienmonan.blogspot.com/
http://tapchinauan.blogspot.com/