【stanford C++】字符串(String)与流(Stream)

时间:2022-03-09 23:57:02
字符串(String)与流(Stream)
一、C++中字符串(String)

字符串(String):就是(可能是空的)字符序列。
C++中的字符串在概念上和Java中的字符串类似。

C++字符串用string类型来表示。在使用string类型之前,必须在程序中包含如下头文件
#include <string>

可以通过调用如下方法:

str.length()

来获取字符串中字符的长度。


可以通过如下方式来从一个字符串中读取一个字符
str[index]

尽管字符串不是数组,但是上述语法是一个方便的语法方式。


字符操作
在C++中,头文件<cctype>包含各种有用的处理字符的函数,以下函数用来检查给定的类型是否是一个给定的字符
isalpha, isdigit, isalnum, islower, isupper, isspace, ispunct.

跟Java中字符串不同,C++中字符串是可变的,可以被修改。
改变单个字符的方式:
str[index] = ch
附加更多的文本方式:
str += text

这些操作直接改变字符串本身,而不是对字符串的副本进行操作。


在C++中,==操作符可以直接拿来用于字符串的比较
if(str1 == str2)
{
/* string match */
}

在一个字符串中查询其他一些字符,可以使用find,如果找不到,则返回string::npos,而不是-1。

if(str1.find(str2) != string::npos)
{
/* found str2 inside str1 */
}

通过调用substr方法从string中获取substring。

substr方法需要知道substring的开始位置和长度(不是结束位置)
string allButFirstChar = str.substr(1);
string lastFiveChars = str.substr(str.length() - 5, 5);
与Java语言不同的时,在C++中,只能连接字符串和字符到其他的字符串中。

在本课程中,提供了"strlib.h"库,让字符串操作更加容易
string s = "I like " + integerToString(137);

strlib.h的代码如下:

/*
* File: strlib.h
* --------------
* This file exports several useful string functions that are not
* included in the C++ string library.
*/ #ifndef _strlib_h
#define _strlib_h #include <iostream>
#include <string> /*
* Function: integerToString
* Usage: string s = integerToString(n);
* -------------------------------------
* Converts an integer into the corresponding string of digits.
* For example, calling <code>integerToString(123)</code> returns
* the string <code>"123"</code>.
*/ std::string integerToString(int n); /*
* Function: stringToInteger
* Usage: int n = stringToInteger(str);
* ------------------------------------
* Converts a string of digits into an integer. If the string is not a
* legal integer or contains extraneous characters other than whitespace,
* <code>stringToInteger</code> calls <code>error</code> with an
* appropriate message.
*/ int stringToInteger(std::string str); /*
* Function: realToString
* Usage: string s = realToString(d);
* ----------------------------------
* Converts a floating-point number into the corresponding string form.
* For example, calling <code>realToString(23.45)</code> returns
* the string <code>"23.45"</code>.
*/ std::string realToString(double d); /*
* Function: stringToReal
* Usage: double d = stringToReal(str);
* ------------------------------------
* Converts a string representing a real number into its corresponding
* value. If the string is not a legal floating-point number or contains
* extraneous characters other than whitespace, <code>stringToReal</code>
* calls <code>error</code> with an appropriate message.
*/ double stringToReal(std::string str); /*
* Function: toUpperCase
* Usage: string s = toUpperCase(str);
* -----------------------------------
* Returns a new string in which all lowercase characters have been converted
* into their uppercase equivalents.
*/ std::string toUpperCase(std::string str); /*
* Function: toLowerCase
* Usage: string s = toLowerCase(str);
* -----------------------------------
* Returns a new string in which all uppercase characters have been converted
* into their lowercase equivalents.
*/ std::string toLowerCase(std::string str); /*
* Function: equalsIgnoreCase
* Usage: if (equalsIgnoreCase(s1, s2)) ...
* ----------------------------------------
* Returns <code>true</code> if <code>s1</code> and <code>s2</code> are
* equal discounting differences in case.
*/ bool equalsIgnoreCase(std::string s1, std::string s2); /*
* Function: startsWith
* Usage: if (startsWith(str, prefix)) ...
* ---------------------------------------
* Returns <code>true</code> if the string <code>str</code> starts with
* the specified prefix, which may be either a string or a character.
*/ bool startsWith(std::string str, std::string prefix);
bool startsWith(std::string str, char prefix); /*
* Function: endsWith
* Usage: if (endsWith(str, suffix)) ...
* -------------------------------------
* Returns <code>true</code> if the string <code>str</code> ends with
* the specified suffix, which may be either a string or a character.
*/ bool endsWith(std::string str, std::string suffix);
bool endsWith(std::string str, char suffix); /*
* Function: trim
* Usage: string trimmed = trim(str);
* ----------------------------------
* Returns a new string after removing any whitespace characters
* from the beginning and end of the argument.
*/ std::string trim(std::string str); /* Private section */ /**********************************************************************/
/* Note: Everything below this point in the file is logically part */
/* of the implementation and should not be of interest to clients. */
/**********************************************************************/ /*
* Friend function: writeQuotedString
* Usage: writeQuotedString(outfile, str, forceQuotes);
* ----------------------------------------------------
* Writes the string str to outfile surrounded by double quotes, converting
* special characters to escape sequences, as necessary. If the optional
* parameter forceQuotes is explicitly set to false, quotes are included
* in the output only if necessary.
*/ void writeQuotedString(std::ostream & os, const std::string & str,
bool forceQuotes = true); /*
* Friend function: readQuotedString
* Usage: readQuotedString(infile, str);
* -------------------------------------
* Reads the next string from infile into the reference parameter str.
* If the first character (other than whitespace) is either a single
* or a double quote, this function reads characters up to the
* matching quote, processing standard escape sequences as it goes.
* If not, readString reads characters up to any of the characters
* in the string STRING_DELIMITERS in the implementation file.
*/ void readQuotedString(std::istream & is, std::string & str); /*
* Friend function: stringNeedsQuoting
* Usage: if (stringNeedsQuoting(str)) ...
* ---------------------------------------
* Checks whether the string needs quoting in order to be read correctly.
*/ bool stringNeedsQuoting(const std::string & str); /*
* Friend function: writeGenericValue
* Usage: writeGenericValue(os, value, forceQuotes);
* -------------------------------------------------
* Writes a generic value to the output stream. If that value is a string,
* this function uses writeQuotedString to write the value.
*/ template <typename ValueType>
void writeGenericValue(std::ostream & os, const ValueType & value,
bool forceQuotes) {
os << value;
} template <>
inline void writeGenericValue(std::ostream & os, const std::string & value,
bool forceQuotes) {
writeQuotedString(os, value, forceQuotes);
} /*
* Friend function: readGenericValue
* Usage: readGenericValue(is, value);
* -----------------------------------
* Reads a generic value from the input stream. If that value is a string,
* this function uses readQuotedString to read the value.
*/ template <typename ValueType>
void readGenericValue(std::istream & is, ValueType & value) {
is >> value;
} template <>
inline void readGenericValue(std::istream & is, std::string & value) {
readQuotedString(is, value);
} #endif

strlib.c的代码如下:

/*
* File: strlib.cpp
* ----------------
* This file implements the strlib.h interface.
*/ #include <cctype>
#include <iostream>
#include <sstream>
#include "error.h"
#include "strlib.h"
using namespace std; /* Function prototypes */ /*
* Implementation notes: numeric conversion
* ----------------------------------------
* These functions use the <sstream> library to perform the conversion.
*/ string integerToString(int n) {
ostringstream stream;
stream << n;
return stream.str();
} int stringToInteger(string str) {
istringstream stream(str);
int value;
stream >> value >> ws;
if (stream.fail() || !stream.eof()) {
error("stringToInteger: Illegal integer format (" + str + ")");
}
return value;
} string realToString(double d) {
ostringstream stream;
stream << uppercase << d;
return stream.str();
} double stringToReal(string str) {
istringstream stream(str);
double value;
stream >> value >> ws;
if (stream.fail() || !stream.eof()) {
error("stringToReal: Illegal floating-point format (" + str + ")");
}
return value;
} /*
* Implementation notes: case conversion
* -------------------------------------
* The functions toUpperCase and toLowerCase return a new string whose
* characters appear in the desired case. These implementations rely on
* the fact that the characters in the string are copied when the
* argument is passed to the function, which makes it possible to change
* the case of the copy without affecting the original.
*/ string toUpperCase(string str) {
int nChars = str.length();
for (int i = 0; i < nChars; i++) {
str[i] = toupper(str[i]);
}
return str;
} string toLowerCase(string str) {
int nChars = str.length();
for (int i = 0; i < nChars; i++) {
str[i] = tolower(str[i]);
}
return str;
} /*
* Implementation notes: equalsIgnoreCase
* --------------------------------------
* This implementation uses a for loop to cycle through the characters in
* each string. Converting each string to uppercase and then comparing
* the results makes for a shorter but less efficient implementation.
*/ bool equalsIgnoreCase(string s1, string s2) {
if (s1.length() != s2.length()) return false;
int nChars = s1.length();
for (int i = 0; i < nChars; i++) {
if (tolower(s1[i]) != tolower(s2[i])) return false;
}
return true;
} /*
* Implementation notes: startsWith, endsWith
* ------------------------------------------
* These implementations are overloaded to allow the second argument to
* be either a string or a character.
*/ bool startsWith(string str, string prefix) {
if (str.length() < prefix.length()) return false;
int nChars = prefix.length();
for (int i = 0; i < nChars; i++) {
if (str[i] != prefix[i]) return false;
}
return true;
} bool startsWith(string str, char prefix) {
return str.length() > 0 && str[0] == prefix;
} bool endsWith(string str, string suffix) {
int nChars = suffix.length();
int start = str.length() - nChars;
if (start < 0) return false;
for (int i = 0; i < nChars; i++) {
if (str[start + i] != suffix[i]) return false;
}
return true;
} bool endsWith(string str, char suffix) {
return str.length() > 0 && str[str.length() - 1] == suffix;
} string trim(string str) {
int finish = str.length() - 1;
while (finish >= 0 && isspace(str[finish])) {
finish--;
}
int start = 0;
while (start <= finish && isspace(str[start])) {
start++;
}
return str.substr(start, finish - start + 1);
} /*
* Implementation notes: readQuotedString and writeQuotedString
* ------------------------------------------------------------
* Most of the work in these functions has to do with escape sequences.
*/ static const string STRING_DELIMITERS = ",:)}]\n"; bool stringNeedsQuoting(const string & str) {
int n = str.length();
for (int i = 0; i < n; i++) {
char ch = str[i];
if (isspace(ch)) return false;
if (STRING_DELIMITERS.find(ch) != string::npos) return true;
}
return false;
} void readQuotedString(istream & is, string & str) {
str = "";
char ch;
while (is.get(ch) && isspace(ch)) {
/* Empty */
}
if (is.fail()) return;
if (ch == '\'' || ch == '"') {
char delim = ch;
while (is.get(ch) && ch != delim) {
if (is.fail()) error("Unterminated string");
if (ch == '\\') {
if (!is.get(ch)) error("Unterminated string");
if (isdigit(ch) || ch == 'x') {
int base = 8;
if (ch == 'x') base = 16;
int result = 0;
int digit = 0;
while (ch != delim) {
if (isdigit(ch)) {
digit = ch - '0';
} else if (isalpha(ch)) {
digit = toupper(ch) - 'A' + 10;
} else {
digit = base;
}
if (digit >= base) break;
result = base * result + digit;
if (!is.get(ch)) error("Unterminated string");
}
ch = char(result);
is.unget();
} else {
switch (ch) {
case 'a': ch = '\a'; break;
case 'b': ch = '\b'; break;
case 'f': ch = '\f'; break;
case 'n': ch = '\n'; break;
case 'r': ch = '\r'; break;
case 't': ch = '\t'; break;
case 'v': ch = '\v'; break;
case '"': ch = '"'; break;
case '\'': ch = '\''; break;
case '\\': ch = '\\'; break;
}
}
}
str += ch;
}
} else {
str += ch;
int endTrim = 0;
while (is.get(ch) && STRING_DELIMITERS.find(ch) == string::npos) {
str += ch;
if (!isspace(ch)) endTrim = str.length();
}
if (is) is.unget();
str = str.substr(0, endTrim);
}
} void writeQuotedString(ostream & os, const string & str, bool forceQuotes) {
if (!forceQuotes && stringNeedsQuoting(str)) forceQuotes = true;
if (forceQuotes) os << '"';
int len = str.length();
for (int i = 0; i < len; i++) {
char ch = str.at(i);
switch (ch) {
case '\a': os << "\\a"; break;
case '\b': os << "\\b"; break;
case '\f': os << "\\f"; break;
case '\n': os << "\\n"; break;
case '\r': os << "\\r"; break;
case '\t': os << "\\t"; break;
case '\v': os << "\\v"; break;
case '"': os << oct << "\\" << (int(ch) & 0xFF); break;
case '\\': os << "\\\\"; break;
default:
if (isprint(ch)) {
os << ch;
} else {
ostringstream oss;
oss << oct << (int(ch) & 0xFF);
os << "\\" << oss.str();
}
}
}
if (forceQuotes) os << '"';
}

在C++中,有两种类型的字符串:

  • C类型字符串,来自于C编程语言
  • C++类型string,C++实现的库
在C++中,尽可能的使用string类型。

对于string s = "Nubian " + "ibex";
这些字符串是C风格的,C风格的字符串是不支持+操作的,该表达式编译不通过。改为如下:
string s = string("Nubian ") + "ibex";

现在显式的转换C风格的字符串为C++类型的字符串,这样该代码是合法的。


二、字符串中的递归操作

1.对一个字符串进行逆序操作
递归的对字符串进行逆序操作,如下示意图所示:
【stanford C++】字符串(String)与流(Stream)

代码实现如下:
/* File: reverse.cpp
*
* Code to recursively reverse a string.
*/
#include <iostream>
#include <string>
#include "simpio.h"
using namespace std; string reverseString(string line); int main() {
string line = getLine("Enter a string: ");
cout << reverseString(line) << endl;
} /* Returns the reverse of the indicated string. */
string reverseString(string line) {
/* If the string is empty, it's its own reverse */
if (line == "") {
return "";
}
/* Otherwise, reverse all but the first character, then tack
* on the first character.
*/
else {
return reverseString(line.substr(1)) + line[0];
}
}

2.回文(palindrome)

回文就是给定的字符串是对称的。
递归的判断给定的字符串是否是回文,示意图如下:
【stanford C++】字符串(String)与流(Stream)
代码实现如下:
/* File: palindrome.cpp
*
* A program that reads a file of English words, then prints out all
* the palindromic words.
*/
#include <iostream>
#include <string>
#include <fstream>
#include "simpio.h"
using namespace std; bool isPalindrome(string text); int main() {
/* Open the file for reading. We really should check whether
* the file is open before proceeding.
*/
string file = "dictionary.txt";
ifstream input(file.c_str()); /* Read each line of the file and print out those that are palindromes. */
string line;
while (getline(input, line)) {
if (isPalindrome(line)) {
cout << line << endl;
}
} return 0;
} /* Returns whether the given string is a palindrome. */
bool isPalindrome(string text) {
/* All characters of length 0 or length 1 are guaranteed to
* be palindromes.
*/
if (text.length() <= 1) {
return true;
}
/* If the first and last character of the string aren't the same,
* the string cannot be a palindrome.
*/
else if (text[0] != text[text.length() - 1]) {
return false;
}
/* Otherwise, this string is a palindrome precisely when the middle
* characters are a palindrome.
*/
else {
return isPalindrome(text.substr(1, text.length() - 2));
}
}

3.C++中从File中读取数据


既然我们知道了如何操作字符串了,那么我们开始从外部文件中读取数据来处理。
在C++中,文件读取使用ifstream类来进行处理。必须包含头文件#include <fstream>来使用ifstream。

1)逐行读取
ifstream类通过使用getline函数从文件中读取一行
getline(file, str);

典型的读取文件中各行的循环如下所示:

string line;
while(getline(file, line))
{
/* ...process line... */
}

回文的实现代码使用了文件的读取。


读取格式化的数据
从文件中读取格式化的数据可以通过使用流提取操作符:file>>variable
可以读取任何原始类型和字符串
当读取字符串时,在换行符或空格处即停止。

典型的读取格式化数据循环如下:
type val;
while(file >> val)
{
/* ... process val... */
}

4.C++中参数传递


在C++中,有两种方法传递一个参数给一个函数:
  • 传值方式:参数通过拷贝传给一个函数。void myFunction(int x);
  • 引用方式:传递给函数的变量在函数中是可以改变的。void myFunction(int &x)
举例:
int main()
{
int x = 10;
int y = 20; //here: x = 10, y = 20
sum(x, y);
//here: x = 10, y = 20
swap(x, y);
//here: x = 20, y = 10
cout << x << " " << y << endl; return 0;
} //Pass by reference
void swap(int &x, int &y)
{
int temp = x;
x = y;
y = temp;
} //Pass by value
void printSum(int x, int y)
{
x += y;
cout << x << endl;
}