コンパイル時にテキストファイル全体を文字列としてCプログラムに含める方法はありますか?
何かのようなもの:
file.txt:
This is
a little
text file
main.c:
#include <stdio.h>
int main(void) {
#blackmagicinclude("file.txt", content)
/*
equiv: char[] content = "This is\na little\ntext file";
*/
printf("%s", content);
}
標準出力に印刷する小さなプログラムを取得する「これは小さなテキストファイルです」
現時点では、ハックのあるpythonスクリプトを使用しましたが、お粗末で、変数名は1つだけに制限されています。別の方法を教えてください。
これには(unix util) xxd を使用することをお勧めします。次のように使用できます
$ echo hello world > a
$ xxd -i a
出力:
unsigned char a[] = {
0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x0a
};
unsigned int a_len = 12;
質問はCについてでしたが、誰かがC++ 11でそれを行おうとすると、新しい raw string literals のおかげで、含まれているテキストファイルを少し変更するだけでそれを行うことができます。
C++でこれを行います:
const char *s =
#include "test.txt"
;
テキストファイルでこれを行います。
R"(Line 1
Line 2
Line 3
Line 4
Line 5
Line 6)"
そのため、ファイルの先頭には接頭辞、末尾には接尾辞のみが必要です。その間、あなたはあなたが望むことをすることができます、あなたが文字シーケンス)"
を必要としない限り、特別なエスケープは必要ありません。ただし、独自のカスタム区切り文字を指定すると、これでも機能します。
R"=====(Line 1
Line 2
Line 3
Now you can use "( and )" in the text file, too.
Line 5
Line 6)====="
次の2つの可能性があります。
\
)、エスケープ"
文字などが必要です。バイトを'\xFF', '\xAB', ...., '\0'
のようなシーケンスに変換する小さなプログラムを書く方が簡単です(または、可能であれば、別の回答で説明されているUNIXツールxxd
を使用してください!):コード:
#include <stdio.h>
int main() {
int c;
while((c = fgetc(stdin)) != EOF) {
printf("'\\x%X',", (unsigned)c);
}
printf("'\\0'"); // put terminating zero
}
(未検証)。それから:
char my_file[] = {
#include "data.h"
};
Data.hが生成される場所
cat file.bin | ./bin2c > data.h
Daemin's に触発されて、次の簡単な例をテストしました:
a.data:
"this is test\n file\n"
test.c:
int main(void)
{
char *test =
#include "a.data"
;
return 0;
}
gcc -E test.cの出力:
# 1 "test.c"
# 1 "<built-in>"
# 1 "<command line>"
# 1 "test.c"
int main(void)
{
char *test =
# 1 "a.data" 1
"this is test\n file\n"
# 6 "test.c" 2
;
return 0;
}
したがって、動作しますが、引用符で囲まれたデータが必要です。
私はカヤールの答えが好きです。 入力ファイルに触れたくない場合ただし、CMakeを使用している場合は、ファイルに区切り文字シーケンスを追加できます。たとえば、次のCMakeコードは入力ファイルをコピーし、それに応じてコンテンツをラップします。
function(make_includable input_file output_file)
file(READ ${input_file} content)
set(delim "for_c++_include")
set(content "R\"${delim}(\n${content})${delim}\"")
file(WRITE ${output_file} "${content}")
endfunction(make_includable)
# Use like
make_includable(external/shaders/cool.frag generated/cool.frag)
次に、次のようにC++に含めます。
constexpr char *test =
#include "generated/cool.frag"
;
objcopy
を使用してこれを行うことができます:
objcopy --input binary --output elf64-x86-64 myfile.txt myfile.o
これで、myfile.txt
。
汚いトリックに頼るつもりなら、生の文字列リテラルと#include
特定の種類のファイル。
たとえば、プロジェクトにSQLiteのいくつかのSQLスクリプトを含め、構文の強調表示を取得したいが、特別なビルドインフラストラクチャは必要ないとします。このファイルを持つことができますtest.sql
これはSQLiteの有効なSQLです。ここで--
はコメントを開始します:
--x, R"(--
SELECT * from TestTable
WHERE field = 5
--)"
そして、私のC++コードには次のものがあります:
int main()
{
auto x = 0;
const char* mysql = (
#include "test.sql"
);
cout << mysql << endl;
}
出力は次のとおりです。
--
SELECT * from TestTable
WHERE field = 5
--
または、Pythonファイルからのコードtest.py
これは有効なPythonスクリプト(#
はPythonおよびpass
はノーオペレーション)でコメントを開始します:
#define pass R"(
pass
def myfunc():
print("Some Python code")
myfunc()
#undef pass
#define pass )"
pass
そして、C++コードで:
int main()
{
const char* mypython = (
#include "test.py"
);
cout << mypython << endl;
}
出力されるもの:
pass
def myfunc():
print("Some Python code")
myfunc()
#undef pass
#define pass
文字列として含めることができる他のさまざまなタイプのコードに対して、同様のトリックを再生することができるはずです。それが良いアイデアであるかどうかはわかりません。これは一種のきちんとしたハックですが、おそらく実際の製品コードには必要ないでしょう。ただし、週末のハックプロジェクトには問題ないかもしれません。
mightとは、次のようなことをすることです。
int main()
{
const char* text = "
#include "file.txt"
";
printf("%s", text);
return 0;
}
もちろん、実際にファイルにあるものに注意してください、二重引用符がないこと、すべての適切な文字がエスケープされていることなどを確認する必要があります.
したがって、実行時にファイルからテキストをロードする、またはコードに直接テキストを埋め込む方が簡単かもしれません。
それでも別のファイルにテキストが必要な場合は、そこにテキストを含めることができますが、文字列として表す必要があります。上記のコードを使用しますが、二重引用符は使用しません。例えば:
file.txt
"Something evil\n"\
"this way comes!"
main.cpp
int main()
{
const char* text =
#include "file.txt"
;
printf("%s", text);
return 0;
}
基本的に、インクルードするテキストファイルにCまたはC++スタイルの文字列が含まれています。ファイルの先頭にこのような大量のテキストがないため、コードがすっきりします。
私のxtr
ユーティリティが必要ですが、bash script
でそれを行うことができます。これは私がbin2inc
と呼ぶスクリプトです。最初のパラメーターは、結果のchar[] variable
の名前です。 2番目のパラメーターは、file
の名前です。出力は、指定された変数名としてエンコードされたファイルコンテンツ(小文字のhex
)を持つC include file
です。 char array
はzero terminated
で、データの長さは$variableName_length
に保存されます
#!/bin/bash
fileSize ()
{
[ -e "$1" ] && {
set -- `ls -l "$1"`;
echo $5;
}
}
echo unsigned char $1'[] = {'
./xtr -fhex -p 0x -s ', ' < "$2";
echo '0x00'
echo '};';
echo '';
echo unsigned long int ${1}_length = $(fileSize "$2")';'
ここでXTRを取得できます xtr(文字eXTRapolator)はGPLV3です
Python3でxxdを再実装し、xxdの厄介な問題をすべて修正しました。
unsigned
を配列にドロップします。スクリプトは次のとおりです。それ自体でフィルタリングされているため、スクリプトの機能を確認できます。
pyxxd.c
#include <stddef.h>
extern const char pyxxd[];
extern const size_t pyxxd_len;
const char pyxxd[] =
"#!/usr/bin/env python3\n"
"\n"
"import sys\n"
"import re\n"
"\n"
"def is_printable_ascii(byte):\n"
" return byte >= ord(' ') and byte <= ord('~')\n"
"\n"
"def needs_escaping(byte):\n"
" return byte == ord('\\\"') or byte == ord('\\\\')\n"
"\n"
"def stringify_nibble(nibble):\n"
" if nibble < 10:\n"
" return chr(nibble + ord('0'))\n"
" return chr(nibble - 10 + ord('a'))\n"
"\n"
"def write_byte(of, byte):\n"
" if is_printable_ascii(byte):\n"
" if needs_escaping(byte):\n"
" of.write('\\\\')\n"
" of.write(chr(byte))\n"
" Elif byte == ord('\\n'):\n"
" of.write('\\\\n\"\\n\"')\n"
" else:\n"
" of.write('\\\\x')\n"
" of.write(stringify_nibble(byte >> 4))\n"
" of.write(stringify_nibble(byte & 0xf))\n"
"\n"
"def mk_valid_identifier(s):\n"
" s = re.sub('^[^_a-z]', '_', s)\n"
" s = re.sub('[^_a-z0-9]', '_', s)\n"
" return s\n"
"\n"
"def main():\n"
" # `xxd -i` compatibility\n"
" if len(sys.argv) != 4 or sys.argv[1] != \"-i\":\n"
" print(\"Usage: xxd -i infile outfile\")\n"
" exit(2)\n"
"\n"
" with open(sys.argv[2], \"rb\") as infile:\n"
" with open(sys.argv[3], \"w\") as outfile:\n"
"\n"
" identifier = mk_valid_identifier(sys.argv[2]);\n"
" outfile.write('#include <stddef.h>\\n\\n');\n"
" outfile.write('extern const char {}[];\\n'.format(identifier));\n"
" outfile.write('extern const size_t {}_len;\\n\\n'.format(identifier));\n"
" outfile.write('const char {}[] =\\n\"'.format(identifier));\n"
"\n"
" while True:\n"
" byte = infile.read(1)\n"
" if byte == b\"\":\n"
" break\n"
" write_byte(outfile, ord(byte))\n"
"\n"
" outfile.write('\";\\n\\n');\n"
" outfile.write('const size_t {}_len = sizeof({}) - 1;\\n'.format(identifier, identifier));\n"
"\n"
"if __== '__main__':\n"
" main()\n"
"";
const size_t pyxxd_len = sizeof(pyxxd) - 1;
使用法(これによりスクリプトが抽出されます):
#include <stdio.h>
extern const char pyxxd[];
extern const size_t pyxxd_len;
int main()
{
fwrite(pyxxd, 1, pyxxd_len, stdout);
}
x.hで
"this is a "
"buncha text"
main.c内
#include <stdio.h>
int main(void)
{
char *textFileContents =
#include "x.h"
;
printf("%s\n", textFileContents);
return 0
}
仕事をするべきです。
コンパイル時に実行できたとしても(一般に実行できるとは思わない)、テキストはファイルの内容ではなく、前処理されたヘッダーである可能性があります。実行時にファイルからテキストを読み込むか、厄介なカットアンドペーストジョブを実行する必要があると思います。
コンパイラとプリプロセッサだけでは不可能だと思います。 gccはこれを許可します:
#define _STRGF(x) # x
#define STRGF(x) _STRGF(x)
printk ( MODULE_NAME " built " __DATE__ " at " __TIME__ " on Host "
STRGF(
# define hostname my_dear_hostname
hostname
)
"\n" );
しかし、残念ながらこれはそうではありません。
#define _STRGF(x) # x
#define STRGF(x) _STRGF(x)
printk ( MODULE_NAME " built " __DATE__ " at " __TIME__ " on Host "
STRGF(
# include "/etc/hostname"
)
"\n" );
エラーは次のとおりです。
/etc/hostname: In function ‘init_module’:
/etc/hostname:1:0: error: unterminated argument list invoking macro "STRGF"
Xxd -iオプションを使用したHasturkunの回答は優れています。変換プロセス(テキスト-> 16進数インクルードファイル)をビルドに直接組み込みたい場合、hexdump.cツール/ライブラリは最近、xxdの-iオプションに似た機能を追加しました(完全なヘッダーを提供しません-必要ですchar配列定義を提供します-ただし、char配列の名前を選択できるという利点があります)。
http://25thandclement.com/~william/projects/hexdump.c.html
ライセンスはxxdよりもはるかに「標準」であり、非常にリベラルです。これを使用してプログラムにinitファイルを埋め込む例は、CMakeLists.txtおよびscheme.cファイルにあります。
https://github.com/starseeker/tinyscheme-cmake
生成されたファイルをソースツリーに含めることとユーティリティをバンドルすることには長所と短所があります。その処理方法は、プロジェクトの具体的な目標とニーズによって異なります。 hexdump.cは、このアプリケーションのバンドルオプションを開きます。
私も同様の問題を抱えていました。小さなファイルの場合、前述のヨハネスシャウブのソリューションは私にとって魅力的なものでした。
ただし、少し大きいファイルの場合、コンパイラの文字配列制限の問題に遭遇しました。そのため、ファイルコンテンツを同じサイズのチャンク(および場合によってはゼロを埋め込む)の2D文字配列に変換する小さなエンコーダアプリケーションを作成しました。次のような2D配列データを含む出力テキストファイルを生成します。
const char main_js_file_data[8][4]= {
{'\x69','\x73','\x20','\0'},
{'\x69','\x73','\x20','\0'},
{'\x61','\x20','\x74','\0'},
{'\x65','\x73','\x74','\0'},
{'\x20','\x66','\x6f','\0'},
{'\x72','\x20','\x79','\0'},
{'\x6f','\x75','\xd','\0'},
{'\xa','\0','\0','\0'}};
4は、実際にはエンコーダーの変数MAX_CHARS_PER_ARRAYです。たとえば、「main_js_file_data.h」などと呼ばれる、結果のCコードを含むファイルは、次のようにC++アプリケーションに簡単にインライン化できます。
#include "main_js_file_data.h"
エンコーダのソースコードは次のとおりです。
#include <fstream>
#include <iterator>
#include <vector>
#include <algorithm>
#define MAX_CHARS_PER_ARRAY 2048
int main(int argc, char * argv[])
{
// three parameters: input filename, output filename, variable name
if (argc < 4)
{
return 1;
}
// buffer data, packaged into chunks
std::vector<char> bufferedData;
// open input file, in binary mode
{
std::ifstream fStr(argv[1], std::ios::binary);
if (!fStr.is_open())
{
return 1;
}
bufferedData.assign(std::istreambuf_iterator<char>(fStr),
std::istreambuf_iterator<char>() );
}
// write output text file, containing a variable declaration,
// which will be a fixed-size two-dimensional plain array
{
std::ofstream fStr(argv[2]);
if (!fStr.is_open())
{
return 1;
}
const std::size_t numChunks = std::size_t(std::ceil(double(bufferedData.size()) / (MAX_CHARS_PER_ARRAY - 1)));
fStr << "const char " << argv[3] << "[" << numChunks << "]" <<
"[" << MAX_CHARS_PER_ARRAY << "]= {" << std::endl;
std::size_t count = 0;
fStr << std::hex;
while (count < bufferedData.size())
{
std::size_t n = 0;
fStr << "{";
for (; n < MAX_CHARS_PER_ARRAY - 1 && count < bufferedData.size(); ++n)
{
fStr << "'\\x" << int(unsigned char(bufferedData[count++])) << "',";
}
// fill missing part to reach fixed chunk size with zero entries
for (std::size_t j = 0; j < (MAX_CHARS_PER_ARRAY - 1) - n; ++j)
{
fStr << "'\\0',";
}
fStr << "'\\0'}";
if (count < bufferedData.size())
{
fStr << ",\n";
}
}
fStr << "};\n";
}
return 0;
}
テキストをプログラムにリンクして、グローバル変数として使用してみませんか? ここに例があります。 GLシェーダーはGPU向けにコンパイルする必要があるため、これを使用してOpen GLシェーダーファイルを実行可能ファイルに含めることを検討しています。ランタイム。