如何在 cout 中定义我自己的特殊字符
how to define my own special character in cout
例如:
cout << " hello\n400";
将打印:
hello
400
另一个例子:
cout << " hello\r400";
将打印:
400ello
有一个选项可以定义我自己的特殊角色吗?
我想制作类似的东西:
cout << " hello\d400";
会给出:
hello
400
(/d 是我的特殊字符,我已经有了使标准输出光标向下一行的功能(cursorDown()),但我只是不知道如何定义每次都会出现的特殊字符书面将调用我的 cursorDown() 函数)
正如其他人所说,您无法让 cout 理解用户定义的字符,但是您可以做的是
- std::cout 是 std::ostream 类型的对象,它重载了 operator<<。您可以创建一个结构对象,该对象在使用类似于任何日志流的 ostream 将字符串打印到文件或控制台之前,为您的特殊字符和其他用户定义的字符解析字符串。
或
- 而不是调用
cout << "something\dsomething"
您可以调用方法 special_cout(std::string);
来解析用户定义字符的字符串并执行调用。
无法定义 "new" 个特殊字符。
但是您可以让流解释特定字符以具有新的含义(您可以定义)。您可以使用当地人来做到这一点。
一些注意事项:
字符串中的字符"xyza"
只是一种简单的字符串编码方式。转义字符是 C++ 允许您表示不可见但定义明确的字符的方式。看一下 ASCII table,您会发现 00 -> 31
(十进制)范围内的所有字符都有特殊含义(通常称为控制字符)。
看这里:http://www.asciitable.com/
您可以将任何字符放入字符串中,方法是使用转义序列指定其确切值;即在字符串中使用 \x0A
将 "New Line" 字符放入字符串中。
比较常用的"control characters"有shorthand个版本(由C++语言定义)。 '\n' => '\x0A'
但你不能添加新的特殊 shorthand 字符,因为这只是语言提供的便利(这就像大多数语言支持的传统)。
但是给定一个字符,你能在 IO 流中赋予它特殊的含义吗? 是。您需要为语言环境定义构面,然后将该语言环境应用于流。
注意:现在将局部变量应用于 std::cin
/std::out
时出现问题。如果流已被使用(以任何方式)应用本地可能会失败,并且 OS 可能会在您到达 main()
之前对流进行处理,从而将语言环境应用于 std::cin
/ std::cout
可能会失败(但您可以轻松地对文件和字符串流执行此操作)。
那么我们该怎么做。
让我们使用 "Vertical Tab" 作为我们想要改变其含义的字符。我选择它是因为它有一个快捷方式 \v
(所以它比 \x0B
更短)并且通常对终端没有意义。
让我们将含义定义为换行并缩进 3 个空格。
#include <locale>
#include <algorithm>
#include <iostream>
#include <fstream>
class IndentFacet: public std::codecvt<char,char,std::mbstate_t>
{
public:
explicit IndentFacet(size_t ref = 0): std::codecvt<char,char,std::mbstate_t>(ref) {}
typedef std::codecvt_base::result result;
typedef std::codecvt<char,char,std::mbstate_t> parent;
typedef parent::intern_type intern_type;
typedef parent::extern_type extern_type;
typedef parent::state_type state_type;
protected:
virtual result do_out(state_type& tabNeeded,
const intern_type* rStart, const intern_type* rEnd, const intern_type*& rNewStart,
extern_type* wStart, extern_type* wEnd, extern_type*& wNewStart) const
{
result res = std::codecvt_base::ok;
for(;(rStart < rEnd) && (wStart < wEnd);++rStart,++wStart)
{
if (*rStart == '\v')
{
if (wEnd - wStart < 4)
{
// We do not have enough space to convert the '\v`
// So stop converting and a subsequent call should do it.
res = std::codecvt_base::partial;
break;
}
// if we find the special character add a new line and three spaces
wStart[0] = '\n';
wStart[1] = ' ';
wStart[2] = ' ';
wStart[3] = ' ';
// Note we do +1 in the for() loop
wStart += 3;
}
else
{
// Otherwise just copy the character.
*wStart = *rStart;
}
}
// Update the read and write points.
rNewStart = rStart;
wNewStart = wStart;
// return the appropriate result.
return res;
}
// Override so the do_out() virtual function is called.
virtual bool do_always_noconv() const throw()
{
return false; // Sometime we add extra tabs
}
};
一些使用语言环境的代码。
int main()
{
std::ios::sync_with_stdio(false);
/* Imbue std::cout before it is used */
std::cout.imbue(std::locale(std::locale::classic(), new IndentFacet()));
// Notice the use of '\v' after the first lien
std::cout << "Line 1\vLine 2\nLine 3\n";
/* You must imbue a file stream before it is opened. */
std::ofstream data;
data.imbue(std::locale(std::locale::classic(), new IndentFacet()));
data.open("PLOP");
// Notice the use of '\v' after the first lien
data << "Loki\vUses Locale\nTo do something silly\n";
}
输出:
> ./a.out
Line 1
Line 2
Line 3
> cat PLOP
Loki
Uses Locale
To do something silly
但是
现在写这些真的不值得。如果您想要像这样的固定缩进,我们可以使用其中包含这些特定字符的命名变量。它会使您的代码稍微冗长一些,但可以解决问题。
#include <string>
#include <iostream>
std::string const newLineWithIndent = "\n ";
int main()
{
std::cout << " hello" << newLineWithIndent << "400";
}
例如:
cout << " hello\n400";
将打印:
hello
400
另一个例子:
cout << " hello\r400";
将打印:
400ello
有一个选项可以定义我自己的特殊角色吗? 我想制作类似的东西:
cout << " hello\d400";
会给出:
hello
400
(/d 是我的特殊字符,我已经有了使标准输出光标向下一行的功能(cursorDown()),但我只是不知道如何定义每次都会出现的特殊字符书面将调用我的 cursorDown() 函数)
正如其他人所说,您无法让 cout 理解用户定义的字符,但是您可以做的是
- std::cout 是 std::ostream 类型的对象,它重载了 operator<<。您可以创建一个结构对象,该对象在使用类似于任何日志流的 ostream 将字符串打印到文件或控制台之前,为您的特殊字符和其他用户定义的字符解析字符串。
或
- 而不是调用
cout << "something\dsomething"
您可以调用方法special_cout(std::string);
来解析用户定义字符的字符串并执行调用。
无法定义 "new" 个特殊字符。
但是您可以让流解释特定字符以具有新的含义(您可以定义)。您可以使用当地人来做到这一点。
一些注意事项:
字符串中的字符"xyza"
只是一种简单的字符串编码方式。转义字符是 C++ 允许您表示不可见但定义明确的字符的方式。看一下 ASCII table,您会发现 00 -> 31
(十进制)范围内的所有字符都有特殊含义(通常称为控制字符)。
看这里:http://www.asciitable.com/
您可以将任何字符放入字符串中,方法是使用转义序列指定其确切值;即在字符串中使用 \x0A
将 "New Line" 字符放入字符串中。
比较常用的"control characters"有shorthand个版本(由C++语言定义)。 '\n' => '\x0A'
但你不能添加新的特殊 shorthand 字符,因为这只是语言提供的便利(这就像大多数语言支持的传统)。
但是给定一个字符,你能在 IO 流中赋予它特殊的含义吗? 是。您需要为语言环境定义构面,然后将该语言环境应用于流。
注意:现在将局部变量应用于 std::cin
/std::out
时出现问题。如果流已被使用(以任何方式)应用本地可能会失败,并且 OS 可能会在您到达 main()
之前对流进行处理,从而将语言环境应用于 std::cin
/ std::cout
可能会失败(但您可以轻松地对文件和字符串流执行此操作)。
那么我们该怎么做。
让我们使用 "Vertical Tab" 作为我们想要改变其含义的字符。我选择它是因为它有一个快捷方式 \v
(所以它比 \x0B
更短)并且通常对终端没有意义。
让我们将含义定义为换行并缩进 3 个空格。
#include <locale>
#include <algorithm>
#include <iostream>
#include <fstream>
class IndentFacet: public std::codecvt<char,char,std::mbstate_t>
{
public:
explicit IndentFacet(size_t ref = 0): std::codecvt<char,char,std::mbstate_t>(ref) {}
typedef std::codecvt_base::result result;
typedef std::codecvt<char,char,std::mbstate_t> parent;
typedef parent::intern_type intern_type;
typedef parent::extern_type extern_type;
typedef parent::state_type state_type;
protected:
virtual result do_out(state_type& tabNeeded,
const intern_type* rStart, const intern_type* rEnd, const intern_type*& rNewStart,
extern_type* wStart, extern_type* wEnd, extern_type*& wNewStart) const
{
result res = std::codecvt_base::ok;
for(;(rStart < rEnd) && (wStart < wEnd);++rStart,++wStart)
{
if (*rStart == '\v')
{
if (wEnd - wStart < 4)
{
// We do not have enough space to convert the '\v`
// So stop converting and a subsequent call should do it.
res = std::codecvt_base::partial;
break;
}
// if we find the special character add a new line and three spaces
wStart[0] = '\n';
wStart[1] = ' ';
wStart[2] = ' ';
wStart[3] = ' ';
// Note we do +1 in the for() loop
wStart += 3;
}
else
{
// Otherwise just copy the character.
*wStart = *rStart;
}
}
// Update the read and write points.
rNewStart = rStart;
wNewStart = wStart;
// return the appropriate result.
return res;
}
// Override so the do_out() virtual function is called.
virtual bool do_always_noconv() const throw()
{
return false; // Sometime we add extra tabs
}
};
一些使用语言环境的代码。
int main()
{
std::ios::sync_with_stdio(false);
/* Imbue std::cout before it is used */
std::cout.imbue(std::locale(std::locale::classic(), new IndentFacet()));
// Notice the use of '\v' after the first lien
std::cout << "Line 1\vLine 2\nLine 3\n";
/* You must imbue a file stream before it is opened. */
std::ofstream data;
data.imbue(std::locale(std::locale::classic(), new IndentFacet()));
data.open("PLOP");
// Notice the use of '\v' after the first lien
data << "Loki\vUses Locale\nTo do something silly\n";
}
输出:
> ./a.out
Line 1
Line 2
Line 3
> cat PLOP
Loki
Uses Locale
To do something silly
但是
现在写这些真的不值得。如果您想要像这样的固定缩进,我们可以使用其中包含这些特定字符的命名变量。它会使您的代码稍微冗长一些,但可以解决问题。
#include <string>
#include <iostream>
std::string const newLineWithIndent = "\n ";
int main()
{
std::cout << " hello" << newLineWithIndent << "400";
}