C 프로그램에서 텍스트 파일을 문자로 "#include"[]
컴파일 타임에 전체 텍스트 파일을 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); }
stdout에 인쇄되는 작은 프로그램 얻기 "이것은 작은 텍스트 파일입니다"
지금은 hackish python 스크립트를 사용했지만 엉덩이가 추악하고 하나의 변수 이름으로 제한되어 있습니다. 다른 방법을 알려 주시겠습니까?
이것을 위해 (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로 처리하려고하면 새로운 원시 문자열 리터럴 덕분에 포함 된 텍스트 파일을 거의 변경하지 않고도 수행 할 수 있습니다 .
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)====="
두 가지 가능성이 있습니다.
- 컴파일러 / 링커 확장을 사용하여 파일을 이진 파일로 변환하고 이진 데이터의 시작과 끝을 가리키는 적절한 기호를 사용하십시오. 이 답변을 참조하십시오 : GNU ld linker script 이진 파일 포함 .
- 파일을 배열을 초기화 할 수있는 일련의 문자 상수로 변환하십시오. ""만 수행 할 수 없으며 여러 줄에 걸쳐있을 수 있습니다. 행 연속 문자 (
\
), 이스케이프"
문자 및 기타 문자가 필요합니다 . 바이트를 시퀀스로 변환하는 작은 프로그램을 작성하는 것이 더 쉽습니다'\xFF', '\xAB', ...., '\0'
(또는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
Ok, Daemin의 게시물 에서 영감을 얻은 다음 간단한 예제를 테스트했습니다.
데이터 :
"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;
}
따라서 작동하지만 따옴표로 묶인 데이터가 필요합니다.
나는 kayahr의 대답을 좋아한다. 그러나 입력 파일을 터치하지 않고 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"
;
내 xtr
유틸리티 가 필요 하지만으로 할 수 있습니다 bash script
. 이것은 내가 부르는 스크립트 bin2inc
입니다. 첫 번째 매개 변수는 결과의 이름입니다 char[] variable
. 두 번째 매개 변수는의 이름입니다 file
. 출력은 주어진 변수 이름으로 include file
파일 컨텐츠가 인코딩 된 (소문자 hex
)로 C 입니다 . 는 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입니다.
다음을 사용하여이 작업을 수행 할 수 있습니다 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
--
또는 test.py
유효한 Python 스크립트 인 파일에서 일부 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
문자열로 포함시키려는 다양한 다른 유형의 코드에 대해 유사한 트릭을 재생할 수 있어야합니다. 그것이 좋은 아이디어인지 아닌지 잘 모르겠습니다. 깔끔한 해킹이지만 실제 프로덕션 코드에서는 원하지 않을 것입니다. 주말 핵 프로젝트에 대해서는 괜찮을 수도 있습니다.
무엇 수도 작동하면 같은 일을 할 경우입니다 :
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 ++ 스타일 문자열이 있습니다. 파일 시작 부분에 많은 양의 텍스트가 없기 때문에 코드가 깔끔해집니다.
python3에서 xxd를 다시 구현하여 xxd의 모든 성가심을 수정했습니다.
- Const 정확성
- 문자열 길이 데이터 유형 : int → size_t
- 널 종료 (원할 경우)
- C 문자열 호환 가능 :
unsigned
어레이에서 드롭 합니다. - 더 작고 읽을 수있는 출력으로 작성했을 때 : 인쇄 가능한 ASCII는 그대로 출력됩니다. 다른 바이트는 16 진수로 인코딩됩니다.
다음은 스크립트 자체로 필터링되어 있으므로 스크립트의 기능을 확인할 수 있습니다.
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 __name__ == '__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);
}
컴파일 타임에 수행 할 수 있다고하더라도 (일반적으로 할 수 있다고 생각하지는 않지만) 텍스트는 파일 내용이 아닌 사전 처리 된 헤더 일 것입니다. 런타임에 파일에서 텍스트를로드하거나 불쾌한 cut-n-paste 작업을 수행해야합니다.
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는이 애플리케이션에 대한 번들링 옵션을 엽니 다.
컴파일러와 전 처리기만으로는 불가능하다고 생각합니다. 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"
텍스트를 프로그램에 링크하여 전역 변수로 사용하십시오! 다음은 예입니다. GL 셰이더를 런타임에 GPU 용으로 컴파일해야하므로이 파일을 사용하여 실행 파일 내에 Open GL 셰이더 파일을 포함하는 것을 고려하고 있습니다.
비슷한 문제가 있었으며 작은 파일의 경우 앞서 언급 한 Johannes Schaub의 솔루션이 저에게 매력처럼 작용했습니다.
그러나 조금 더 큰 파일의 경우 컴파일러의 문자 배열 제한에 문제가 발생했습니다. 따라서 필자는 파일 내용을 같은 크기의 청크 (및 패딩 0)의 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;
}
xh에서
"this is a "
"buncha text"
main.c에서
#include <stdio.h>
int main(void)
{
char *textFileContents =
#include "x.h"
;
printf("%s\n", textFileContents);
return 0
}
일을해야합니다.
참고 URL : https://stackoverflow.com/questions/410980/include-a-text-file-in-ac-program-as-a-char
'Programming' 카테고리의 다른 글
'썽크'란 무엇입니까? (0) | 2020.07.12 |
---|---|
첫 번째 인스턴스에서만 문자열 분리-Java (0) | 2020.07.12 |
마지막 100 줄 로그 검색 (0) | 2020.07.12 |
변수 이름이 문자열로 저장된 경우 변수 값을 얻는 방법은 무엇입니까? (0) | 2020.07.12 |
크롬은 "이 페이지가 추가 대화 상자를 만드는 것을 방지"의 작업을 취소합니다. (0) | 2020.07.12 |