유니티 엔진에서 json 사용하기.



우선 유니티를 실행.



Asset Store를 열고



검색어로 json을 입력하여 검색




JSON .NET for Unity 를 찾아서 클릭




에셋을 다운로드 하고



불러오기를 눌러서 프로젝트에 추가



프로젝트에 추가가 완료되면

JsonDotNet이라는 폴더가 생성되어 있을 것이다.



간단한 스크립트를 만들어서 테스트 해보면 된다.

스크린샷은 여기까지만 사용하고 아래에 나올 코드를 참고하도록 하자.

해당 json 라이브러리에 대한 자세한 설명은 newtonsoft 공식 홈페이지를 참조 할 것.

간단한 설명은 에셋을 받으면서 생긴 "JsonDotNet" 폴더 안에 Documentation 폴더에 pdf 파일 참조.

주로 사용할 법한 내용만 요약하는게 이 글의 목적이다.




using System.IO;
using System.Collections.Generic;

using UnityEngine;

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

public class JsonSaveLoad : MonoBehaviour
{
    public void Save()
    {
        //  key-value 사용
        JObject savedata = new JObject();       //  JObject 인스턴스 생성
        savedata["key-name"] = "value-data";    //  key-value 삽입
        savedata["anyname"] = 1f;               //  int, float, string
        savedata["is-save"] = true;             //  bool 등 다양한 자료형 사용 가능

        //  json에서 배열 사용하기
        JArray arraydata = new JArray();        //  JArray 인스턴스 생성
        for (int i = 0; i < 5; i++)
        {
            //  랜덤한 값을 추가한다.
            //  C++에서 사용하는 vector의 push_back과 같다고 보면 된다.
            arraydata.Add(Random.Range(0.0f, 10.0f));
        }
        savedata["arraydata"] = arraydata;      //  위에서 만든 JArray를 대입.

        //  다른 방법으로 JArray 사용하기
        savedata["newarr"] = new JArray();      //  새로운 key에 value로 JArray 할당.
        for (int i = 0; i < 5; i++)
        {
            ((JArray)savedata["newarr"]).Add(Random.Range(0, 50));  //  JArray 변수를 만들어서 축약 가능
        }

        //  json 형식을 value로 사용하기
        savedata["parent"] = new JObject();     //  key를 지정하고 value에 new JObject()를 대입.
        savedata["parent"]["child1"] = 123;
        savedata["parent"]["child2"] = 456;
        
        //  class를 json으로 변환하기
        SaveData s = new SaveData();    //  인스턴스화 시키고 적당히 데이터를 입력.
        s.id = 0;
        s.namelist.Add("komastar");
        s.namelist.Add("kintaro");
        savedata["class-savedata"] = JToken.FromObject(s);  //  파싱.
        
        //  파일로 저장
        string savestring = JsonConvert.SerializeObject(savedata, Formatting.Indented);     //  JObject를 Serialize하여 json string 생성
        File.WriteAllText(Application.persistentDataPath + "/savedata.json", savestring);   //  생성된 string을 파일에 쓴다
    }

    public void Load()
    {
        //  불러오기는 저장의 역순
        string loadstring = File.ReadAllText(Application.persistentDataPath + "/savedata.json");    //  string을 읽음
        JObject loaddata = JObject.Parse(loadstring);   //  JObject 파싱

        //  key 값으로 데이터 접근하여 적절히 사용
        Debug.Log("key-value 개수 : " + loaddata.Count);

        Debug.Log("----------------------------");
        
        Debug.Log(loaddata["class-savedata"]);

        Debug.Log("----------------------------");

        JArray loadarray = (JArray)loaddata["arraydata"];
        for (int i = 0; i < loadarray.Count; i++)
        {
            Debug.Log(loadarray[i]);
        }

        Debug.Log("----------------------------");

        foreach (var item in loaddata["newarr"])
        {
            Debug.Log(item);
        }

        Debug.Log("----------------------------");

        Debug.Log(loaddata["newarr"]);
    }
}

public class SaveData
{
    //  변수 이름이 key값으로 사용된다.
    public int id;

    [JsonProperty("name-list")]     //  프로퍼티 이름을 지정하면 변수 이름 대신 key값으로 사용된다.
    public List namelist;   //  "namelist" : {} 대신 "name-list" : {} 으로 사용됨.

    public SaveData()
    {
        id = 0;
        namelist = new List();
    }
}

/*
{
    "key-name": "value-data",
    "anyname": 1.0,
    "is-save": true,
    "arraydata": [
        3.138103,
        6.43384361,
        0.0607228279,
        5.9034543,
        6.343808,
        0.208146572,
        6.17789,
        8.661151,
        9.782631,
        4.389159
    ],
    "newarr": [
        0,
        29,
        0,
        34,
        26
    ],
    "parent": {
        "child1": 123,
        "child2": 456
    },
    "class-savedata": {
        "id": 0,
        "name-list": [
            "komastar",
            "kintaro"
        ]
    }
}
*/

'Programming > Unity3D' 카테고리의 다른 글

Unity에서 JSON 사용하기 ( unity for json )  (1) 2018.07.12
  1. 박찬혁 2018.09.27 16:27

    감사합니다 ^^ 잘 쓰겠습니다

경고.

딱 한번 설치해보고 의식의 흐름에 따라 조립된 기억을 기반으로 작성한 내용이므로 아래의 작업은 Komastar가 보장하지 않습니다.

모든 실행의 책임은 사용자에게 있으며 이 글을 보고 따라해서 발생한 문제에 대해선 Komastar는 어떤 책임도 지지 않습니다.

따라서 실제 개발 전 가상화된 개발 환경 구축 후 테스트를 통해 안정성을 먼저 검토하시기 바랍니다.


1. PHP 설치

php.net 에서 PHP7 압축파일을 다운로드

압축파일을 풀고 폴더 이름을 php7으로 변경 (optional)

php7폴더를 C:\Program Files\ 아래로 이동시킨다

시스템 환경변수의 PATH에 C:\Program Files\php7 추가

(환경변수 추가 시 기존 스트링과 추가할 스트링 사이에 세미콜론 추가하는 것을 잊지말자)

php7폴더 내부에 보면 php.ini-development 라는 설정파일이 있음

"-development"를 제거

텍스트 에디터로 해당 파일을 열고

"php_openssl.dll" 검색하여 해당 라인의 주석 해제

"On windows" 검색하여 그 다음 줄의 주석을 해제



2. composer 설치

https://getcomposer.org/ 설치파일 다운로드

설치 진행

CMD 실행 후 composer -V로 설치 확인


"내문서\Composer\composer.json"에 아래 내용 추가 후 저장

"repositories": [

    {

         "type": "composer", 

         "url": "https://packagist.org"

    },

    { "packagist": false }

]


"내문서\Composer\config.json"에 아래 내용 추가 후 저장

"config": {

        "disable-tls": true

}


3. laravel 설치

CMD 실행 후 아래 명령어 실행

composer global require "laravel/installer"

(무작정 실행해보고 예외가 떠서 그 예외에 대한 처리를 윗 단계에서 미리 했으나 새로운 예외가 생길 수 있음. 그땐 구글링 해볼 것)



4. laravel 시작

CMD 실행 후 프로젝트 폴더로 이동

laravel new 프로젝트이름

위 커맨드를 실행하면 "프로젝트이름"으로 폴더가 생성되고 내부에 laravel에 필요한 파일들이 생성됨.


모든 걸 마쳤다면 이제 개발을 시작하면 된다.

'Programming > PHP' 카테고리의 다른 글

Laravel Installation @ Windows 8 (라라벨 설치 윈도우8)  (0) 2017.06.11
https://msdn.microsoft.com/ko-kr/library/az24scfc(v=vs.120).aspx

 

'Programming > .NET' 카테고리의 다른 글

C# 정규표현식 Regular Expression MSDN 참조  (0) 2015.07.30
[ASP.NET]관리용 웹페이지 제작 1  (0) 2015.02.18

POV : Persistence Of Vision

빛의 잔상 효과를 이용해 디스플레이를 만드는 기술

Youtube에 POV를 검색하면 수도 없이 많은 영상이 쏟아지니 그것을 참고함

Spoke POV를 보고 계획을 세움. 순도 90% 자작 프로젝트

참고한 내용은 제작에 필요한 재료(적당히 비슷하거나 같게)

https://learn.adafruit.com/spokepov/

할 일 : 알고리즘 만들기, 소스 코드 작성, 하드웨어 조립 제작

 

실험 준비

MCU (ATtiny2313V) - 2EA

Hall sensor (WSH135-XPAN2) - 10EA

ISP (MAI-ISP-MK2-B) - 1EA

브레드보드 (EIC-108) - 1EA

LED(R) - 40EA

LED(G) - 40EA

LED(B) - 40EA

LED(W) - 40EA

Shift Register (74HC595) - 16EA

멀티미터 (UT10A) - 1EA

이외 소모품으로 납, 핀헤더, 점퍼킷 구매

외부 메모리가 있어야 하지만 이번 달은 테스트만 진행해볼 예정이기에 구매하지 않음

이후 Ambilight 프로젝트 진행할 때 견적이 적절하면

 

2015. 06. 11 배송완료

Atmel Studio 6.2 설치

ISP 드라이버는 Atmel Studio 설치 파일에 포함되어 있음

 

개발환경 정리

OS : Windows 8.1 64bit

IDE : Atmel Studio 6.2 SP2

Language : C

Programmer : AVRISP MK2

Tab Size : 4 space

 

개발환경 테스트

간단하게 LED 하나 깜박이는 코드 작성

 

#define F_CPU 12000000
#include <avr/io.h>
#include <util/delay.h>
 
void main()
{
    int endCount = 0;
    DDRB = 0xFF;
 
    while(1)
    {
        PORTB = 0xFF;
        _delay_ms(100);
        PORTB = 0x00;
        _delay_ms(100);
        if(endCount > 10)
        {
            break;
        }
        endCount++;
    }
}

'Programming > Embedded' 카테고리의 다른 글

Bike POV DIY (Spoke POV)  (0) 2015.06.04
ATmega128 MPU6050  (18) 2014.03.18
Getting Start STM32F103 Dev  (0) 2014.02.28
STM32F103 - QuadCopter 참고 소스  (0) 2014.02.19
STM32F103 - PWM 소스  (0) 2014.02.19
FB155BC 설정 with USB2UART Downloader  (0) 2014.02.19
HTML, JS, web

참조

https://github.com/hakimel/reveal.js

http://lab.hakim.se/reveal-js/

 

작업 환경

OS : Windows 7 SP1 x64

Editor : Sublime Text 2

 

실행1 : 깃헙 링크를 따라가서 프로젝트를 다운받아 압축을 풀고 (혹은 git clone) index.html 을 브라우저로 실행

실행2 : 깃헙 링크에 들어가면 Full Setup 항목을 순차적으로 진행하면 됨

1. https://nodejs.org/ 에 가서 node.js 를 설치

2. 설치 완료 후 윈도우 커맨드에서 npm install -g grunt-cli 실행

3. Reveal.js 프로젝트 폴더로 이동하여 grunt serve 실행

4. nodejs 웹서버가 실행되며 기본 브라우저가 열릴 것임. http://0.0.0.0:8000

5. localhost로 실행되어야 하므로 설정 파일 편집

Gruntfile.js

아래 부분을 찾아 붉은색으로 칠해진 부분 추가

connect: {
   server: {
    options: {
     hostname: '127.0.0.1',
     port: port,
     base: '.',
                    livereload: true,
                    open: true
    }
   }
  },

6. 매번 커맨드 창 열어서 실행하기 귀찮으니 배치파일 작성

7. 텍스트 편집기를 열어 아래의 내용을 입력

@echo off
grunt serve --port 8001

run.bat 로 저장(저장 위치는 index.html과 같은 위치)

8. 이제 run.bat를 실행

 

편집 : index.html 을 텍스트 편집기로 열어서 편집

기본적인 설정은 다 되어 있는 상태이므로 head 태그 부분은 넘어감

body 태그를 보면 div와 section이 보일 것임

 

    
CONTENTS
reveal 클래스 내부에 slides 클래스를 넣고 section 태그가 하나의 슬라이드 페이지가 된다.


CONTENTS 1
CONTENTS 2

section 태그가 형제로 나열되어 있으면 좌우 이동으로 슬라이드를 넘긴다


CONTENTS 1
CONTENTS 2

section 태그 내부에 section 태그들을 나열하면 상하 이동으로 슬라이드를 넘긴다

img 태그와 iframe 태그 등을 넣어 이미지 혹은 다른 웹페이지를 슬라이드에 첨부 할 수 있으며

슬라이드 쇼라고 해도 결국 HTML 페이지이기 때문에 iframe 태그에 들어간 페이지는 슬라이드 중에 브라우징도 가능하다

github의 설명을 보면 알겠지만 상당히 다양한 기능을 갖추고 있으며

HTML 기반이기에 멀티미디어 사용이 굉장히 편리하다

간단한 프로모션 사이트 정도는 reveal.js 만 가지고 충분히 만들어볼만하다고 생각함

github의 사용법을 훑어보기만 하고 후에 필요할때가 써먹어 볼 예정

'Programming > HTML/CSS/JS' 카테고리의 다른 글

[Reveal.js]슬라이드 쇼. 웹으로 간다  (0) 2015.05.13

검색어

ASP.NET / MVC / DB / Database

 

개발 환경

OS : Windows 8.1 64bit

IDE : Visual Studio 2013 Community

DB : MS SQL Server 2008 R2 Express

Framework : .Net Framework 4.5

 

서버 환경

OS : Windows Server 2012

WAS : IIS 8

DB : MS SQL Server 2008 R2 Express

 

개발 목표

간단한 일정 관리 페이지

상용 수준은 어렵겠지만 규모를 줄이고 수준을 낮춰서 구현 할 예정

 

기능 목록

1. 일정 등록

일정 구성요소 : 제목, 내용, 등록자, 등록일, 수정일, 시작일, 종료일, 참여자

2. 일정 수정

등록자, 등록일, 수정일을 제외한 모든 항목에 대해 수정이 가능

3. 일정 삭제

자신이 등록한 일정만 삭제 가능

4. 일정 확인

기본적으로 본인이 등록한 일정과 참여하는 일정을 표시

테이블 형태의 목록 혹은 Gantt 차트 형태로 표시

5. 일정 알림

등록한 일정이 실행될 시간이 다가오면 설정해둔 값에 따라 사용자의 e-mail로 알림

6. DashBoard (Admin)

사이트 사용량을 기록하고 한눈에 확인 할 수 있음

 

본격! 구현화계 크라피카

1. Project Setting

Create Project : New Project - C# - Web - MVC ( .Net 4.5 )

프로젝트 생성

 

.NET Framework 4.5 선택. ASP.NET 웹 응용 프로그램 선택. 프로젝트 이름 입력

 

MVC 선택

 

프로젝트 생성 완료

축하 인사와 함께 도움이 되는 링크들이 잔뜩

 

디버깅 실행

설치된 브라우저에 한하여 디버깅 타겟을 변경 할 수 있음

 

실행 결과

프로젝트에 기본으로 포함된 코드 덕분에 바로 실행 가능

 

사용하지 않는 파일들 제거

최초 프로젝트 생성시 같이 생성되는 파일로 참고용으로 놔둬도 좋다

사용하지는 않을 예정

 

Connect Database Server : Web.config

프로젝트의 Web.Config 설정

 

2. Get Reference Package (Nuget Console)

 

Nuget 패키지 관리자 실행

 

콘솔창에 Install-Package Petapoco 입력

 

설치 완료된 모습

 


이제 Database Migration 작업을 해보자

서비스 게시와 동시에 관리를 위한 계정을 생성하기 위하여 진행하는 작업이다

우선 패키지 관리자 콘솔에서 아래 명령어를 입력한다

PM> Enable-Migrations

아래와 같이 Migrations/Configuration.cs 가 생성된걸 확인 할 수 있다

 

 

protected override void Seed(Manager.Models.ApplicationDbContext context)
{
    IdentityResult IdRoleResult;
    IdentityResult IdUserResult;

    var roleStore = new RoleStore<IdentityRole>(context);
    var roleManager = new RoleManager<IdentityRole>(roleStore);

    if (!roleManager.RoleExists("Admin"))
    {
        IdRoleResult = roleManager.Create(new IdentityRole { Name = "Admin" });
    }
    if (!roleManager.RoleExists("Manager"))
    {
        IdRoleResult = roleManager.Create(new IdentityRole { Name = "Manager" });
    }
    if (!roleManager.RoleExists("Tester"))
    {
        IdRoleResult = roleManager.Create(new IdentityRole { Name = "Tester" });
    }
    var userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(context));

    var admin = new ApplicationUser { UserName = "admin" };
    IdUserResult = userManager.Create(admin, "admin");
    var adminId = userManager.FindByName(admin.UserName).Id;
    IdUserResult = userManager.AddToRole(adminId, "Admin");

    var manager = new ApplicationUser { UserName = "manager" };
    IdUserResult = userManager.Create(manager, "manager");
    var managerId = userManager.FindByName(manager.UserName).Id;
    IdUserResult = userManager.AddToRole(managerId, "Manager");

    var tester = new ApplicationUser { UserName = "tester" };
    IdUserResult = userManager.Create(tester, "tester");
    var testerId = userManager.FindByName(tester.UserName).Id;
    IdUserResult = userManager.AddToRole(testerId, "Tester");
}

 

Configuration.cs에 Seed 메소드를 위 내용으로 채운다

간단히 설명하자면 ASP.NET에서 인증을 도와주는 Identity2와 EntityFramework가 계저의 역할을 정의하고

역할을 맡을 계정을 미리 하나씩 생성해두는 코드이다

ASP.NET에서는 Code First라고 부르는 약간 독특한 프로세스인데 데이터베이스를 작성하는 코드를 먼저 짜두면

EntityFramework가 데이터베이스 생성을 돕는다

우리는 이 Code First를 이용해서 데이터베이스를 생성하면서 같이 필수 계정을 등록하는 작업을 하는 것이다

일단 기본적으로 관리자 계정과 역할을 생성해야 사이트 생성 후 관리를 할 수 있을 것이다

이 역할을 Admin이라 명명한다

위 코드에서는 Admin 역할 계정명을 admin으로 작성했으나 헷갈리지 않게 하려고 한 것이고 실제로 admin 혹은 administrator를 그대로 사용하면 계정 생성이 안된다

확인된 걸로는 admin과 administrator만 피하면 어떤 것이든 생성되는 것 같다

일단 코드를 수정하여 "webAdmin / webAdmin"으로 생성하자

또한 사이트를 관리하는데 시스템 적인 관리가 아닌 운영 측면의 관리를 위해 Manager 역할을 생성한다

사이트에 기능을 새로 추가하거나 수정하고 라이브 서버에 게시하고 싶은데 일반적인 사용자의 접근을 막고 싶은 경우가 있을 것이다

Tester라는 역할을 만들어두고 기능 혹은 페이지를 Tester에게만 보이게 하여 QA를 실시 할 수도 있겠다

일반 사용자는 따로 역할을 배정하지 않도록 할 것이다

아래 명령어를 입력하여 설정한 내용을 적용하여 실제 DB를 생성해보자

PM> Update-Database

Web.config 설정과 Migration 설정 코드에 의해서 솔루션 폴더에 있는 App_Data 폴더에 Manager.mdf / Manager_log.ldf 파일이 생성되었을 것이다

그리고 SQL Management Studio를 실행하여 로컬 연결로 들어가면 Manger 데이터베이스가 추가 되어 있을 것이다

기본적인 환경 설정은 여기까지

앞으로 MVC 구조로 원하는 사이트를 구현하고 각종 라이브러리를 사용해 개발과 운영의 편의성을 높여 볼 것이다

 

다음 포스팅 : ASP.NET with Petapoco

 

3. How to use Petapoco

DB를 다루는데 도움이 되는 가벼운 싱글톤 라이브러리

인젝션 방지도 기본적으로 해줌

테이블과 매칭 시킬 모델 클래스를 생성

이전 단계에서 nuget 패키지 설치를 했음


 

4. Hangfire

스케줄링 라이브러리

대쉬보드를 통해 스케줄 걸린 작업을 확인하고 결과도 볼 수 있음

PM> Install-Package Hangfire

Hangfire 실행

원격지에서 Hangfire dashboard 보기

1회성 스케줄 작업 추가

주기적인 스케줄 작업 추가

 

5. SMTP 서비스


IIS 설정


6. log4net

log4j의 닷넷 버젼

쉬운 설정과 사용법으로 빠르게 적용이 가능함

사용이 용이한 것에 비해 강력한 기능

로깅을 위한 log4net 설정

로깅 시나리오 구성

로깅 실행 및 확인

PM> Install-Package log4net


 

7. javaScript! 웹을 화려하게! 유려하게! 미려하게!

차트 라이브러리(바, 레이더, 도넛 등)

http://www.chartjs.org/

 

차트 라이브러리(간트)

https://github.com/komastar/GanttChart

 

날짜와 시간 라이브러리

http://momentjs.com


'Programming > .NET' 카테고리의 다른 글

C# 정규표현식 Regular Expression MSDN 참조  (0) 2015.07.30
[ASP.NET]관리용 웹페이지 제작 1  (0) 2015.02.18

Windows 7 64bit / Visual Studio 2013 Express

 

 

#include "stdafx.h"

int _tmain(int argc, _TCHAR* argv[])
{
    int height = -1;
    int lineNumber = 0;
    int numberPrint = 1;
    int variation = 1;
    int mid = 0;
    int numberCount = 0;

    printf_s("Input height : ");
    scanf_s("%d", &height);

    for (int heightCount = 1; heightCount < height; heightCount++)
    {
        numberCount = 0;
        numberPrint = heightCount;

        

        for (int spaceCount = 0; spaceCount < height - heightCount; spaceCount++)
        {
            printf_s(" ");
            numberCount++;
        }

        for (int widthCount = 0; widthCount < (heightCount * 2) - 1; widthCount++)
        {
            if (numberPrint >= 10)
            {
                numberPrint = numberPrint % 10;
            }
            else if (numberPrint < 0)
            {
                numberPrint = 9;
            }

            mid = (heightCount * 2) - 1;
            printf_s("%d", numberPrint);
            numberCount++;

            if (numberCount >= height)
            {
                variation = -1;
            }
            
            numberPrint += variation;
        }
        printf_s("\n");
        variation = 1;
    }

    return 0;
}

Output

 

 

Windows 7 64bit / Visual Studio 2013 Express 32bit

#include "stdafx.h"

int _tmain(int argc, _TCHAR* argv[])
{
    char ch = 'A';
    int height = -1;

    printf_s("Input Height : ");
    scanf_s("%d", &height);

    for (int heightCount = 0; heightCount < height; heightCount++)
    {
        for (int spaceCount = 0; spaceCount < height - heightCount; spaceCount++)
        {
            printf_s(" ");
        }
        for (int widthCount = 0; widthCount < heightCount; widthCount++)
        {
            printf_s("%c ", ch);
            ch++;
            if (ch > 90)
            {
                ch = 'A';
            }
        }
        ch = 'A';
        printf_s("\n");
    }
    
    return 0;
}

Output

 

 

환경 : Windows 7 64bit / Visual Studio 2013 Express

#include "stdafx.h"

int _tmain(int argc, _TCHAR* argv[])
{
    int width = -1;
    int height = -1;
    int gap = -1;

    printf_s("Input Width : ");
    scanf_s("%d", &width);
    
    printf_s("Input Height : ");
    scanf_s("%d", &height);

    printf_s("Input Gap : ");
    scanf_s("%d", &gap);

    int gapPrint = 0;

    for (int printCount = 0; printCount < height * width; printCount++)
    {
        if ((printCount % width) == 0)
        {
            printCount++;
            printf_s("\n");
            for (int gapCount = gapPrint; gapCount < gap; gapCount++)
            {
                printf_s(" ");
            }
            if (gapPrint == gap)
            {
                gapPrint = 0;
            }
            else
            {
                gapPrint++;
            }
        }
        printf_s("*");
        for (int gapCount = 0; gapCount < gap; gapCount++)
        {
            if ((printCount % width) == 0)
            {
                printf_s("\n");
            }
            printf_s(" ");
        }
    }

    return 0;
}

Output

 

 

2014. 05. 27. 10:00 작성

시작


Windows 7 64bit

Visual Studio Express 2013 for Windows Desktop 32bit


윈도우 명령창에서 동작하는 명령어인 tree 를 C++로 구현한다.

외관상 같은 결과를 보이도록 동작하게 만드는 것이 목표이다.

우선 tree의 출력 내용을 본다.

윈도우 명령창을 실행시키고 tree를 입력한다.

현재 경로의 하위 폴더를 모두 표시해준다.


옵션을 알아보기 위해 도움말을 호출해본다.

도움말을 보는 옵션은 /? 이며 윈도우 명령어들은 이 옵션으로 도움말을 볼 수 있다.

도움말에 옵션이 있으니 옵션을 사용해보도록 한다.

기본적으로 폴더만 보여주는데 /f 옵션을 추가하니 설명대로 파일도 출력된다.

/a 옵션을 보자

깔끔한 그래픽 특수문자 대신 텍스트 특수문자로 변경되었다.

혹시 이 두가지를 같이 사용해도 될까?

두가지 옵션이 모두 적용된 상태로 출력된다.

이번엔 도움말에서 본 경로를 지정해보자.

위와 같이 경로를 지정하니 해당 경로에서 tree 명령어를 실행한 결과가 나온다.

경로와 옵션 모두를 추가하면 하는대로 적용이 되어 출력된다.

옵션의 순서를 바꿔도 모두 적용이 된다.


- 출력되는 결과를 보고 정리를 하면 옵션의 종류는 2가지(F, A) 가 있고 경로를 지정하여 해당 경로의 하위 구조를 볼 수 있다.


- 옵션과 경로를 정상적으로 입력만 한다면 옵션과 경로의 위치는 섞여도 상관이 없으며 모두 적용되어 결과물이 나온다.


- 옵션이나 경로를 잘못 입력하였을 경우는 스크린샷 대신 텍스트로 대체하겠다

경로 정보가 두개 이상 들어가면 "매개 변수가 너무 많습니다."

경로 하위에 폴더나 파일이 없는 경우엔 "[현재 경로]에 하위 폴더가 없습니다."

옵션으로 입력한 글자가 잘못된 경우는 "잘못된 스위치 - [입력한 옵션]"

올바른 옵션에 글자를 더 입력하거나 중복 입력 혹은 3개 이상의 옵션인 경우엔 "매개 변수 형식이 틀립니다. - [입력한 옵션 중 마지막 옵션]"

위와 같이 예외 상황을 처리해준다.


이제 구현해야 하는 것들을 정리해보자.

기본적으로 현재 경로의 하위 구조를 출력해줘야 한다.

그래픽 문자는 까다롭기 때문에 일단 항상 /a 인 상태로 출력한다.

다른 경로를 지정하면 그 경로의 하위 구조를 출력한다.

옵션과 경로의 내용이 올바르다면 입력 순서가 바뀌는 것에 상관없이 모두 적용하여 출력한다.

2014. 05. 27. 10:31 임시 저장

우선 트리 형태로 표현하기 위한 방법을 생각해보자.

- 파일과 폴더로만 구분을 하고 폴더만 하위 파일이나 폴더를 가질 수 있다.

- 폴더의 하위 경로로 내려가는 경우 경로 표시 전에 공백을 둬서 하위임을 나타낸다.

- 같은 위상에 출력할 파일이나 폴더가 있다면 '+'문자로 현재 표시하는 대상이 Branch 임을 표시한다.

- 더 이상 표시할 파일이나 폴더가 없을 경우 마지막 폴더나 파일은 \로 표시해준다.

- 경로에 C:\ 를 입력하면 Root 기준으로 Tree를 그려준다.

- 경로에 C: 를 입력하면 현재 경로 기준으로 Tree를 그려준다.

- 경로 정보에 C:\ 경우 이외엔 \ 로 끝나지 않는다.

2014. 05. 28. 17:53 임시 저장


위의 조건을 만족하는 프로그램을 작성해야 한다.


프로그램에 필요한 기능을 정리를 한다면

파일을 탐색하는 함수를 알아야 하겠고, 하위 경로의 탐색을 위해선 재귀 호출을 사용하는 것이 가장 쉬워보인다.

탐색을 하고 난 뒤 이 파일이 폴더인지 아닌지 구분을 해야 한다.

옵션을 아무것도 입력하지 않은 상태로 실행한다면 실행하는 경로를 대상으로 탐색해야 하니 실행하는 경로를 알아야 한다.

기본 기능을 만드는 것이 우선이니 옵션은 위 기능들을 구현하고 생각해본다.


파일 탐색에 사용되는 함수를 알아본다.

구글에서 한글로 검색하는 것 만큼 답답한 일은 없다.

영어 문장을 완벽하게 작성해서 검색 할 필요도 없다. 우리가 알고 싶은건 파일을 찾는 것이니 단순하게 생각해서

find file 을 검색한다. 그러나 이렇게 검색하면 각종 언어의 내용이 다 나올 수 있으므로 in c를 붙이거나 c++을 붙인다.

검색 결과 Visual C++에서 사용하는 파일 탐색 함수는 findFirstFile 과 findNextFile 이다.

 

#include "stdafx.h"
#include 

#define ARGV_LENGTH 4   //  인자값의 최대 개수는 3개 (index 0번의 인자값에는 실행하는 경로가 들어가기 때문에)

using namespace std;

int PrintTree(LPCSTR cPath, int iRecursiveCount, int iFlag, unsigned int structure, int broCnt);
void PrintBranch(int iRecursiveCount, unsigned int uBranchData, int iFlag, int iBranchEnd);
void PrintErrorMsg(int iErrFlag, char* argv);
void PrintHelp(void);
int ExceptionHandle(int arCount);

int main(int argc, char* argv[])
{
    LPCSTR csPath = "";         //  탐색 경로를 저장 할 변수.
    int iErrCheckFlag = 0;      //  발생한 오류의 종류를 저장하는 변수.
    int iArgvLen[ARGV_LENGTH];  //  입력받은 인자값 각각의 길이.
    int iOption = 0;            //  입력받은 옵션에 따라 비트를 설정한다.
    int iErrCount = 0;          //  인자값 오류가 발생 할 경우 몇번째 값인지 확인.
    int iPathFlag = 0;          //  경로를 입력받았는지 확인.
    int iOptionFlagF = 0;       //  F옵션을 입력받았는지 확인.
    int iOptionFlagA = 0;       //  A옵션을 입력받았는지 확인.
    unsigned int uiTreeStructure = 0;   //  트리 구조의 줄기를 그릴 정보 저장.
    char cOpt;                  //  입력받은 옵션 치환.
    char sPathBuffer[MAX_PATH]; //  경로 임시 저장.
    
    DWORD dwSerial;             //  볼륨의 시리얼 번호를 저장.
    
    GetVolumeInformationA(LPSTR("C:\\"), NULL, NULL, &dwSerial, NULL, NULL, NULL, NULL);    //  현재 볼륨의 시리얼 번호를 가져옴.
    
    for (int i = 0; i < ARGV_LENGTH; i++)   //  각 인자값의 길이를 담을 배열을 초기화.
    {
        iArgvLen[i] = 0;
    }
    
    if (argc != 0)  //  인자값이 1개 이상이면 인자값의 개수가 정상인지 확인한다.
    {
        /* 옵션과 경로를 모두 입력해도 최대 3개의 인자만 받아진다.
        따라서 인자값의 개수가 3개를 초과하면 에러로 간주하고 처리.*/
        iErrCheckFlag = ExceptionHandle(argc);          //  인자값이 3개 이상이라면 오류 플래그에 값을 반환.
        PrintErrorMsg(iErrCheckFlag, argv[argc - 1]);   //  오류 플래그에 따라 오류의 종류를 출력하고 종료.
                                                        //  오류가 없다면 진행.
    }

    printf_s("폴더 PATH의 목록입니다.\n볼륨 일련 번호는 %04X-%04X입니다.\n\n", HIWORD(dwSerial), LOWORD(dwSerial));   // 현재 볼륨의 시리얼 번호 출력.

    for (int argCount = 1; argCount < argc; argCount++) //  입력된 인자값들을 구분하는 반복문 시작.
    {
        if (argv[argCount][0] == '/')       //  인자값이 옵션일 경우 이 분기로 들어간다.
        {
            if (argv[argCount][2] == NULL)  //  옵션은 f 혹은 a 한글자만 들어가기 때문에 2글자 이상이라면 오류로 처리한다.
            {
                cOpt = argv[argCount][1];   //  옵션 변수 치환.

                if (cOpt == 'f' || cOpt == 'F') //  대소문자 상관없이 옵션을 입력받을 수 있다.
                {
                    if (iOptionFlagF == 0)  //  F옵션이 처음 나온다면 옵션 플래그 비트를 설정한다.
                    {
                        iOption |= 0x1;
                        iOption &= 0x5;
                        iOptionFlagF = 1;
                    }
                    else                    //  F옵션이 두번이상 입력되면 오류로 처리한다.
                    {
                        iErrCheckFlag = 4;
                        iErrCount = argCount;
                    }
                }
                else if (cOpt == 'a' || cOpt == 'A')    //  마찬가지로 대소문자 구분 없이 입력받는다.
                {
                    if (iOptionFlagA == 0)  //  A옵션이 처음 나온다면 옵션 플래그 비트를 설정한다.
                    {
                        iOption |= 0x4;
                        iOption &= 0x5;
                        iOptionFlagA = 1;
                    }
                    else                    //  A옵션이 두번이상 입력되면 오류로 처리한다.
                    {
                        iErrCheckFlag = 4;
                        iErrCount = argCount;
                    }
                }
                else if (cOpt == 'h' || cOpt == 'H' || cOpt == '?') //  /h /H /? 옵션은 도움말을 출력한다.
                {
                    PrintHelp(); // 도움말 출력.
                    exit(1);    //  도움말을 출력했다면 프로그램 종료.
                }
            }
            else   //  옵션에 두글자 이상이 들어온 경우
            {
                iErrCount = argCount;   //  몇번째 인자에서 오류가 발생했는지 변수에 저장.
                iErrCheckFlag = 3;      //  오류의 종류를 변수에 저장.
            }
        }
        else      //   인자가 옵션이 아니라 경로 일 경우.
        {
            if (!iPathFlag) //  경로를 처음 입력 받는 경우
            {
                iPathFlag = 1;      //  더 이상 경로를 입력받지 않는다.
                strcpy_s(sPathBuffer, argv[argCount]);  //  입력받은 경로를 탐색 대상으로 한다.
            }
            else
            {
                iErrCheckFlag = 1;
                iErrCount = argCount;
            }
            strcat_s(sPathBuffer, "\\");
            csPath = sPathBuffer;
            printf("%s\n", csPath);
        }
    }
    PrintErrorMsg(iErrCheckFlag, argv[iErrCount]);  //  iErrCheckFlag에 발생한 오류의 종류가 저장되어있다. 오류에 맞게 메시지를 출력하고 종료.
    PrintTree(csPath, 0, iOption, uiTreeStructure, 1);  //  오류가 없다면 tree를 그리는 재귀함수를 호출한다.

	return 0;
}

int PrintTree(LPCSTR cPath, int iRecursiveCount, int iFlag, unsigned int uBranchData, int broCnt)
{
    WIN32_FIND_DATAA strFindData;   //  탐색한 내용을 저장할 구조체 선언
    HANDLE hHandle;                 //  탐색을 위해 핸들 선언

    int iFileType = 0;          //  폴더와 파일 구분
    int iBroNodeCount = 0;      //  형제 노드 개수 파악
    int iPrintCount = 0;        //  출력된 내용의 개수
    int iBranchEnd = 0;         //  가지의 끝
    char sCurrentPath[1000];    //  현재 경로 정보
    char sTempPath[1000];       //  경로 임시 저장
    string sBar = "";
    
    strncpy_s(sCurrentPath, cPath, strlen(cPath));              //  입력받은 경로 인자값을 현재 경로에 복사
    strncpy_s(sTempPath, sCurrentPath, strlen(sCurrentPath));   //  문자열 연산을 위해 임시 저장 변수에 복사
    strcat_s(sTempPath, "*");                                   //  모든 경로를 탐색하기 위해 *을 붙여줌
    
    hHandle = FindFirstFileA((LPCSTR)sTempPath, &strFindData);  //  경로 정보에 따른 파일을 탐색하고 구조체에 넣음
    
    if (hHandle == INVALID_HANDLE_VALUE)        //  핸들에 문제가 있으면 함수 종료
    {
        return 0;
    }

    /* counting brother node */
    if (broCnt > 0)
    {
        uBranchData |= (0x1 << (iRecursiveCount));      //  트리 구조를 그리기 위한 정보를 만든다.
    }

    do
    {
        if (!strcmp(strFindData.cFileName, ".") || !strcmp(strFindData.cFileName, ".."))
        { } //  현재 폴더와 상위 폴더를 뜻하는 '.'과 '..'은 제외한다.
        else
        {
            if (iFlag & 0x1)
            {
                iBroNodeCount++;    //  옵션 f 가 활성화되어 있다면 모든 노드 수를 센다.
            }
            else
            {
                iFileType = strFindData.dwFileAttributes;   //  탐색한 파일의 종류를 변수에 저장
                iFileType = iFileType >> 4;                 //  5번째 비트가 1이면 폴더
                if (iFileType & 0x1)
                {
                    iBroNodeCount++;    //  옵션 f가 비활성화 상태면 폴더 수만 센다.
                }
            }
        }
    } while (FindNextFileA(hHandle, &strFindData)); //  다음 파일을 탐색한다.
    
    FindClose(hHandle); // 형제 노드 탐색을 종료한다.

    /* print part */
    hHandle = FindFirstFileA((LPCSTR)sTempPath, &strFindData);

    do
    {
        if (!strcmp(strFindData.cFileName, ".") || !strcmp(strFindData.cFileName, ".."))
        {}
        else
        {
            iFileType = strFindData.dwFileAttributes;
            iFileType = iFileType >> 4;
            if (iFlag & 0x1)
            {
                iPrintCount++;  //  f 옵션이 활성화 상태면 매번 카운트를 증가시킨다.
            }
            else
            {
                if (iFileType & 0x1)
                {
                    iPrintCount++;  // f 옵션이 비활성화 상태면 폴더를 만날때만 카운트를 증가시킨다.
                }
            }

            if (iPrintCount == iBroNodeCount)   //  가지의 끝인지 확인한다.
            {
                iBranchEnd = 1;
                uBranchData &= ~(0x1 << iRecursiveCount);   //  가지가 끝났다면 이후로 가지를 출력하지 않는다.
            }
            else
            {
                iBranchEnd = 0;
            }

            if (!(iFlag & 0x1))
            {
                if (iFileType & 0x1)    //  f 옵션이 비활성화 상태이면 폴더만 출력한다.
                {
                    PrintBranch(iRecursiveCount, uBranchData, iFlag, iBranchEnd);
                    printf_s("%s\n", strFindData.cFileName);
                }
            }
            else    //  f 옵션이 활성화 상태면 모두 출력한다.
            {
                PrintBranch(iRecursiveCount, uBranchData, iFlag, iBranchEnd);
                printf_s("%s\n", strFindData.cFileName);
            }
            
            if (iFileType & 0x1)    //  폴더일 경우 하위 경로를 재귀호출
            {
                iRecursiveCount++;
                char nextPath[1000] = "";
                char sTempPath[1000] = "";
                strcat_s(nextPath, sCurrentPath);
                strcat_s(nextPath, (char*)strFindData.cFileName);
                strncpy_s(sTempPath, nextPath, strlen(nextPath));
                strcat_s(sTempPath, "\\");
                
                if (PrintTree(sTempPath, iRecursiveCount, iFlag, uBranchData, iBroNodeCount) == 1)
                {
                    iRecursiveCount--;
                }
            }
            else
            {
                continue;
            }
        }
    } while (FindNextFileA(hHandle, &strFindData)); //  다음 파일을 탐색

    FindClose(hHandle);
    
    return 1;
}

void PrintBranch(int iRecursiveCount, unsigned int uBranchData, int iFlag, int iBranchEnd)
{
    /* 트리의 가지를 그린다. 그래픽 문자와 텍스트 문자 중 택일 */
    for (int i = 0; i < iRecursiveCount; i++)
    {
        if (uBranchData & (0x1 << i))
        {
            if (iFlag & 0x4)
            {
                printf_s("|   ");
            }
            else
            {
                printf_s("\u2502   ");
            }
        }
        else
        {
            printf_s("    ");
        }
    }
    
    if (iFlag & 0x4)
    {
        // print normal ascii
        if (iBranchEnd)
        {
            printf_s("\\---");
        }
        else
        {
            printf_s("+---");
        }
    }
    else
    {
        // print exteneded ascii
        if (iBranchEnd)
        {
            printf_s("\u2514\u2500");
        }
        else
        {
            printf_s("\u251c\u2500");
        }
    }
}

int ExceptionHandle(int arCount)
{
    /* 인자 개수 오류를 처리 */
    if (arCount - 1 > 3)
    {
        return 4;
    }
    else
    {
        return 0;
    }
}

void PrintErrorMsg(int iErrFlag, char *argv)
{
    /* 오류 메시지 출력 */
    switch (iErrFlag)
    {
    case 1: 
        printf_s("매개 변수가 너무 많습니다 - %s\n", argv);
        exit(1);
        break;
    case 2: 
        printf_s("하위 폴더가 없습니다\n");
        exit(1);
        break;
    case 3: 
        printf_s("잘못된 스위치 - %s\n", argv);
        exit(1);
        break;
    case 4: 
        printf_s("매개 변수 형식이 틀립니다 - %s\n", argv);
        exit(1);
        break;
    default:
        break;
    }
}

void PrintHelp()
{
    /* 도움말 출력 */
    printf("\
드라이브 또는 경로의 폴더 구조를 그래픽으로 화면에 표시합니다.\n\n\
TREE [드라이브:][경로] [/F] [/A]\n\n\
   / F  각 폴더에 있는 파일 이름을 화면에 표시합니다.\n\
   / A  그래픽 문자대신 텍스트 문자를 사용합니다.\n\n");
}

+ Recent posts