REQUEST)
byte[] messageBytes = new byte[delimiterIndex];
Array.Copy(data, 0, messageBytes, 0, delimiterIndex);
byte[] transactionCodeBytes = messageBytes[0..5];
--------messageBytes[0..5]-------------------------------------------
위는 5바이트 인가요?

RESPONSE)

네, byte[] transactionCodeBytes = messageBytes[0..5];는 5바이트의 배열을 의미합니다.

C#에서 messageBytes[0..5] 구문은 messageBytes 배열의 0번 인덱스부터 5번 인덱스까지의 부분 배열을 생성합니다. 이는 0부터 4까지 인덱스를 포함하므로 총 5개의 바이트가 됩니다.

구체적으로:

  • messageBytes[0..5]는 messageBytes 배열의 0번 인덱스부터 4번 인덱스까지의 바이트를 포함한 배열입니다.
  • 배열 인덱스 범위는 [startIndex..endIndex) 형식이므로, 0..5는 0부터 4까지, 총 5개의 바이트를 포함합니다.

예시:

byte[] messageBytes = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };
byte[] transactionCodeBytes = messageBytes[0..5]; // [0, 1, 2, 3, 4] 바이트 포함


REQUEST)
messageBytes[0..4] 가 5바이트 아닌가요?

RESPONSE)

messageBytes[0..4]는 5바이트가 아니라 4바이트입니다.

C#에서 messageBytes[0..4] 구문은 0번 인덱스부터 3번 인덱스까지의 바이트를 포함하는 부분 배열을 생성합니다. 이는 인덱스가 0부터 시작하고, 4는 포함되지 않기 때문에 총 4개의 바이트를 반환합니다.

인덱스 범위 설명:

  • messageBytes[0..4]는 0, 1, 2, 3번 인덱스를 포함하는 부분 배열이므로, 총 4개의 바이트입니다.
  • [start..end) 범위에서 end는 포함되지 않습니다.

예시:

byte[] messageBytes = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };
byte[] transactionCodeBytes = messageBytes[0..4]; // [0, 1, 2, 3] 바이트 포함

이 경우, transactionCodeBytes는 [0x01, 0x02, 0x03, 0x04] 값이 포함된 4바이트 배열이 됩니다.

1. HashSet.Add() 중복 허용 여부

HashSet<T>의 Add 메서드는 중복된 값을 허용하지 않습니다.

  • 동작 원리: HashSet은 내부적으로 해시 테이블을 사용하여 데이터를 저장하며, 동일한 값(동일한 해시 값과 동등성을 가진 값)을 추가하려고 하면 false를 반환하고 값을 추가하지 않습니다.
  • 예제:
HashSet<string> aaa = new HashSet<string>();

bool added1 = aaa.Add("Hello"); // 반환값: true (값이 추가됨)
bool added2 = aaa.Add("Hello"); // 반환값: false (중복된 값, 추가되지 않음)

Console.WriteLine(added1); // true
Console.WriteLine(added2); // false

2. HashSet의 주요 용도

HashSet<T>는 주로 다음과 같은 상황에서 사용됩니다:

1) 중복 제거

  • HashSet은 중복된 값을 허용하지 않으므로, 중복 요소를 제거하고 고유한 값을 유지해야 할 때 적합합니다.
  • 예제:
     
string[] items = { "apple", "banana", "apple", "orange", "banana" };
HashSet<string> uniqueItems = new HashSet<string>(items);

foreach (var item in uniqueItems)
{
    Console.WriteLine(item);
}
// 출력: apple, banana, orange

2) 빠른 검색

  • HashSet은 **O(1)**의 시간 복잡도로 항목을 검색합니다. 대량의 데이터를 처리하면서 특정 값의 존재 여부를 빠르게 확인해야 할 때 유용합니다.
  • 예제:
     
HashSet<int> numbers = new HashSet<int> { 1, 2, 3, 4, 5 };

if (numbers.Contains(3))
{
    Console.WriteLine("3이 존재합니다.");
}

3) 집합 연산

  • HashSet은 집합 연산(합집합, 교집합, 차집합)을 효율적으로 처리할 수 있는 메서드를 제공합니다.
    • 합집합: UnionWith
    • 교집합: IntersectWith
    • 차집합: ExceptWith
    • 대칭 차집합: SymmetricExceptWith
  • 예제:
     
HashSet<int> set1 = new HashSet<int> { 1, 2, 3 };
HashSet<int> set2 = new HashSet<int> { 2, 3, 4 };

set1.IntersectWith(set2); // 교집합: { 2, 3 }

foreach (var item in set1)
{
    Console.WriteLine(item); // 출력: 2, 3
}

4) 데이터 필터링

  • 중복된 데이터를 제거하거나 특정 조건에 맞는 고유 데이터를 필터링하는 데 활용됩니다.

3. 장점

  • 빠른 성능: HashSet은 값의 추가, 삭제, 검색이 평균적으로 **O(1)**의 시간 복잡도를 가집니다.
  • 중복 방지: 값의 고유성을 보장합니다.
  • 집합 연산 지원: 데이터 처리 시 유용한 집합 연산을 제공합니다.

4. 단점

  • 순서가 없음: HashSet은 요소의 순서를 보장하지 않습니다. 순서가 중요한 경우 List 또는 SortedSet을 사용하는 것이 적합합니다.
  • 추가적인 메모리 사용: 내부적으로 해시 테이블을 유지하기 때문에 메모리 사용량이 상대적으로 많을 수 있습니다.

5. 결론

HashSet<T>는 중복 제거와 빠른 검색이 필요한 상황, 또는 집합 연산을 효율적으로 수행하려는 경우에 매우 유용합니다. 단, 순서가 필요하지 않은 데이터를 처리할 때 적합합니다.

AppDomain.CurrentDomain.BaseDirectory로 얻은 디렉토리 경로에 /Data/CreateDB.sql 파일의 경로를 추가하려면, Path.Combine() 메서드를 사용하여 경로를 안전하게 결합할 수 있습니다.

다음은 currentDirectory에 /Data/CreateDB.sql 파일의 경로를 결합하고 이를 currentFilename 변수에 저장하는 코드 예시입니다:

using System;
using System.IO;

class Program
{
    static void Main(string[] args)
    {
        // 현재 실행 중인 프로그램의 경로 (dotnet run 실행 폴더)
        string currentDirectory = AppDomain.CurrentDomain.BaseDirectory;

        // 하위 디렉토리와 파일 경로를 결합
        string currentFilename = Path.Combine(currentDirectory, "Data", "CreateDB.sql");

        // 결과 출력
        Console.WriteLine($"현재 파일 경로: {currentFilename}");
    }
}

설명:

  • AppDomain.CurrentDomain.BaseDirectory: 현재 실행 중인 프로그램의 경로를 반환합니다.
  • Path.Combine(): 여러 경로 요소를 결합하여 올바른 경로 문자열을 생성합니다. 이 방법은 운영 체제에 맞는 경로 구분자를 자동으로 사용하므로 \ 또는 /를 직접 처리할 필요가 없습니다.
    • "Data"는 BaseDirectory의 하위 디렉터리입니다.
    • "CreateDB.sql"은 그 디렉터리 내의 파일입니다.

결과:

  • 위 코드에서 currentFilename은 currentDirectory 경로와 Data/CreateDB.sql을 결합한 절대 경로를 가지게 됩니다. 예를 들어, currentDirectory가 C:\MyApp\인 경우, currentFilename은 C:\MyApp\Data\CreateDB.sql이 됩니다.

참고:

Path.Combine()은 각 부분을 안전하게 결합하여 운영 체제에 맞는 경로를 생성하므로, 경로 구분자를 직접 추가하거나 처리할 필요가 없어 더 안전하고 권장되는 방법입니다.

 
 
 
 
class Program
{
    public static void Main()
    {
        CHKNM nmm = new CHKNM();
nmm.RUN();
    }
}
class CHKNM
{
    public void RUN()
    {
        string currentDirectory = AppDomain.CurrentDomain.BaseDirectory;
            // 하위 디렉토리와 파일 경로를 결합
            string currentFilename = Path.Combine(currentDirectory, "Data", "CreateDB.sql");
    string currentEstFilename = Path.Combine(currentDirectory, "Data", "CreateEstateDB.sql");


    Console.WriteLine(currentDirectory);
    Console.WriteLine(currentFilename);
    Console.WriteLine(currentEstFilename);
    }
}

C:\Users\xterm\Downloads\dotnetconsole90>dotnet run
C:\Users\xterm\Downloads\dotnetconsole90\dotnetconsole90.csproj : warning NU1803: 'http://nuget.grapecity.com/nuget' 'HTTP' 원본을 사용하여 'restore' 작업을 실 행하고 있습니다. 비
 HTTPS 액세스는 이후 버전에서 제거됩니다. 'HTTPS' 원본으로 마이그레이션하는 것이 좋습니다.
C:\Users\xterm\Downloads\dotnetconsole90\dotnetconsole90.csproj : warning NU1803: 'http://nuget.grapecity.com/nuget' 'HTTP' 원본을 사용하여 'restore' 작업을 실 행하고 있습니다. 비
 HTTPS 액세스는 이후 버전에서 제거됩니다. 'HTTPS' 원본으로 마이그레이션하는 것이 좋습니다.
C:\Users\xterm\Downloads\dotnetconsole90\bin\Debug\net8.0\
C:\Users\xterm\Downloads\dotnetconsole90\bin\Debug\net8.0\Data\CreateDB.sql
C:\Users\xterm\Downloads\dotnetconsole90\bin\Debug\net8.0\Data\CreateEstateDB.sql

C:\Users\xterm\Downloads\dotnetconsole90>

Json 구조체.출력형태 코드(따로따로, 혹은 전체)

1. 나누어서 출력하는 형태
2. 전체를 출력하는 형태 - JsonConvert.SerializeObject(estate, Formatting.Indented)

public void DataToBuffer(byte[] data, int datalen)
{
    string dataText = Encoding.UTF8.GetString(data, 0, datalen);

    if ((dataText.IndexOf("ComplexNo") >= 0) && (dataText.IndexOf("CortarNo") >= 0))
    {
        estateAllInterfaceText estate = JsonConvert.DeserializeObject<estateAllInterfaceText>(dataText);

         PublicApiNM.SendToDebug8(3, "[" + estate.dataClass + "/" + estate.infoMarketClass + "/" + estate.transactionTime + "/" + estate.ComplexNo + "/" + estate.ComplexName + "/" + estate.CortarNo + "]");
         PublicApiNM.SendToDebug8(3, "[" + JsonConvert.SerializeObject(estate, Formatting.Indented) + "]");
    }
}


[2025-01-16 05:15:30]>[ES/555/20250110202308/120617/강정이매/5013011600]
[2025-01-16 05:15:30]>[{
  "dataClass": "ES",
  "infoMarketClass": "555",
  "transactionTime": "20250110202308",
  "ComplexNo": "120617",
  "ComplexName": "강정이매",
  "CortarNo": "5013011600",
  "RealEstateTypeCode": "OPST",
  "RealEstateTypeName": "오피스텔",
  "UseApproveYmd": "20180208",
  "CortarAddress": "제주도 서귀포시 강정동",
  "MinPriceByLetter": "2억 2,000",
  "MaxPriceByLetter": "2억 2,000",
  "MinLeasePriceByLetter": "NONE",
  "MaxLeasePriceByLetter": "NONE",
  "MinPrice": "22000",
  "MaxPrice": "22000",
  "MinLeasePrice": "0",
  "MaxLeasePrice": "0"
}]

코드에서 특정 외부프로세스를 종료시키려고 할떄에(이미 프로세스.아이디를 알고 있는경우에), 예를 들어서ㅡ, 자신의 프로그램에서 발생시킨 프로세스의 경우에, Process.GetProcessById().Kill() 을 이용하려면(?)


app.manifest 파일은 애플리케이션에 대한 메타데이터를 포함하는 파일로, 프로그램이 실행될 때 필요한 권한이나 설정을 지정할 수 있습니다. 예를 들어, 프로그램을 관리자 권한으로 실행하려면 app.manifest 파일에 requestedExecutionLevel을 설정할 수 있습니다.

1. Visual Studio에서 app.manifest 파일 생성 및 설정 방법

Step 1: app.manifest 파일 생성

  1. 솔루션 탐색기에서 프로젝트를 마우스 오른쪽 버튼으로 클릭한 후, "추가" > **"새 항목"**을 선택합니다.
  2. **"애플리케이션 매니페스트 파일"**을 선택하고, 이름을 app.manifest로 지정한 후 "추가" 버튼을 클릭합니다.
  3. 이 작업으로 프로젝트에 app.manifest 파일이 생성됩니다.

Step 2: app.manifest 파일에 관리자 권한 설정 추가

app.manifest 파일을 열고, requestedExecutionLevel을 **requireAdministrator**로 설정하여 애플리케이션이 관리자 권한을 요구하도록 합니다.

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v3" manifestVersion="1.0">
  <assemblyIdentity version="1.0.0.0" name="MyApp.app" />

  <!-- 요청된 실행 레벨 설정 -->
  <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />

</assembly>
  • level="requireAdministrator": 애플리케이션을 실행하려면 관리자 권한이 필요합니다.
  • uiAccess="false": 사용자 인터페이스 접근 권한을 설정합니다. 기본적으로 false로 설정되며, 특정 상황에서만 true로 설정합니다.

Step 3: 프로젝트 빌드 및 실행

  1. 프로젝트를 다시 빌드합니다. 이때 app.manifest 파일에 설정한 권한이 자동으로 반영됩니다.
  2. 관리자 권한이 필요한 애플리케이션을 실행하면, Windows에서 사용자 계정 컨트롤(UAC) 팝업이 나타나고, 사용자가 "예"를 선택해야 실행됩니다.

2. app.manifest 파일에 추가할 수 있는 다른 설정들

app.manifest 파일에는 권한 설정 외에도 여러 중요한 설정을 할 수 있습니다. 예를 들어, 애플리케이션의 최소 Windows 버전, DPI 설정, 그리고 다중 프로세서 사용 등을 지정할 수 있습니다.

예시: 최소 Windows 버전 설정

<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
    <!-- 최소 Windows 7 이상 -->
    <application xmlns="urn:schemas-microsoft-com:compatibility.v1">
        <supportedOS Id="win7" />
    </application>
</compatibility>

예시: DPI 호환성 설정

<application xmlns="urn:schemas-microsoft-com:asm.v3">
    <!-- DPI 자동 크기 조정 -->
    <windowsSettings>
        <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
    </windowsSettings>
</application>

3. 관리자 권한 없이 실행하려면

만약 관리자가 아닌 권한으로 실행하고 싶다면, requestedExecutionLevel을 **asInvoker**로 설정할 수 있습니다. 이 경우 애플리케이션은 호출한 프로세스와 동일한 권한을 사용하여 실행됩니다.

<requestedExecutionLevel level="asInvoker" uiAccess="false" />

이 설정은 기본값으로 관리자가 아닌 사용자 권한으로 실행됩니다.


결론

app.manifest 파일을 사용하면 애플리케이션이 실행될 때 필요한 권한 및 설정을 정의할 수 있습니다. 관리자 권한이 필요한 애플리케이션의 경우 requestedExecutionLevel을 requireAdministrator로 설정하여 관리자 권한을 요구하도록 할 수 있습니다. 이를 통해 Process.GetProcessById().Kill() 등의 중요한 작업을 수행할 때 필요한 권한을 보장할 수 있습니다.

C#의 Queue<T>는 기본적으로 동적 크기를 가지며, 사용자가 삽입하는 데이터의 양에 따라 용량이 자동으로 조정됩니다.

Queue<T>의 용량과 동작에 대한 주요 사항은 다음과 같습니다:

1. 초기 용량

  • Queue<T>는 기본적으로 내부 배열을 사용하여 데이터를 저장합니다.
  • 초기 용량은 0입니다. 첫 번째 항목이 추가되면 내부적으로 기본 크기의 배열이 생성됩니다.

2. 자동 확장

  • Queue<T>에 항목을 추가할 때 현재 용량이 부족하면 내부적으로 배열의 크기가 자동으로 증가합니다.
  • 용량 증가는 일반적으로 현재 용량의 두 배로 이루어져, 효율적으로 동작하도록 설계되어 있습니다.

3. 최대 용량

  • Queue<T>의 최대 용량은 시스템의 가용 메모리에 의해 제한됩니다.
  • 64비트 애플리케이션에서는 매우 큰 크기의 큐를 만들 수 있지만, 시스템 메모리에 의존하기 때문에 메모리가 부족해질 수 있습니다.

4. 참고 사항

  • recvQueue는 Queue<byte[]> 타입이므로, 큐에 추가되는 항목은 byte[] 배열입니다.
  • 각 byte[]의 크기는 배열의 길이에 따라 다릅니다. 따라서 큐가 차지하는 전체 메모리는 큐에 들어있는 byte[]들의 개수와 각 배열의 크기에 의해 결정됩니다.

용량 확인 및 관리 방법

  1. 현재 저장된 항목의 수
  2.  
    코드 복사
    int itemCount = recvQueue.Count;
  3. csharp
  4. 전체 메모리 사용량 확인 (예: 디버깅용)
    • 각 byte[] 배열의 길이를 합산하면 대략적인 메모리 사용량을 계산할 수 있습니다:
      csharp
      코드 복사
      long totalMemoryUsage = recvQueue.Sum(item => item.Length);
  5. 명시적으로 초기 용량 설정
    • 필요 시, Queue<T> 생성 시 초기 용량을 설정할 수 있습니다:
      csharp
      코드 복사
      private Queue<byte[]> recvQueue = new Queue<byte[]>(initialCapacity);

결론

recvQueue의 용량은 동적으로 증가하며, 최대 용량은 시스템 메모리에 의해 제한됩니다. 만약 큐에 들어가는 byte[] 데이터가 매우 크거나 많다면, 메모리 사용량을 주의 깊게 모니터링하는 것이 좋습니다.

메모리 정렬(alignment) 및 **패딩(padding)**과 관련이 있습니다. C 언어에서 구조체는 멤버들을 특정 규칙에 맞춰 메모리 상에 배치합니다.

1. 메모리 정렬 (Alignment)

C 언어에서는 메모리 정렬(alignment)이라는 개념이 존재합니다. 각 데이터 타입은 특정 바이트 경계에 맞춰 메모리에서 정렬됩니다. 이 정렬은 CPU의 성능과 관련이 있으며, 데이터 접근을 최적화하려는 목적이 있습니다.

  • int 타입은 보통 4바이트 경계에 정렬되어야 합니다.
  • char 타입은 1바이트 크기이므로 1바이트 경계에 정렬됩니다.

따라서, C 언어에서 struct 내부에 여러 타입의 변수가 있을 때, 각 타입이 그에 맞는 정렬을 따르게 되며, 필요할 경우 패딩을 추가하여 정렬 규칙을 맞춥니다.

2. 패딩 (Padding)

패딩은 정렬 규칙을 맞추기 위해 자동으로 추가되는 메모리 공간입니다. C 언어에서는 메모리 정렬을 맞추기 위해, 데이터 타입의 크기가 해당 타입의 정렬 기준에 맞지 않으면 추가적인 바이트가 삽입됩니다.

예시:

struct NM { int age; // 4 bytes char name; // 1 byte };
  • int는 보통 4바이트 정렬을 요구합니다. age는 4바이트 경계에 맞게 정렬됩니다.
  • char는 1바이트 크기이고, 정렬에 특별한 제한이 없습니다. 하지만 name이 끝나고 난 후, 구조체의 크기가 4바이트 배수로 정렬되어야 할 수 있습니다. 즉, 구조체의 전체 크기가 4바이트 배수가 되도록 패딩이 추가됩니다.

구체적으로 설명하면:

  • age는 4바이트를 차지하고, 그 뒤에 1바이트 크기의 name이 옵니다. 그러면 name 뒤에 3바이트가 패딩으로 추가되어야 전체 크기가 4의 배수가 되도록 맞춰집니다.

따라서, 구조체 NM는:

  • age (4 bytes)
  • name (1 byte)
  • 패딩 (3 bytes)

이렇게 총 4 + 1 + 3 = 8바이트가 됩니다. 즉, sizeof(struct aaa)는 8이 됩니다.

요약:

sizeof(struct NM)가 4가 아니라 8인 이유는 int 타입의 정렬 요구사항과 구조체의 메모리 정렬 규칙에 의해 패딩이 추가되기 때문입니다.

3. 다른 언어에서의 메모리 정렬 및 패딩

C++:

C++도 C 언어와 비슷한 방식으로 구조체 내에서 메모리 정렬을 처리합니다. 따라서 C++에서도 같은 규칙을 따라 메모리 정렬을 맞추고, 패딩을 추가하여 구조체의 크기를 결정합니다.

Java:

Java는 기본적으로 32비트64비트 아키텍처에서 다른 메모리 정렬 방식을 사용합니다. Java에서는 내부적으로 객체를 정렬하는 방식이 다르고, int는 기본적으로 4바이트로 정렬됩니다. 그러나 Java는 메모리 정렬을 크게 신경 쓰지 않으며, 내부적으로 JVM이 최적화합니다. Java의 구조체는 사실 클래스 형태로 구현되며, 이 클래스는 JVM의 메모리 관리 방식을 따릅니다. Java에서는 일반적으로 sizeof를 사용하지 않지만, Object 크기는 JVM이 관리합니다.

Python:

Python은 C와 같은 시스템 수준 언어가 아니므로 구조체의 크기를 sizeof와 같이 직접적으로 계산할 필요가 없습니다. 그러나 struct 모듈을 사용하면 바이트 단위로 데이터를 처리할 수 있으며, 이 때는 C와 유사한 방식으로 정렬과 패딩을 처리합니다. Python에서 struct 모듈을 사용하여 C 스타일의 구조체를 만들 때도 메모리 정렬이 자동으로 적용됩니다.

Go:

Go 언어는 C와 유사한 메모리 정렬 규칙을 따르지만, 구조체의 각 필드는 8바이트 정렬을 기본으로 하며, Go는 이를 자동으로 관리합니다. Go에서 unsafe.Sizeof()를 사용하여 구조체 크기를 확인할 수 있습니다.

Rust:

Rust도 C와 유사하게 메모리 정렬을 자동으로 처리합니다. #[repr(C)] 어트리뷰트를 사용하여 C와 같은 방식으로 메모리 레이아웃을 강제할 수 있으며, 기본적으로 1, 2, 4, 8 등의 크기 단위로 정렬합니다. Rust에서는 std::mem::size_of::<T>()를 사용하여 구조체 크기를 확인할 수 있습니다.

4. 정리

C에서 sizeof(struct NM)가 4가 아니라 8인 이유는 메모리 정렬패딩 때문입니다. int는 4바이트 경계에서 정렬되어야 하고, 그 뒤에 위치한 char는 1바이트만 차지하지만, 구조체 전체 크기를 4의 배수로 맞추기 위해 추가적인 3바이트가 패딩으로 삽입됩니다.

다른 언어들에서도 비슷한 방식으로 메모리 정렬과 패딩이 적용되며, 그 방식은 언어의 구현과 메모리 관리 방식에 따라 다를 수 있습니다.

Winsows Form에 DataGridView를 이용한 네이버 부동산 크롤링 데이타 표시(서울시.마포구)

>dotnet new winforms -o NaverWinFrm401
>dotnet add package ServiceStack
>dotnet add package Newtonsoft.Json
>dotnet add package System.Data.SQLite
>dotnet add package ServiceStack.Redis

특징)
1. INFO table에서 데이타를 읽어와서 표시하는것을 원칙
2. INFO table의 데이타는 프로그램시작시에, INFOTMP에 크롤링데이타 저장후에 INFO로 복사

EstateMainFrm.cs
0.02MB

 

+ Recent posts