XPCOM字符串操作(一)
XPCOM中的字符串类可以用操作做宽字节(16bit)和窄字节字符串。
宽字节和窄字节字符串基类是分开的,但是它们提供的接口是一致的。而对于每一个宽字节的实现类,都有一个类似的窄字节实现类。
Wide | Narrow |
nsAString | nsACString |
nsString | nsCString |
nsAutoString | nsCAutoString |
etc... |
抽象基类
所有的字符串类都是从nsAString
/nsACString这两个抽象基类派生。
nsAString
/nsACString提供了一些访问和操作字符串的基本方法。这两个类的命名模仿了Mozilla对于接口的命名,(Mozilla的接口命名中使用I来表示接口,如nsISupports),A表示“Abstract”。
下面是对字符串体系中主要类的总体描述:
nsAString
/nsACString: 所有字符串类的基类,定义了xpcom中字符串的基本操作,如赋值、字符存取、基本的字符操作,比较等。nsAString 可以不必是以null(\0)结尾的。
(nsAString is not necessarily null-terminated.)
nsString
/nsCString: 从以上两个基类继承而来,可以保证字符串是以null结尾。这两个类允许通过get()方法获取c风格的字符串(以'\0'结尾的字符数组)。
.Length()
- 字符串字符数目 (对于窄字符串类字符是char对于宽字节来说是PRUnichar
)。.IsEmpty()
- 最快的可以判断字符串为空的方法. 用这个方法来代替string.Length
== 0.Equals(string)
- 判定两个字符串内容相同.
-
.Assign(string)
- 用string的值给字符串赋值. -
.Append(string)
- 追加string到字符串. -
.Insert(string, position)
- 在给定位置(position
)的字符前插入string. -
.Truncate(length)
- 将字符串缩短到length指定的长度.
只读字符串
c++的const修饰符可以指定一个字符串类是否可写。 如果一个字符串被指定为const,并且视图调用一个non-const的方法,编译器将会给出一个错误。
For example:
void nsFoo::ReverseCharacters(nsAString& str) { ... str.Assign(reversedStr); // modifies the string }
而下面的代码将不能通过编译, 因为你为一个静态的对象进行赋值了:
void nsFoo::ReverseCharacters(const nsAString& str) { ... str.Assign(reversedStr); }
xpcom字符串类继承结构
-
nsString / nsCString
- 一个以null结尾的字符串,数据在堆(heap)上分配,在对象析构的时候会把数据也给干掉。 -
nsAutoString / nsCAutoString
- 拥有一个64个字符的缓冲区,缓冲区和字符串类本身位于同一存储空间。如果为之类字符串赋一个小于64个字符的值,它会使用它本身的缓冲区,如果超过64字符,它会在对上分配一个新的缓冲区。 -
nsXPIDLString / nsXPIDLCString
- 这类字符串支持通过getter_Copies()
方法获取wstring / string。
这类字符串同样支持空缓冲区(nsString的
buffer永远不会为null)。 -
nsDependentString
- 这个字符串永远都不会有自己的buffer。 当把一个原生的字符串转化成(const PRUnichar*
orconst char*
)nsAString类型的时候特别有用。注意你的字符串必须是以null结尾的
,如果不是使用 nsDependentSubstring。 -
nsPrintfCString
- 类似于nsCAutoString。
它的构造函数可以接受printf
-style的参数来格式化一个字符串。 -
NS_LITERAL_STRING/NS_NAMED_LITERAL_STRING
- 它们可以把常量字符串(such as "abc") 转化成一个nsString或者
nsString的子类型。
在支持双字节的平台上 (e.g., MSVC++ or GCC with the -fshort-wchar option), 它们是一些类似nsDependentString使用方式的简单的宏。
但是很显然宏比包装成nsDependentString要快很多。
Iterators
因为Mozilla中的字符串是一个单一的buffer,所以可以利用原生指针来完成迭代:
/** * Find whether there is a tab character in `data` */ bool HasTab(const nsAString& data) { const PRUnichar* cur = data.BeginReading(); const PRUnichar* end = data.EndReading(); for (; cur < end; ++cur) { if (PRUnichar('\t') == *cur) return true; } return false; }
注意end指向字符串结束字符的下一个位置,因为永远不要“提领”end指针.
向一个可变的字符串中写入也很容易:
/** * Replace every tab character in `data` with a space. */ void ReplaceTabs(nsAString& data) { PRUnichar* cur = data.BeginWriting(); PRUnichar* end = data.EndWriting(); for (; cur < end; ++cur) { if (PRUnichar('\t') == *cur) *cur = PRUnichar(' '); } }
可以通过SetLength()来改变字符串的长度。
需要注意的是如果要增加一个字符串长度的话可能会失败 (如没有足够的内存), 如果调用失败的话,字符串会简单的保持它的缓冲区和长度不变; 因此需要调用Length()方法看SetLength()是否成功. 同样要注意的是SetLength()成功以后迭代器指针将会失效:
/** * Replace every tab character in `data` with four spaces. */ void ReplaceTabs2(nsAString& data) { int len = data.Length(); PRUnichar *cur = data.BeginWriting(); PRUnichar *end = data.EndWriting(); // Because `cur` may change during the loop, track the position // within the string. int pos = 0; while (cur < end) { if (PRUnichar('\t') != *cur) { ++pos; ++cur; } else { len += 3; data.SetLength(len); if (data.Length() != len) // error handling goes here! // After SetLength, read `cur` and `end` again cur = data.BeginWriting() + pos; end = data.EndWriting(); // move the remaining data over if (pos < len - 1) memmove(cur + 4, cur + 1, (len - 1 - pos) * sizeof(PRUnichar)); // fill the tab with spaces *cur = PRUnichar(' '); *(cur + 1) = PRUnichar(' '); *(cur + 2) = PRUnichar(' '); *(cur + 3) = PRUnichar(' '); pos += 4; cur += 4; } } }
如果在写入一个字符串的时候字符串的buffer变小了,用SetLength来通知你的对象:
/**
* Remove every tab character from `data`
*/
void RemoveTabs(nsAString& data)
{
int len = data.Length();
PRUnichar* cur = data.BeginWriting();
PRUnichar* end = data.EndWriting();
while (cur < end) {
if (PRUnichar('\t') == *cur) {
len -= 1;
end -= 1;
if (cur < end)
memmove(cur, cur + 1, (end - cur) * sizeof(PRUnichar));
} else {
cur += 1;
}
}
data.SetLength(len);
}
帮助类和函数
搜索一个字符串
FindInReadable()
用来替代老的string.Find(..)函数:
PRBool FindInReadable(const nsAString& pattern, nsAString::const_iterator start, nsAString::const_iterator end, nsStringComparator& aComparator = nsDefaultStringComparator());
start
和end指向搜索的起点和终点。
如果搜索成功, start和
end将会指向找到的字串的起点和终点。
函数返回PR_TRUE或PR_FALSE来指示搜索是否成功。
An example:
const nsAString& str = GetSomeString(); nsAString::const_iterator start, end; str.BeginReading(start); str.EndReading(end); NS_NAMED_LITERAL_STRING(valuePrefix, "value="); if (FindInReadable(valuePrefix, start, end)) { // end now points to the character after the pattern valueStart = end; }
内存分配
从一个已经存在的字符串类分配一个新的字符buffer(意思是你得到一个新的c-style字符指针或数组,这个c-style的字符指针或数组里面字符的值和字符串类相等,当然如果这样的话意味着您得自己去释放这部分内存)的首选方法如下:
-
PRUnichar* ToNewUnicode(nsAString&)
- 从nsAString分配PRUnichar*
buffer 。 -
char *ToNewCString(nsACString&)
- 从nsACString分配char*
buffer。需要注意的是这个函数也可以接受nsAString参数,但是这个时候会进行有损失的转化,所以最好在知道所转化的字符串是ASCII编码的情况下使用此函数。 -
char* ToNewUTF8String(nsAString&)
- 从一个UTF-8编码的nsAString上分配char*
buffer。
这些函数返回的buffer是通过XPCOM的allocator分配空间的(不使用malloc等)。所以必须使用NS_Free来释放其内存。
子串(string fragments)
如果不需要重新分配空间并拷贝字符到新空间的话,或者一个字符串的字串是很容易的。 Substring()方法是完成这一工作的首选:
void ProcessString(const nsAString& str) { const nsAString& firstFive = Substring(str, 0, 5); // from index 0, length 5 // firstFive is now a string representing the first 5 characters }