字符串与流处理
2018-03-26 本文已影响17人
downdemo
文件读取
#include <fstream>
ifstream fin;
ofstream fout;
fin.open(filename);
if (fin.is_open())
while (!fin.eof()) fin >> ...
fin.close();
fout.open(filename, ios_base::out); // 覆盖
if (fout.is_open())
fout << ...
fout.close();
void open (const char* filename,
ios_base::openmode mode = ios_base::in | ios_base::out);
ios_base::in 读文件,若不存在此文件不创建,设置ios_base::badbit
ios_base::out 覆盖写文件,若不存在则创建
ios_base::in | ios_base::out 读写,不存在不创建,设置ios_base::badbit
ios_base::binary 二进制方式读写,不存在不创建
ios_base::ate 打开文件定义到末尾(at end),不存在不创建
ios_base::app 从文件尾追加写,不存在则创建
ios_base::trunc 打开文件,已存在则清空,不存在不创建
- 把文件中的内容读取到map中
map<string, A> a; // A是一个类
...
while(!fin.eof())
{
A temp;
fin >> temp; // 在A中重载了>>
a.insert(make_pair(a.get(),a); // A::get()返回一个string成员
}
- 把map的内容导出到一个文件中
...
if(fout.is_open())
{
map<string, string>::iterator it;
for (it = a.begin(); it != a.end(); ++it)
fout << "key: " << it->first
<< "value: " << it->second << endl;
}
- 一次性读取所有内容
#include <sstream>
std::cout << fin.rdbuf(); // 打印所有内容
std::stringstream buf;
buf << fin.rdbuf();
std::string alldata(buf.str());
字符串类型转换
- 使用itoa
#include <stdlib.h>
char* itoa(int value, char*string, int radix); // radix为转换后的进制
// 例子
#include <stdlib.h>
#include <stdio.h>
int main()
{
int number = 123456;
char string[25];
itoa(number, string, 10);
printf("integer = %d string = %s\n", number, string);
return 0;
}
- MFC下用Format转成CString
UINT i = 10;
CString str;
str.Format(_T("%d"), i);
- linux下没有itoa,可以使用sprintf
#include <stdio.h>
int sprintf ( char * str, const char * format, ... );
// 返回值为字符串长度(即strlen,不包含末尾'\0')
// 例子
#include <stdlib.h>
#include <stdio.h>
int main()
{
char buffer[50];
int n, a = 3, b = 4;
n = sprintf(buffer, "%d plus %d is %d", a, b, a+b);
printf("%s is a string %d chars long\n", buffer, n);
return 0;
}
// 打印结果
5 plus 3 is 8 is a string 13 chars long
// 连接字符串
char buffer[25]
char* str1 = "hello ";
char* str2 = "world!";
sprintf(buffer, "%s%s", str1, str2);
// 连接数组
char buffer[25];
int arr1[5], arr2[5];
for(int i = 0; i < 5; ++i)
{
arr1[i] = i;
arr2[i] = i;
}
sprintf(buffer, "%.5s%.5s", arr1, arr2);
// 或sprintf(buffer, %.*s, %.*s, sizeof(arr1), arr1, sizeof(arr2), arr2);
- 在VS中,用sprintf_s如果出现报错
error C2664: “int sprintf_s(char *const ,const size_t,const char *const ,...)”: 无法将参数 2 从“const char [9]”转换为“const size_t”
- 则需要在第二个参数指出容器大小
sprintf_s(buf, 50, "%d%d", n1, n2);
- 如果string想转int的话,使用C++11的stoi即可
int i = std::stoi(str);
- QString、std::string、const char*的互转
std::string str = qstr.toStdString();
QString qstr1 = QString::fromLocal8Bit(str.c_str()))
QString qstr2 = QString::fromStdString(str);
<cstring>库函数
char* strcpy( char* dest, const char* src );
char *strncpy( char *dest, const char *src, std::size_t count );
char *strcat( char *dest, const char *src );
char *strncat( char *dest, const char *src, std::size_t count );
std::size_t strlen( const char* str );
int strcmp( const char *lhs, const char *rhs );
void* memset( void* dest, int ch, std::size_t count );
// 常用于清空数组memset(buf, 0, sizeof(buf))
void* memcpy( void* dest, const void* src, std::size_t count );
格式调整
- setw
#include <sstream>
#include <iostream>
#include <iomanip>
int main() {
std::cout << "no setw:" << 42 << '\n'
<< "setw(6):" << std::setw(6) << 42 << '\n'
<< "setw(6), several elements: " << 89
<< std::setw(6) << 12 << 34 << '\n';
std::istringstream is("hello, world");
char arr[10]; is >> std::setw(6) >> arr;
std::cout << "Input from \"" << is.str() << "\" with setw(6) gave \""
<< arr << "\"\n";
}
// output
no setw:42
setw(6): 42
setw(6), several elements: 89 1234
Input from "hello, world" with setw(6) gave "hello"
- width()和left/right/internal
// modify adjustfield using manipulators
#include <iostream> // std::cout, std::internal, std::left, std::right
int main () {
int n = -77;
std::cout.width(6); std::cout << std::internal << n << '\n';
std::cout.width(6); std::cout << std::left << n << '\n';
std::cout.width(6); std::cout << std::right << n << '\n';
return 0;
}
// output
- 77
-77
-77
- setprecision()
#include <iostream> // std::cout, std::fixed
#include <iomanip> // std::setprecision
int main () {
double f =3.14159;
std::cout << std::setprecision(5) << f << '\n';
std::cout << std::setprecision(9) << f << '\n';
std::cout << std::fixed;
std::cout << std::setprecision(5) << f << '\n';
std::cout << std::setprecision(9) << f << '\n';
return 0;
}
// output
3.1416
3.14159
3.14159
3.141590000
- fixed
#include <iostream> // std::cout, std::fixed, std::scientific
int main () {
double a = 3.1415926534;
double b = 2006.0;
double c = 1.0e-10;
std::cout.precision(5);
std::cout << "default:\n";
std::cout << a << '\n' << b << '\n' << c << '\n';
std::cout << '\n';
std::cout << "fixed:\n" << std::fixed;
std::cout << a << '\n' << b << '\n' << c << '\n';
std::cout << '\n';
std::cout << "scientific:\n" << std::scientific;
std::cout << a << '\n' << b << '\n' << c << '\n';
return 0;
}
// output
default:
3.1416
2006
1e-010
fixed:
3.14159
2006.00000
0.00000
scientific:
3.14159e+000
2.00600e+003
1.00000e-010
输出字符对齐
- 通常情况下使用setw和left就可实现左对齐
void print()
{
std::cout << std::left << std::setw(20) << ID
<< std::left << std::setw(20) << name
<< std::fixed << std::setprecision(2) << "¥" << price;
}
- 但如果有中文字符串就会出现无法对齐的情况,这时候增加制表符\t即可,使用printf比cout更方便
// printf不能直接打印string类型,要用string类的.c_str()转化
void print()
{
printf("%-20s\t%-20s\t¥%-20.2f",ID.c_str(), name.c_str(), price);
}
- printf的对齐方式说明
%-20s // -为左对齐(不加-为右对齐),占20位长,不足补空格
%020s // 右对齐20位,不足补0
%.2f // 带两位小数
隐藏输入的字符串
- 使用conio.h中的_getch(),功能是输入字符但不显示
#include <conio.h>
void inputPassword(string &str, int size) {
char c;
int count = 0;
char *password = new char[size];
while ((c = _getch()) != '\r')
{
if (c == 8) // 退格
{
if (count == 0) continue;
putchar('\b'); // 回退一格
putchar(' '); // 输出一个空格将原来的*隐藏
putchar('\b'); // 再回退一格等待输入
--count;
}
if (count == size - 1)// 最大长度为size-1
{
continue;
}
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'))
{
putchar('*'); // 接收到一个字符后, 打印一个*
password[count] = c;
count++;
}
}
password[count] = '\0';
str = password;
delete[] password;
cout << endl;
}
- 但是conio.h不是C标准库中的头文件,linux的编译器一般不包含此头文件。linux下可以利用tcgetattr()和tcsetattr()实现getch(),tcgetattr和tcsetattr分别用于获取和设置终端参数
#include <termio.h>
int getch(void)
{
struct termios tm, tm_old;
int fd = 0, ch;
if (tcgetattr(fd, &tm) < 0) {//保存现在的终端设置
return -1;
}
tm_old = tm;
cfmakeraw(&tm);//更改终端设置为原始模式,该模式下所有的输入数据以字节为单位被处理
if (tcsetattr(fd, TCSANOW, &tm) < 0) {//设置上更改之后的设置
return -1;
}
ch = getchar();
if (tcsetattr(fd, TCSANOW, &tm_old) < 0) {//更改设置为最初的样子
return -1;
}
return ch;
}
- 也可以利用linux的stty -echo命令,作用是不显示内容
#include <iostream>
#include <cstdio> // for getchar
#include <cstdlib> // for system
using namespace std;
int main()
{
char ch;
cout << "input char:";
system("stty -echo");
ch = getchar();
system("stty echo");
cout << ch << endl;
}
清除输入流缓存
- 对于下面的代码,每次的string输入都会被跳过
#include <iostream>
#include <string>
using namespace std;
int main()
{
while (true)
{
string s;
int n;
cout << "input number:";
cin >> n;
cout << "input string:";
getline(cin, s);
}
}
运行结果
- 通常这种做法的设计场景是,输入一个string类型的ID如学号,再输入一个整数如成绩,进入循环,上一次循环的cin接到下一次的getline则会出现问题。由于输入第一组数据是正常的,第二次循环开始才会出现上述情况,所以碰到此问题没有经验很难找到原因
#include <iostream>
#include <string>
using namespace std;
int main()
{
while (true)
{
string s;
int n;
cout << "input string:";
getline(cin, s);
cout << "input number:";
cin >> n;
}
}
运行结果
- 原因是cin把回车的换行符留在了输入流缓存中,getline会读取换行符,认为是空行。解决方案是养成在每次cin之后使用get()的习惯
#include <iostream>
#include <string>
using namespace std;
int main()
{
while (true)
{
string s;
int n;
cout << "input string:";
getline(cin, s);
cout << "input number:";
(cin >> n).get();
}
}
- 或者在每次getline之前用cin.clear()和cin.sync()清空缓存区
#include <iostream>
#include <string>
using namespace std;
int main()
{
while (true)
{
string s;
int n;
cin.clear();
cin.sync();
cout << "input string:";
getline(cin, s);
cout << "input number:";
cin >> n;
}
}
- linux用cin.clear()和cin.sync()清空发现无效,改用setbuf(stdin,NULL)
#include <iostream>
#include <string>
using namespace std;
int main()
{
while (true)
{
string s;
int n;
setbuf(stdin,NULL);
cout << "input string:";
getline(cin, s);
cout << "input number:";
cin >> n;
}
}
打印时间
- 相关结构体和函数
#include <sys/time.h>
struct timeval
{
long tv_sec; // 秒unix时间戳
long tv_usec; // 微妙
};
struct timezone
{
int tz_minuteswest; // 时间差 = 格林尼治时间 - 北京时间 = -480
int tz_dsttime; // 时间的修正方式,即日光节约时间
}
typedef long time_t;
struct tm
{
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon; // 0代表一月,取值为[0,11]
int tm_year; // 年份,其值从1900开始
int tm_wday; // 从星期天开始,取值为[0,6]
int tm_yday; // 从1月1日开始,取值为[0,365]
int tm_isdst; // 夏令时标识符,实行时为正,不实行为0,不了解情况时为负
long inttm_gmtoff; // 指定了日期变更线东面时区中UTC东部时区正秒数或UTC西部时区的负秒数
const char* tm_zone; // 当前时区的名字(与环境变量TZ相关)
};
time_t time(time_t* t); // 返回并使t指向当前时间,若t为nullptr则只返回unix时间戳
int gettimeofday(struct timeval*tv, struct timezone *tz); // tv保存时间,tz保存时区,可设为空
struct tm* localtime(const time_t* clock); // 将unix时间戳转为本地时间
struct tm* gmtime(const time_t *time); // 将unix时间戳转为格林威治时间
struct tm* localtime_r( const time_t* timer, struct tm* result ); // 多线程中替代localtime的函数
struct tm* gmtime_r( const time_t* timer, struct tm* result ); // 多线程中替代gmtime的函数
char *ctime(const time_t* time); // 时间转为字符串
char *asctime(const struct tm* tblock); // 时间转为字符串
- 例子
#include <iostream>
#include <string>
#include <sys/time.h>
std::string time_now()
{
char t_now[20];
struct timeval t;
gettimeofday(&t, NULL);
strftime(t_now, sizeof(t_now)-1, "%Y%m%d%H%M%S", localtime(&t.tv_sec));
return t_now+2; // 略去年份前两位
}
int main()
{
std::cout << time_now();
}