C/C++. Quine. Куайны или квайны

Что такое куайн или квайн программирование?
Куайн, квайн (англ. quine) — компьютерная программа (частный случай метапрограммирования), которая выдаёт на выходе точную копию своего исходного текста.
На днях столкнулся с подобной задачей: реализовать на С++ куайн или квайн, но с одним но - вывести текст не на стандартное устройство вывода (например, консоль), а во внешний текстовый файлик (например, output.txt).
Пришлось немного поломать голову. С одной стороны - просто, а с другой начинаешь себя спрашивать - с какого угла подойти? Немного помогла статья с "хабры" - "Как писать квайны":
Введение 
Многие программисты считают написание квайнов (программ, выводящих свой исходный код) непосильной задачей. И действительно — все эти цепные квайны и квайны различного порядка, при взгляде на которые можно потеряться в казалось бы бессмысленном наборе символов…
Однако на самом деле написать квайн на каком-либо языке не так сложно, как кажется. Сейчас я расскажу, как сделать это на различных языках программирования. Более того, мы не будем использовать «хаки» интерпретеруемых языков вроде операции вывода исходного кода и функций типа eval, а также напишем квайны на интерпретируемых и компилируемых языках.
Теория
Попробуем написать квайн. Для этого возьмём инструкцию языка для вывода и передадим ей как параметр код программы. Однако в коде мы снова используем этот же код и так далее — возникает бесконечная рекурсия. Но что можно сделать для того, чтобы не передавать строковую константу? Решение — поместить строку (копию части кода) в переменную. Для удобства назовём такую строку s-строкой, а переменную с этой строкой — s-переменной. Чтобы и в s-переменной не было рекурсии, мы просто исключим из неё фрагмент со значением этой самой переменной. То есть, выглядеть это будет примерно так:

C:

char s[]="char s[]=;";

Примечание. Как в этом фрагменте, так и в квайнах, которые получатся в итоге, для простоты мы не будем соблюдать правила форматирования кода. Тем не менее, их соблюдение вы сможете без труда добавить самостоятельно после прочтения статьи.

Далее, при выводе, мы подставим значение s-строки в её же определение в коде (в примере выше — перед тремя последними символами). Здесь же возникает ещё несколько проблем. Первая проблема — при подставлении в s-строке нельзя использовать символы, которые поведут себя в коде не так, как надо. Например, мы не можем так просто вставить кавычку — ведь вместо того, чтобы стать частью s-строки, она завершит её определение и выводимый код не будет совпадать с исходным, являясь некорректным вообще.


Экранирование применить здесь достаточно сложно — символ экранирования надо тоже экранировать и т.д.. Гораздо проще, например, использовать другой вариант кавычек — так, во многих интерпретируемых языках разрешено использование как одинарных, так и двойных кавычек для задания строки, а отличие состоит в том, что можно без проблем использовать одну кавычку в константе, если она ограничивается другими. То есть, код
'"' создаст односимвольную строку с двойной кавычкой, а код "'" — с одиночной. Если использовать этот вариант, удобно задать в начале переменную с какой-либо кавычкой, а затем использовать её при выводе.

Но и этот вариант не универсален: в Си, к примеру, есть лишь один вариант кавычек. Тогда можно использовать другой способ — задавать кавычку кодом символа, печатая символ с таким кодом при выводе.


Следующая проблема — вставка другой строки (или символа с каким-либо кодом) в вывод s-строки. Решение здесь очевидно — брать подстроку s-строки специальной функцией, выводить её, далее выводить то, что надо вставить, затем выводить другую подстроку s-строки. Может показаться, что в Си взятие подстроки для вывода потребует немало кода. Тут нам на помощь придёт мощь функции printf. Так, например, вот варианты кода для различных языков, печатающего часть строки s со второго символа (считая с единицы) по четвёртый включительно:


Python:

print(s[1:4])

Ruby:

print s[1..3]

Perl:

print substr(s,1,2)

C:

printf("%.2s",s+1);

Обычно методы взятия подстроки могут также брать её остаток до конца. Например, напечатаем строку s со второго символа до конца строки (то есть, всю строку кроме первого символа):

Python:

print(s[1:])

Ruby:

print s[1..-1]

Perl:

print substr(s,1)

C:

printf("%s",s+1);

Если такой возможности нет, придётся на место параметра с длиной подстроки поставить заглушку типа «XX», а затем в конце посчитать символы до конца и подставить их вместо «XX» в коде и в s-строке, не изменяя длины различных частей кода. Например, если в длине окажется одна цифра, целесообразно подставить вместо первого икса пробел, ведь если его удалить, длины частей s-строки изменятся и их придётся пересчитывать.

Интерпретируемые языки
Итак, начнём писать квайны, собрав все суждения выше. На Python я написал такой квайн (работает и на 3.x):
q="'";s='q="";s=;print(s[:3]+q+s[3:7]+q+s+q+s[7:])';print(s[:3]+q+s[3:7]+q+s+q+s[7:])

Здесь переменная q используется как переменная, где хранится одинарная кавычка, далее идёт определение s-переменной со всем кодом, кроме самой s-строки. После этого идёт вывод s-переменной со следующими вставками:
1). Одинарная кавычка как значение переменной q;
2). Одинарная кавычка как начало определения s-строки;
3). Сама s-строка (да-да, s-строка вставляется внутри s-строки);
4). Одинарная кавычка как конец определения s-строки.
Примечание. При написании квайнов по данному методу не забывайте копировать все изменения в коде в копию кода в s-строке.

С минимальными изменениями можно получить квайн только для
Python 2.x:
q="'";s='q="";s=;print s[:3]+q+s[3:7]+q+s+q+s[7:]';print s[:3]+q+s[3:7]+q+s+q+s[7:]

Абсолютно аналогичны и квайны на других языках, где мы изменяем лишь некоторые синтаксические особенности:

Ruby:

q="'";s='q="";s=;print s[0..2]+q+s[3..6]+q+s+q+s[7..-1]';print s[0..2]+q+s[3..6]+q+s+q+s[7..-1]

Perl:

$q="'";$s='$q="";$s=;print substr($s,0,4).$q.substr($s,4,5).$q.$s.$q.substr($s,9)'; 
print substr($s,0,4).$q.substr($s,4,5).$q.$s.$q.substr($s,9)

PHP:

<?$q="'";$s='<?$q="";$s=;print substr($s,0,6).$q.substr($s,6,5).$q.$s.$q.substr($s,11);' 
;print substr($s,0,6).$q.substr($s,6,5).$q.$s.$q.substr($s,11);

Компилируемые языки.
Написание квайна на C оказалось чуть более трудной задачей. Здесь я активно использовал коды символов: двойной кавычки — 34, и перевода строки — 13 (он понадобился, чтобы отделить директиву компилятора для включения stdio.h), а также интересный способ взятия подстроки с помощью printf, уже описанный выше.

А вот и сам квайн:


#include <stdio.h>
int main(){const char *s="#include <stdio.h>int main(){const char *s=;
printf(%.18s%c%.25s%c%s%c%.8s%c%.33s%c%s,s,10,s+18,34,s,34,s+43,34,s+51,34,s+84);
return 0;}";printf("%.18s%c%.25s%c%s%c%.8s%c%.33s%c%s",s,10,s+18,34,s,34,s+43,34,s+51,34,s+84); 
return 0;}

Заключение.
Вот и всё. Я написал квайны на большинстве языков, интерпретаторы и компиляторы которых обнаружил на своём компьютере. Думаю, теперь вы и сами напишете подобную программу на своём любимом языке программирования, если я не упомянул его здесь. В качестве упражнения вы также можете написать квайн на таких языках, как Java, C#, Haskell или Pascal. Не бойтесь трудностей — достаточно попробовать, и всё получится! 

На мой взгляд, главное в решении такого рода задачи проявить внимание к деталям и усидчивость, так как легко допустить просчет. 


Ниже приведен листинг решения поставленной задачи.  Скачать можно по ссылке - Download.


#include <stdio.h>
void main(){FILE *f=fopen("output.txt","w");const char *s="#include <stdio.h>void main(){FILE *f=fopen(output.txt,w);const char *s=;fprintf(f,%.18s%c%.26s%c%.10s%c%.1s%c%.1s%c%.16s%c%s%c%.11s%c%.60s%c%s,s,10,s+18,34,s+44,34,s+54,34,s+55,34,s+56,34,s,34,s+72,34,s+83,34,s+143);fclose(f);}";fprintf(f,"%.18s%c%.26s%c%.10s%c%.1s%c%.1s%c%.16s%c%s%c%.11s%c%.60s%c%s",s,10,s+18,34,s+44,34,s+54,34,s+55,34,s+56,34,s,34,s+72,34,s+83,34,s+143);fclose(f);}