C++中实现文件的读出与写入
补充:string天然就能存储一个字符串,而str类型则需要定义成字符数组char c[]才可以存储几个字符或者一句话。
例如:char c[10]=“hello!”;
string s=“hello!”;
#include <string>
#include <fstream>
using namespace std;
#### 1.将string类型的数据s(内容是hello)中的第一个字母从out当前内容的末尾开始写入out文件。
int main()
{
ifstream myfile("F:\\C++ project\\hello.txt");
ofstream outfile("F:\\C++ project\\out.txt", ios::app);
string temp;
if (!myfile.is_open())
{
cout << "未成功打开文件" << endl;
}
string s("hello word");
while(getline(myfile,temp))
{
outfile << s[0];
outfile << endl;
}
myfile.close();
outfile.close();
return 0;
}
2.将hello文件打开,将out文件打开,从hello中读取全部内容并从out当前内容的末尾开始写入。
#include <string>
#include <fstream>
using namespace std;
int main()
{
ifstream myfile("F:\\C++ project\\hello.txt");
ofstream outfile("F:\\C++ project\\out.txt", ios::app);
string temp;
if (!myfile.is_open())
{
cout << "未成功打开文件" << endl;
}
while(getline(myfile,temp))
{
outfile << temp;
outfile << endl;
}
myfile.close();
outfile.close();
return 0;
}
注意string类型数据的用法
以下是引用的原文1:
近期适配几款游戏的数据,因为重复量太大,因此写一个对数据进行处理的程序,下面是整个过程中接触到的一些东西。
以下内容转载自:https://www.cnblogs.com/1242118789lr/p/6885691.html。
fstream提供了三个类,用来实现c++对文件的操作(文件的创建、读、写)
ifstream -- 从已有的文件读入
ofstream -- 向文件写内容
fstream - 打开文件供读写
文件打开模式:
ios::in 只读
ios::out 只写
ios::app 从文件末尾开始写,防止丢失文件中原来就有的内容
ios::binary 二进制模式
ios::nocreate 打开一个文件时,如果文件不存在,不创建文件
ios::noreplace 打开一个文件时,如果文件不存在,创建该文件
ios::trunc 打开一个文件,然后清空内容
ios::ate 打开一个文件时,将位置移动到文件尾
文件指针位置在c++中的用法:
ios::beg 文件头
ios::end 文件尾
ios::cur 当前位置
举个例子:
file.seekg(0,ios::beg); //让文件指针定位到文件开头
file.seekg(0,ios::end); //让文件指针定位到文件末尾
file.seekg(10,ios::cur); //让文件指针从当前位置向文件末方向移动10个字节
file.seekg(-10,ios::cur); //让文件指针从当前位置向文件开始方向移动10个字节
file.seekg(10,ios::beg); //让文件指针定位到离文件开头10个字节的位置
注意:移动的单位是字节,而不是行。
常用的错误判断方法:
good() 如果文件打开成功
bad() 打开文件时发生错误
eof() 到达文件尾
#include <vector>
#include <string>
#include <fstream>
#include <iostream>
using namespace std;
int main()
{
ifstream myfile("G:\\C++ project\\Read\\hello.txt");
ofstream outfile("G:\\C++ project\\Read\\out.txt", ios::app);
string temp;
if (!myfile.is_open())
{
cout << "未成功打开文件" << endl;
}
while(getline(myfile,temp))
{
outfile << temp;
outfile << endl;
}
myfile.close();
outfile.close();
return 0;
}
其中getline的功能如下:
–从输入流中读入字符,存到string变量
–直到出现以下情况为止:
•读入了文件结束标志
•读到一个新行
•达到字符串的最大长度
–如果getline没有读入字符,将返回false,可用于判断文件是否结束
上述代码读取的是string类型的数据,那么对于int型数据该怎么办呢,其实道理差不多,下面举例说明:
#include<iostream>
#include <string>
#include <vector>
#include <fstream> //文件流库函数
using namespace std;
int cost[10][10];
int main()
{
int v, w, weight;
ifstream infile; //输入流
ofstream outfile; //输出流
infile.open(“G:\C++ project\Read\data.txt”, ios::in);
if(!infile.is_open ())
cout << “Open file failure” << endl;
while (!infile.eof()) // 若未到文件结束一直循环
{
infile >> v >> w >> weight;
cost[v][w] = weight;
cost[w][v] = weight;
}
infile.close(); //关闭文件
outfile.open(“G:\C++ project\Read\result.txt”, ios::app); //每次写都定位的文件结尾,不会丢失原来的内容,用out则会丢失原来的内容
if(!outfile.is_open ())
cout << “Open file failure” << endl;
for (int i = 0; i != 10; ++i)
{
for (int j = 0; j != 10; ++j)
{
outfile << i << “\t” << j << “\t” << cost[i][j] << endl; //在result.txt中写入结果
}
}
outfile.close();
return 0;
while (1);
}
上述代码的功能是读取data.txt文件的数据,注意,此时要求data.txt文件中的数据是三个一行,每个数据用空格隔开,之所以这样做,是因为在许多项目中,比如某为的算法比赛中,根据图的数据构建图的邻接矩阵或者邻接表时,数据都是这样安排的,在上面的代码中v和w代表顶点标号,weight代表边<v,w>的权值,上述代码的功能就是构建由data.txt文件描述的图的邻近矩阵。
data.txt文件的数据如下:
程序运行后,result.txt文件的内容如下:
因为数据太长只给出一部分
事实上,要求data.txt文件中的数据都是如此排列的要求有点高,如果data.txt文件中有的行有两个数据,有的行有三个数据,有的行有4个数据,上述方法就行不通了,其实改一下上面的代码就可以了,重要的是你要明白什么时候读取的那一行有几个数据,下面举例说明:
假设data.txt文件中的数据如下:
每行的数据都不一样多
第一行的数据表示,每行有三个数据的有5行,且在前面,每行有两个的数据的有两行,在后面,除第一行外,后面的才是正真的数据,因此读取这些数据的代码如下:
- #include<iostream>
- #include <string>
- #include <vector>
- #include <fstream> //文件流库函数
-
- using namespace std;
-
- int cost[10][10];
-
- int main()
- {
- int Num_3,Num_2;
- int v, w, weight;
- ifstream infile; //输入流
- ofstream outfile; //输出流
-
- infile.open(“G:\C++ project\Read\data.txt”, ios::in);
- if(!infile.is_open ())
- cout << “Open file failure” << endl;
-
- infile >> Num_3 >>Num_2 ; //先读取第一行
-
- while (0 != Num_3 ) // 读取3个数据的
- {
- infile >> v >> w >> weight;
- cost[v][w] = weight;
- cost[w][v] = weight;
- Num_3–;
- }
-
- while (0 != Num_2) // 读取3个数据的
- {
- infile >> v >> w;
- cost[v][w] = 100;
- cost[w][v] = 100;
- Num_2–;
- }
-
- infile.close(); //关闭文件
-
- outfile.open(“G:\C++ project\Read\result.txt”, ios::out); //每次写都定位的文件结尾,不会丢失原来的内容,用out则会丢失原来的内容
- if(!outfile.is_open ())
- cout << “Open file failure” << endl;
- for (int i = 0; i != 10; ++i)
- {
- for (int j = 0; j != 10; ++j)
- {
- outfile << i << “\t” << j << “\t” << cost[i][j] << endl; //在result.txt中写入结果
- }
-
- }
- outfile.close();
-
- return 0;
- while (1);
- }
为方便检验,我们把那些没有指明权值的边(即对应data.txt文件中那些每行只有两个数据的)的权值设为100,上述代码执行结果如下:
结果显示,读取正确。
注意:上面的代码之所以运行成立,是因为每行之间空几行好像并没有影响,比如上面的data.txt中,第一行与第二行之间空1行或者2行都没有关系。
二、string流
string头文件定义了三个类型来支持内存IO,istringstream向string读取数据,ostringstream从string写数据,stringstream既可从string读取数据也可向string写数据,就像string是一个IO流一样。
1、istringstream的用法,例子如下:
- #include <string>
- #include <sstream> //使用istringstream所需要的头文件
- #include <iostream>
- using namespace std;
- int main()
- {
- string str = “Hello word! I am Lee”;
- istringstream is(str); //将is绑定到str
- string s;
- while (is >> s)
- {
- cout << s << endl;
- }
- return 0;
- }
上述代码运行结果如下:
这相当于把一个句子拆分成单词,联系到前文提到的从文件中读取string的方法,如果读取到的string对象为一个句子,包含很多单词,那么我们就可以运用这种方法把string对象拆分开来。
2、ostringstream的用法
ostringstream同样是由一个string对象构造而来,ostringstream类向一个string插入字符。
- #include <string>
- #include <sstream> //使用ostringstream所需要的头文件
- #include <iostream>
- using namespace std;
- int main()
- {
- ostringstream ostr;
- // ostr.str(“abc”);//如果构造的时候设置了字符串参数,那么增长操作的时候不会从结尾开始增加,而是修改原有数据,超出的部分增长
- ostr.put(‘d’);
- ostr.put(‘e’);
- ostr<<“fg”;
- string gstr = ostr.str();
- cout<<gstr << endl;
- return 0;
- }
运行结果如下:
以下是引用的原文2
string类
声明
string类本不是STL的容器,但是它与STL容器有着很多相似的操作,因此,把string放在这里一起进行介绍。
之所以抛弃char*的字符串而选用C++标准程序库中的string类,是因为他和前者比较起来,不必担心内存是否足够、字符串长度等等,而且作为一个类出现,他集成的操作函数足以完成我们大多数情况下的需要。我们尽可以把它看成是C++的基本数据类型。
首先,为了在我们的程序中使用string类型,我们必须包含头文件。如下:
#include <string> // 注意这里不是string.h,string.h是C字符串头文件
1、声明一个C++字符串
声明一个字符串变量很简单: string str;
这样我们就声明了一个字符串变量,但既然是一个类,就有构造函数和析构函数。上面的声明没有传入参数,所以就直接使用了string的默认的构造函数,这个函数所作的就是把str初始化为一个空字符串。string类的构造函数和析构函数如下:
- 1) string s; // 生成一个空字符串s
- 2) string s(str) ; // 拷贝构造函数生成str的复制品
- 3) string s(str, stridx); // 将字符串str内"始于位置stridx"的部分当作字符串的初值
- 4) string s(str, stridx, strlen) ; // 将字符串str内"始于stridx且长度顶多strlen"的部分作为字符串的初值
- 5) string s(cstr) ; // 将C字符串(以NULL结束)作为s的初值
- 6) string s(chars, chars_len) ; // 将C字符串前chars_len个字符作为字符串s的初值。
- 7) string s(num, ‘c’) ; // 生成一个字符串,包含num个c字符
- 8) string s(“value”); string s=“value”; // 将s初始化为一个字符串字面值副本
- 9) string s(begin, end); // 以区间begin/end(不包含end)内的字符作为字符串s的初值
- 10) s.~string(); //销毁所有字符,释放内存
2、string与C字符数组的比较
string串要取得其中某一个字符,和传统的C字符串一样,可以用s[i]的方式取得。比较不一样的是如果s有三个字符,传统C的字符串的s[3]是’\0’字符,但是C++的string则是只到s[2]这个字符而已。
1、C风格字符串
- 用”“括起来的字符串常量,C++中的字符串常量由编译器在末尾添加一个空字符;
- 末尾添加了‘\0’的字符数组,C风格字符串的末尾必须有一个’\0’。
2、C字符数组及其与string串的区别
- char ch[ ]={‘C’, ‘+’, ‘+’}; //末尾无NULL
- char ch[ ]={‘C’, ‘+’, ‘+’, ‘\0’}; //末尾显式添加NULL
- char ch[ ]=”C++”; //末尾自动添加NULL字符 若[ ]内数字大于实际字符数,将实际字符存入数组,其余位置全部为’\0’。
3、string对象的操作
- string s;
- 1) s.empty(); // s为空串 返回true
- 2) s.size(); // 返回s中字符个数 类型应为:string::size_type
- 3) s[n]; // 从0开始相当于下标访问
- 4) s1+s2; // 把s1和s2连接成新串 返回新串
- 5) s1=s2; // 把s1替换为s2的副本
- 6) v1==v2; // 比较,相等返回true
- 7) `!=, <, <=, >, >=` 惯有操作 任何一个大写字母都小于任意的小写字母
当进行string对象和字符串字面值混合连接操作时,+操作符的左右操作数必须至少有一个是string类型的:
- string s1(“hello”);
- string s3=s1+”world”; //合法操作
- string s4=”hello”+”world”; //非法操作:两个字符串字面值相加
4、字符串操作函数
1、string类函数
1) =, s.assign() // 赋以新值
2) swap() // 交换两个字符串的内容
3) +=, s.append(), s.push_back() // 在尾部添加字符
4) s.insert() // 插入字符
5) s.erase() // 删除字符
6) s.clear() // 删除全部字符
7) s.replace() // 替换字符
8) + // 串联字符串
9) ==,!=,<,<=,>,>=,compare() // 比较字符串
10) size(),length() // 返回字符数量
11) max_size() // 返回字符的可能最大个数
12) s.empty() // 判断字符串是否为空
13) s.capacity() // 返回重新分配之前的字符容量
14) reserve() // 保留一定量内存以容纳一定数量的字符
15) [ ], at() // 存取单一字符
16) >>,getline() // 从stream读取某值
17) << // 将谋值写入stream
18) copy() // 将某值赋值为一个C_string
19) c_str() // 返回一个指向正规C字符串(C_string)的指针 内容与本string串相同 有’\0’
20) data() // 将内容以字符数组形式返回 无’\0’
21) s.substr() // 返回某个子字符串
22) begin() end() // 提供类似STL的迭代器支持
23) rbegin() rend() // 逆向迭代器
24) get_allocator() // 返回配置器
2、函数说明
1、s.assign();
- s.assign(str); // 不说
- s.assign(str,1,3); // 如果str是"iamangel" 就是把"ama"赋给字符串
- s.assign(str,2,string::npos); // 把字符串str从索引值2开始到结尾赋给s
- s.assign("gaint"); // 不说
- s.assign("nico",5); // 把’n’ ‘I’ ‘c’ ‘o’ ‘\0’赋给字符串
- s.assign(5,'x'); // 把五个x赋给字符串
2、大小和容量函数
一个C++字符串存在三种大小:
1) 现有的字符数,函数是s.size()和s.length(),他们等效。s.empty()用来检查字符串是否为空。
2) max_size(); 这个大小是指当前C++字符串最多能包含的字符数,很可能和机器本身的限制或者字符串所在位置连续内存的大小有关系。
3) capacity()重新分配内存之前string所能包含的最大字符数。
这里另一个需要指出的是reserve()函数,这个函数为string重新分配内存。重新分配的大小由其参数决定,默认参数为0,这时候会对string进行非强制性缩减。
3、元素存取
我们可以使用下标操作符[]和函数at()对元素包含的字符进行访问。但是应该注意的是操作符[]并不检查索引是否有效(有效索引0~str.length()),如果索引失效,会引起未定义的行为。而at()会检查,如果使用at()的时候索引无效,会抛出out_of_range异常。
有一个例外不得不说,const string a;的操作符[]对索引值是a.length()仍然有效,其返回值是’\0’。其他的各种情况,a.length()索引都是无效的。
4、比较函数
C ++字符串支持常见的比较操作符(>,>=,<,<=,==,!=),甚至支持string与C-string的比较(如 str<”hello”)。在使用>,>=,<,<=这些操作符的时候是根据”当前字符特性”将字符按字典顺序进行逐一的比较。字典排序靠前的字符小,比较的顺序是从前向后比较,遇到不相等的字符就按这个位置上的两个字符的比较结果确定两个字符串的大小。
另一个功能强大的比较函数是成员函数compare()。他支持多参数处理,支持用索引值和长度定位子串来进行比较。他返回一个整数来表示比较结果,返回值意义如下:0-相等 、>0-大于、<0-小于。
5、插入字符
也许你需要在string中间的某个位置插入字符串,这时候你可以用insert()函数,这个函数需要你指定一个安插位置的索引,被插入的字符串将放在这个索引的后面。
s.insert(0,”my name”);
s.insert(1,str);
这种形式的insert()函数不支持传入单个字符,这时的单个字符必须写成字符串形式。为了插入单个字符,insert()函数提供了两个对插入单个字符操作的重载函数:
insert(size_type index, size_type num, chart c)和insert(iterator pos, size_type num, chart c)。
其中size_type是无符号整数,iterator是char*,所以,你这么调用insert函数是不行的:
insert(0, 1, ‘j’);这时候第一个参数将转换成哪一个呢?
所以你必须这么写:insert((string::size_type)0, 1, ‘j’)!
第二种形式指出了使用迭代器安插字符的形式。
6、提取子串s.substr()
- s.substr(); // 返回s的全部内容
- s.substr(11); // 从索引11往后的子串
- s.substr(5,6); // 从索引5开始6个字符
5、字符串流stringstream操作
Iostream标准库支持内存中的输入输出,只要将流与存储在程序内存中的string对象捆绑起来即可。此时,可使用iostream输入和输出操作符读写这个stream对象。使用stringstream,我们必须包含头文件#include。
1、string s
- 1) >>操作符 // 用于从istream对象中读入输入
- 2) is >> s; // 从输入流is中读取一个以空白字符分割的字符串,写入s
- 3) <<操作符 // 用于把输出写到ostream对象中
- 4) os << s; // 将s写到输出流os中
- 5) getline(is, s); // 从输入流is中读取一行字符,写入s,直到遇到分行符或到了文件尾
- 6) istream // 输入流 提供输入操作
- 7) ostream // 输出流 提供输出操作
2、stringstream特定的操作
- 1) stringstream strm; // 创建自由的stringstream对象
- 2) stringstream strm(s); // 创建存储s的副本的stringstream对象,s是stringstream类型
- 3) strm.str(); // 返回strm中存储的string类型对象
- 4) strm.str(s); // 将string类型的s复制给strm 返回void
3、string到int的转换
stringstream通常是用来做数据转换的,如果你打算在多次转换中使用同一个stringstream对象,记住在每次转换前要使用clear()方法。在多次转换中重复使用同一个stringstream(而不是每次都创建一个新的对象)对象最大的好处在于效率。stringstream对象的构造和析构函数通常是非常耗费CPU时间的。
string到int的转换(与其他类型间的转换一样大同小异):
- string result=”10000”;
- int n=0;
- stream<<result;
- stream>>n; // n等于10000
6、C字符串、string串、stringstream之间的关系
首先必须了解,string可以被看成是以字符为元素的一种容器。字符构成序列(字符串)。有时候在字符序列中进行遍历,标准的string类提供了STL容器接口。具有一些成员函数比如begin()、end(),迭代器可以根据他们进行定位。注意,与char*不同的是,string不一定以NULL(‘\0’)结束。string长度可以根据length()得到,string可以根据下标访问。所以,不能将string直接赋值给char*。
1、string转换成const char *
如果要将字面值string直接转换成const char *类型。string有2个函数可以运用:一个是.c_str(),一个是data成员函数。
c_str()函数返回一个指向正规C字符串的指针,内容与本string串相同。这是为了与C语言兼容,在C语言中没有string类型,故必须通过string类对象的成员函数c_str()把string 对象转换成C中的字符串样式。注意:一定要使用strcpy()函数等来操作方法c_str()返回的指针
- string str = "Hello World";
- const char *ch1 = str.c_str();
- const char *ch2 = str.data();
此时,ch1与ch2的内容将都是”Hello World”。但是只能转换成const char*,如果去掉const编译不能通过。
2、string转换成char *
C++提供的由C++字符串得到对应的C_string的方法是使用data()、c_str()和copy(),其中
1) data()以字符数组的形式返回字符串内容,但并不添加’\0’。
2) c_str()返回一个以’\0’结尾的字符数组,返回值是const char*。
3) copy()则把字符串的内容复制或写入既有的c_string或字符数组内。
C++字符串并不以’\0’结尾。我的建议是在程序中能使用C++字符串就使用,除非万不得已不选用c_string。
如果要转换成char*,可以用string的一个成员函数strcpy实现。
- string str = "Hello World";
- int len = str.length();
- char *data = new char[len+1]; //这里+1还是不+1需要注意
- strcpy(data, str.c_str()); // const char *data = new char[len+1]; strcpy(data, str);
此时,data中的内容为”Hello World”使用c_str()要么str赋给一个const指针,要么用strcpy()复制。
3、char *转换成string
string类型能够自动将C风格的字符串转换成string对象:
- string str;
- const char *pc = "Hello World";
- str = pc;
- printf(“%s\n”, str); //此处出现错误的输出
- cout<<str<<endl;
不过这个是会出现问题的。有一种情况我要说明一下。当我们定义了一个string类型之后,用printf(“%s”,str);输出是会出问题的。这是因为“%s”要求后面的对象的首地址。但是string不是这样的一个类型。所以肯定出错。
用cout输出是没有问题的,若一定要printf输出。那么可以这样:
printf("%s",str.c_str());
4、char[ ] 转换成string
这个与char*的情况相同,也可以直接赋值,但是也会出现上面的问题,需要同样的处理。
- 字符数组转化成string类型:
- char ch [] = "ABCDEFG";
- string str(ch); //也可string str = ch;
或者
- char ch [] = "ABCDEFG";
- string str;
- str = ch; //在原有基础上添加可以用str += ch;
5、string转换成char[ ]
string对象转换成C风格的字符串:
const char *str = s.c_str();
这是因为为了防止字符数组被程序直接处理c_str()返回了一个指向常量数组的指针。
由于我们知道string的长度可以根据length()函数得到,又可以根据下标直接访问,所以用一个循环就可以赋值了,这样的转换不可以直接赋值。
- string str = "Hello World";
- int len=str.length();
- char ch[255]={};
- for( int i=0;i<str.length();i++)
- ch[i] = str[i];
- ch[len+1] = '\0';
- printf("%s\n", ch);
- cout<<ch<<endl;
6、stringstream与string间的绑定
- stringstream strm;
- string s;
- strm<<s; // 将s写入到strm
- strm>>s; // 从strm读取串写入s
- strm.str(); // 返回strm中存储的string类型对象
- strm.str(s); // 将string类型的s复制给strm 返回void
- char* cstr; // 将C字符数组转换成流
- string str(cstr);
- stringstream ss(str);