What you need is this: http://www.yiiframew.../slug-behavior/
It will by default clean out all funky characters and replace spaces with a dash.
I am using it, and it works great.
What you need to change is these lines (line 130 onwards) in the behavior:
$title = strtolower($title);
$title = preg_replace('/&.+?;/', '', $title); // kill entities
$title = str_replace('.', '-', $title);
$title = preg_replace('/[^%a-z0-9 _-]/', '', $title);
$title = preg_replace('/\s+/', '-', $title);
$title = preg_replace('|-+|', '-', $title);
$title = trim($title, '-');
You could change it to this:
$title = strtolower($title);
$title = preg_replace('/&.+?;/', '', $title); // kill entities
$title = str_replace('.', '_', $title);
$title = preg_replace('/[^%a-z0-9 _-]/', '', $title);
$title = preg_replace('/\s+/', '_', $title);
$title = preg_replace('|-+|', '_', $title);
$title = trim($title, '_');
Not tested. 
Then you need to adjust your controller(s) to accept the slug instead of the id:
public function getPost() {
if(isset($_GET['slug'])){
return $this->loadModel($_GET['slug']);
}
}
public function loadModel($slug) {
if ($this->_model === null) {
if (Yii::app()->user->isGuest)
$condition = 'status="' . Post::STATUS_PUBLISHED . '" OR status="' . Post::STATUS_ARCHIVED . '"';
else
$condition='';
$this->_model = Post::model()->findByAttributes(array('slug' => $slug), $condition);
if ($this->_model === null)
throw new CHttpException(404, 'The requested page does not exist.');
}
return $this->_model;
}
public function actionView($slug) {
$post = $this->loadModel($slug);
$this->pageDescription = $post->description;
$this->pageKeywords = $post->tags->toString();
$comment = $this->newComment($post);
$this->render('view', array(
'model' => $post,
'comment' => $comment,
));
}
And the URL rules:
''=>'post/index',
'<slug:[a-z0-9-]+>'=>'post/view',
'page/<slug:[a-z0-9-]+>'=>'page/view',