C ++换行符中的ascii艺术生成器
ascii art generator in c++ newlining
我正在尝试生成给定字符串的 ASCII 艺术作品。
art.cpp
#pragma once
#include <string>
#include <vector>
#include "art.h"
std::string Art::display(std::string& text) {
std::string final_text;
final_text.reserve(text.length());
for (char letter: text) {
std::string single = letters[letter];
/* tried this */ single.erase(std::remove(single.begin(), single.end(), '\n'), single.end());
final_text += single;
}
return final_text;
}
art.h
#pragma once
#include <string>
#include <vector>
#include <map>
class Art {
public:
std::map<char, std::string> letters = {
std::make_pair('a', std::string(R"(
_
/ \
/ _ \
/ ___ \
/__/ \__\
)")),
std::make_pair('b', std::string(R"(
____
| __ )
| _ \
| |_) |
|____/
)")),
std::make_pair('c', std::string(R"(
____
/ ___|
| |
| |___
\____|
)")),
std::make_pair('d', std::string(R"(
____
| _ \
| | | |
| |_| |
|____/
)")),
std::make_pair('e', std::string(R"(
_____
| ____|
| _|
| |___
|_____|
)")),
std::make_pair('f', std::string("asdf")),
std::make_pair('g', std::string("asdf")),
std::make_pair('h', std::string(R"(
_ _
| | | |
| |_| |
| _ |
|_| |_|
)")),
std::make_pair('i', std::string("asdf")),
std::make_pair('j', std::string("asdf")),
std::make_pair('k', std::string(R"(
_ __
| |/ /
| ' /
| . \
|_|\_\
)")),
std::make_pair('l', std::string("asdf")),
std::make_pair('m', std::string("asdf")),
std::make_pair('n', std::string("asdf")),
std::make_pair('o', std::string(R"(
___
/ _ \
| | | |
| |_| |
\___/
)")),
std::make_pair('p', std::string("asdf")),
std::make_pair('q', std::string("asdf")),
std::make_pair('r', std::string(R"(
____
| _ \
| |_) |
| _ <
|_| \_\
)")),
std::make_pair('s', std::string("asdf")),
std::make_pair('t', std::string("asdf")),
std::make_pair('u', std::string("asdf")),
std::make_pair('v', std::string("asdf")),
std::make_pair('w', std::string("asdf")),
std::make_pair('x', std::string("asdf")),
std::make_pair('y', std::string(R"(
__ __
\ \ / /
\ V /
| |
|_|
)")),
std::make_pair('z', std::string("asdf"))
};
std::string display(std::string& text);
};
这是一个 str 引用,然后遍历每个字符并在映射中找到该字符并获取其对应的 ASCII 艺术字母,然后将其添加到最终字符串中。
这就提出了一个问题。当我想打印字符串 Art::display()
returns.
时,它会在新行上打印每个字符
我试过什么?我试过删除换行符,但那会弄乱一些零碎的东西 together/apart.
我想让它做什么?我希望它从左到右打印一个单词,而不是在新行上打印每个字符。
在控制台中,您通常是逐行编写。当您打印其中一个字符时,您将获取字符串,并且将打印带有 \n
的每一行。一行一行。
每个字符一个接一个地打印,这很正常,这就是输出的方式:逐行输出。
跳跃只是将几行附加到同一行。对于字符 a
,不是用 X 行来绘制字符,而是将它们全部合并为一行。这不是你想要的,但你正在朝着我认为正确的想法前进。
你需要做的是对所有需要的字符,逐行合并它们,并像你已经在做的那样逐行输出结果,但不是一次一个字符,而是合并所有需要的字符。
我正在添加一个“视觉”,希望能让事情变得更清楚。
目前你有
_\n
/ \n
/ _ \n
/ ___ \n
/__/ \__\n
____\n
| __ ) \n
| _ \ \n
| |_) |\n
|____/ \n
跳转是因为删除 \n
这样做(对于 a
):
_/ \/ _ \/ ___ \/__/ \__\
你想要的是
_ ____ \n
/ \ | __ ) \n
/ _ \ | _ \ \n
/ ___ \ | |_) | \n
/__/ \__\ |____/ \n
对于您的输出,保留一个包含 5 行的数组,用于 Art 字符的 5 行不同的行。追加艺术字时,换行拆分,将艺术字的第一行追加到输出的第一行,艺术字的第二行追加到输出的第二行,依此类推
与此同时,您可以使用 string_view
而不是 string
来制作地图,并通过使用 UDLs 和仅使用 { }
而不是 [=15] 来简化您的源代码=].
std::make_pair('a', std::string(R"(
_
/ \
/ _ \
/ ___ \
/__/ \__\
)")),
变为:
{'a', R"(
_
/ \
/ _ \
/ ___ \
/__/ \__\
)"sv },
我会添加一个 Letter
class 来简化解析和打印字母。
示例:
#include <iomanip>
#include <iostream>
#include <map>
#include <string_view>
#include <vector>
class Letter {
public:
// The constructor takes a string_view and parses it into lines that
// it stores in a vector<string_view>
Letter(std::string_view l) : width_(0) {
auto b = l.begin();
for(auto it = b; it != l.end(); ++it) {
if(*it == '\n') {
auto w = it - b;
if(w > width_) width_ = w;
svs.emplace_back(b, w);
b = it + 1;
}
}
auto w = l.end() - b;
if(w > width_) width_ = w;
svs.emplace_back(b, w);
}
// some convenience functions
unsigned width() const { return width_; }
unsigned height() const { return svs.size(); }
// An operator to get a specific line to render from this Letter.
// It returns a string with the proper width (the max width of all the
// lines for this Letter). If `line` is out of bounds, it returns a
// blank line.
std::string operator()(unsigned line) const {
if(line >= svs.size()) return std::string(width_, ' ');
return std::string(svs[line]) + std::string(width_ - svs[line].size(), ' ');
}
// If you just want to print one letter:
friend std::ostream& operator<<(std::ostream& os, const Letter& l) {
for(auto& sv : l.svs) os << sv << '\n';
return os;
}
private:
std::vector<std::string_view> svs;
unsigned width_;
};
// A user defined literal operator to simplify creating the map:
Letter operator "" _L(const char* str, std::size_t len) {
return std::string_view{str, len};
}
//---------------------------------------------------------------
std::map<char, Letter> letters = {
{' ', " "_L }, // space
{'a',
R"( _
/ \
/ _ \
/ ___ \
/__/ \__\
)"_L},
{'b',
R"( ____
| __ )
| _ \
| |_) |
|____/
)"_L}
}; // note that each Letter ends with _L to trigger the user defined literal
// A printing function that can either be called with a specified height,
// or be made to find the appropriate height for this string.
void print(std::string_view txt, unsigned height = 0) {
if(height == 0) {
for(char ch : txt) {
try {
auto w = letters.at(ch).height();
if(w > height) height = w;
}
catch(...) {} // don't mind non-existing letters
}
}
for(unsigned i = 0; i < height; ++i) {
for(char ch : txt) {
const Letter& l = letters.at(ch);
std::cout << l(i);
}
std::cout << '\n';
}
}
int main() {
print("abab baba");
}
输出:
_ ____ _ ____ ____ _ ____ _
/ \ | __ ) / \ | __ ) | __ ) / \ | __ ) / \
/ _ \ | _ \ / _ \ | _ \ | _ \ / _ \ | _ \ / _ \
/ ___ \ | |_) | / ___ \ | |_) | | |_) | / ___ \ | |_) | / ___ \
/__/ \__\|____/ /__/ \__\|____/ |____/ /__/ \__\|____/ /__/ \__\
注意:MSVC(包含在 Visual Studio 中的编译器)出于某种原因在我的 string_view
中出现问题。这是一个使用 std::string
s 来让 MSVC 满意的版本:
#include <iomanip>
#include <iostream>
#include <iterator>
#include <map>
#include <string>
#include <vector>
class Letter {
public:
// The constructor takes a string_view and parses it into lines that
// it stores in a vector<string_view>
Letter() = default;
Letter(const std::string& str) {
auto b = str.cbegin();
auto end = str.cend();
for(std::string::const_iterator it = b; it != end; ++it) {
if(*it == '\n') {
std::size_t w = std::distance(b, it);
if(w > width_) width_ = w;
svs.emplace_back(b, it);
b = it + 1;
}
}
std::size_t w = std::distance(b, str.cend());
if(w > width_) width_ = w;
svs.emplace_back(b, str.cend());
}
// some convenience functions
std::size_t width() const { return width_; }
std::size_t height() const { return svs.size(); }
// An operator to get a specific line to render from this Letter.
// It returns a string with the proper width (the max width of all the
// lines for this Letter). If `line` is out of bounds, it returns a
// blank line.
std::string operator()(std::size_t line) const {
if(line >= svs.size()) return std::string(width_, ' ');
return svs[line] + std::string(width_ - svs[line].size(), ' ');
}
// If you just want to print one letter:
friend std::ostream& operator<<(std::ostream& os, const Letter& l) {
for(auto& sv : l.svs) os << sv << '\n';
return os;
}
private:
std::vector<std::string> svs;
std::size_t width_ = 0;
};
// A user defined literal operator to simplify creating the map:
Letter operator "" _L(const char* str, std::size_t len) {
return std::string(str, str + len);
}
//---------------------------------------------------------------
std::map<char, Letter> letters = {
{' ', " "_L },
{'a',
R"( _
/ \
/ _ \
/ ___ \
/__/ \__\
)"_L},
{'b',
R"( ____
| __ )
| _ \
| |_) |
|____/
)"_L}
}; // note that each Letter ends with _L to trigger the user defined literal
// A printing function that can either be called with a specified height,
// or be made to find the appropriate height for this string.
void print(std::string_view txt, std::size_t height = 0) {
if(height == 0) {
for(char ch : txt) {
try {
auto w = letters.at(ch).height();
if(w > height) height = w;
}
catch(...) {} // don't mind non-existing letters
}
}
for(std::size_t i = 0; i < height; ++i) {
for(char ch : txt) {
const Letter& l = letters.at(ch);
std::cout << l(i);
}
std::cout << '\n';
}
}
int main() {
print("abab baba");
}
我正在尝试生成给定字符串的 ASCII 艺术作品。
art.cpp
#pragma once
#include <string>
#include <vector>
#include "art.h"
std::string Art::display(std::string& text) {
std::string final_text;
final_text.reserve(text.length());
for (char letter: text) {
std::string single = letters[letter];
/* tried this */ single.erase(std::remove(single.begin(), single.end(), '\n'), single.end());
final_text += single;
}
return final_text;
}
art.h
#pragma once
#include <string>
#include <vector>
#include <map>
class Art {
public:
std::map<char, std::string> letters = {
std::make_pair('a', std::string(R"(
_
/ \
/ _ \
/ ___ \
/__/ \__\
)")),
std::make_pair('b', std::string(R"(
____
| __ )
| _ \
| |_) |
|____/
)")),
std::make_pair('c', std::string(R"(
____
/ ___|
| |
| |___
\____|
)")),
std::make_pair('d', std::string(R"(
____
| _ \
| | | |
| |_| |
|____/
)")),
std::make_pair('e', std::string(R"(
_____
| ____|
| _|
| |___
|_____|
)")),
std::make_pair('f', std::string("asdf")),
std::make_pair('g', std::string("asdf")),
std::make_pair('h', std::string(R"(
_ _
| | | |
| |_| |
| _ |
|_| |_|
)")),
std::make_pair('i', std::string("asdf")),
std::make_pair('j', std::string("asdf")),
std::make_pair('k', std::string(R"(
_ __
| |/ /
| ' /
| . \
|_|\_\
)")),
std::make_pair('l', std::string("asdf")),
std::make_pair('m', std::string("asdf")),
std::make_pair('n', std::string("asdf")),
std::make_pair('o', std::string(R"(
___
/ _ \
| | | |
| |_| |
\___/
)")),
std::make_pair('p', std::string("asdf")),
std::make_pair('q', std::string("asdf")),
std::make_pair('r', std::string(R"(
____
| _ \
| |_) |
| _ <
|_| \_\
)")),
std::make_pair('s', std::string("asdf")),
std::make_pair('t', std::string("asdf")),
std::make_pair('u', std::string("asdf")),
std::make_pair('v', std::string("asdf")),
std::make_pair('w', std::string("asdf")),
std::make_pair('x', std::string("asdf")),
std::make_pair('y', std::string(R"(
__ __
\ \ / /
\ V /
| |
|_|
)")),
std::make_pair('z', std::string("asdf"))
};
std::string display(std::string& text);
};
这是一个 str 引用,然后遍历每个字符并在映射中找到该字符并获取其对应的 ASCII 艺术字母,然后将其添加到最终字符串中。
这就提出了一个问题。当我想打印字符串 Art::display()
returns.
时,它会在新行上打印每个字符
我试过什么?我试过删除换行符,但那会弄乱一些零碎的东西 together/apart.
我想让它做什么?我希望它从左到右打印一个单词,而不是在新行上打印每个字符。
在控制台中,您通常是逐行编写。当您打印其中一个字符时,您将获取字符串,并且将打印带有 \n
的每一行。一行一行。
每个字符一个接一个地打印,这很正常,这就是输出的方式:逐行输出。
跳跃只是将几行附加到同一行。对于字符 a
,不是用 X 行来绘制字符,而是将它们全部合并为一行。这不是你想要的,但你正在朝着我认为正确的想法前进。
你需要做的是对所有需要的字符,逐行合并它们,并像你已经在做的那样逐行输出结果,但不是一次一个字符,而是合并所有需要的字符。
我正在添加一个“视觉”,希望能让事情变得更清楚。
目前你有
_\n
/ \n
/ _ \n
/ ___ \n
/__/ \__\n
____\n
| __ ) \n
| _ \ \n
| |_) |\n
|____/ \n
跳转是因为删除 \n
这样做(对于 a
):
_/ \/ _ \/ ___ \/__/ \__\
你想要的是
_ ____ \n
/ \ | __ ) \n
/ _ \ | _ \ \n
/ ___ \ | |_) | \n
/__/ \__\ |____/ \n
对于您的输出,保留一个包含 5 行的数组,用于 Art 字符的 5 行不同的行。追加艺术字时,换行拆分,将艺术字的第一行追加到输出的第一行,艺术字的第二行追加到输出的第二行,依此类推
与此同时,您可以使用 string_view
而不是 string
来制作地图,并通过使用 UDLs 和仅使用 { }
而不是 [=15] 来简化您的源代码=].
std::make_pair('a', std::string(R"(
_
/ \
/ _ \
/ ___ \
/__/ \__\
)")),
变为:
{'a', R"(
_
/ \
/ _ \
/ ___ \
/__/ \__\
)"sv },
我会添加一个 Letter
class 来简化解析和打印字母。
示例:
#include <iomanip>
#include <iostream>
#include <map>
#include <string_view>
#include <vector>
class Letter {
public:
// The constructor takes a string_view and parses it into lines that
// it stores in a vector<string_view>
Letter(std::string_view l) : width_(0) {
auto b = l.begin();
for(auto it = b; it != l.end(); ++it) {
if(*it == '\n') {
auto w = it - b;
if(w > width_) width_ = w;
svs.emplace_back(b, w);
b = it + 1;
}
}
auto w = l.end() - b;
if(w > width_) width_ = w;
svs.emplace_back(b, w);
}
// some convenience functions
unsigned width() const { return width_; }
unsigned height() const { return svs.size(); }
// An operator to get a specific line to render from this Letter.
// It returns a string with the proper width (the max width of all the
// lines for this Letter). If `line` is out of bounds, it returns a
// blank line.
std::string operator()(unsigned line) const {
if(line >= svs.size()) return std::string(width_, ' ');
return std::string(svs[line]) + std::string(width_ - svs[line].size(), ' ');
}
// If you just want to print one letter:
friend std::ostream& operator<<(std::ostream& os, const Letter& l) {
for(auto& sv : l.svs) os << sv << '\n';
return os;
}
private:
std::vector<std::string_view> svs;
unsigned width_;
};
// A user defined literal operator to simplify creating the map:
Letter operator "" _L(const char* str, std::size_t len) {
return std::string_view{str, len};
}
//---------------------------------------------------------------
std::map<char, Letter> letters = {
{' ', " "_L }, // space
{'a',
R"( _
/ \
/ _ \
/ ___ \
/__/ \__\
)"_L},
{'b',
R"( ____
| __ )
| _ \
| |_) |
|____/
)"_L}
}; // note that each Letter ends with _L to trigger the user defined literal
// A printing function that can either be called with a specified height,
// or be made to find the appropriate height for this string.
void print(std::string_view txt, unsigned height = 0) {
if(height == 0) {
for(char ch : txt) {
try {
auto w = letters.at(ch).height();
if(w > height) height = w;
}
catch(...) {} // don't mind non-existing letters
}
}
for(unsigned i = 0; i < height; ++i) {
for(char ch : txt) {
const Letter& l = letters.at(ch);
std::cout << l(i);
}
std::cout << '\n';
}
}
int main() {
print("abab baba");
}
输出:
_ ____ _ ____ ____ _ ____ _
/ \ | __ ) / \ | __ ) | __ ) / \ | __ ) / \
/ _ \ | _ \ / _ \ | _ \ | _ \ / _ \ | _ \ / _ \
/ ___ \ | |_) | / ___ \ | |_) | | |_) | / ___ \ | |_) | / ___ \
/__/ \__\|____/ /__/ \__\|____/ |____/ /__/ \__\|____/ /__/ \__\
注意:MSVC(包含在 Visual Studio 中的编译器)出于某种原因在我的 string_view
中出现问题。这是一个使用 std::string
s 来让 MSVC 满意的版本:
#include <iomanip>
#include <iostream>
#include <iterator>
#include <map>
#include <string>
#include <vector>
class Letter {
public:
// The constructor takes a string_view and parses it into lines that
// it stores in a vector<string_view>
Letter() = default;
Letter(const std::string& str) {
auto b = str.cbegin();
auto end = str.cend();
for(std::string::const_iterator it = b; it != end; ++it) {
if(*it == '\n') {
std::size_t w = std::distance(b, it);
if(w > width_) width_ = w;
svs.emplace_back(b, it);
b = it + 1;
}
}
std::size_t w = std::distance(b, str.cend());
if(w > width_) width_ = w;
svs.emplace_back(b, str.cend());
}
// some convenience functions
std::size_t width() const { return width_; }
std::size_t height() const { return svs.size(); }
// An operator to get a specific line to render from this Letter.
// It returns a string with the proper width (the max width of all the
// lines for this Letter). If `line` is out of bounds, it returns a
// blank line.
std::string operator()(std::size_t line) const {
if(line >= svs.size()) return std::string(width_, ' ');
return svs[line] + std::string(width_ - svs[line].size(), ' ');
}
// If you just want to print one letter:
friend std::ostream& operator<<(std::ostream& os, const Letter& l) {
for(auto& sv : l.svs) os << sv << '\n';
return os;
}
private:
std::vector<std::string> svs;
std::size_t width_ = 0;
};
// A user defined literal operator to simplify creating the map:
Letter operator "" _L(const char* str, std::size_t len) {
return std::string(str, str + len);
}
//---------------------------------------------------------------
std::map<char, Letter> letters = {
{' ', " "_L },
{'a',
R"( _
/ \
/ _ \
/ ___ \
/__/ \__\
)"_L},
{'b',
R"( ____
| __ )
| _ \
| |_) |
|____/
)"_L}
}; // note that each Letter ends with _L to trigger the user defined literal
// A printing function that can either be called with a specified height,
// or be made to find the appropriate height for this string.
void print(std::string_view txt, std::size_t height = 0) {
if(height == 0) {
for(char ch : txt) {
try {
auto w = letters.at(ch).height();
if(w > height) height = w;
}
catch(...) {} // don't mind non-existing letters
}
}
for(std::size_t i = 0; i < height; ++i) {
for(char ch : txt) {
const Letter& l = letters.at(ch);
std::cout << l(i);
}
std::cout << '\n';
}
}
int main() {
print("abab baba");
}