C/C++ 프로그레스바 (ProgressBar)

프로그래밍/C, C++|2023. 6. 16. 07:57
반응형

C/C++의 콘솔 환경에서 프로그레스바 (진행바) 구현 소스입니다.

아래와 같이 심플하게 개수, 프로그레스바, 진행률 이 출력됩니다.

 

10/10 [==================================================] 100%

 

#include <stdio.h>
#include <stdlib.h>  
#include <windows.h> // Sleep 함수 

int main() {
        const char bar = '='; // 프로그레스바 문자  
        const char blank = ' '; // 비어있는 프로그레스바 문자  
        const int LEN = 20; // 프로그레스바 길이  
        const int MAX = 1000; // 진행작업 최대값 
        const int SPEED = 50; // 카운트 증가 대기시간  
        int count = 0; // 현재 진행된 작업  
        int i; // 반복문 전용 변수  
        float tick = (float)100/LEN; // 몇 %마다 프로그레스바 추가할지 계산 
        printf("%0.2f%% 마다 bar 1개 출력\n\n", tick); 
        int bar_count; // 프로그레스바 갯수 저장 변수  
        float percent; // 퍼센트 저장 변수  
        while(count <= MAX) {
                printf("\r%d/%d [", count, MAX); // 진행 상태 출력  
                percent = (float)count/MAX*100; // 퍼센트 계산  
                bar_count = percent/tick; // 프로그레스바 갯수 계산  
                for(i=0; i<LEN; i++) { // LEN길이의 프로그레스바 출력  
                        if(bar_count > i) { // 프로그레스바 길이보다 i가 작으면 
                                printf("%c", bar);
                        } else { // i가 더 커지면  
                                printf("%c", blank);
                        }
                }
                printf("] %0.2f%%", percent); // 퍼센트 출력  
                count++; // 카운트 1증가  
                Sleep(SPEED); // SPEEDms 대기  
        }
        printf(" done!\n\n");
        system("pause"); // 프로그램 종료 전 일시정지  
        return 0; 
}

 

1000개의 전체 작업량 중 1개가 완료되었다면 진행률은 0.1%가 됩니다.
현재 진행량/전체 진행량 * 100 = 진행률(%)
100분율 계산법입니다.
해당 연산은 코드의 19행에서 진행하고 있습니다.
현재 진행량은 코드상에서 count 변수
전체 진행향은 코드상에서 MAX 변수입니다.

진행률을 계산했으면 해당 진행률을 기준으로 프로그레스바(진행바)를 출력해야합니다.
100(%)/프로그레스바 길이 = 몇 %마다 프로그레스바 1개 출력
만약 프로그레스바의 길이가 20이라고 하면
100/20 = 5
[====================] 100%
위의 모습일겁니다.

길이는 고정되어있음으로 진행률 퍼센트에 따라 = 문자를 출력해줘야 하죠
코드의 13행에서 해당 계산을 진행하고 있습니다.
계산 후 tick 이라는 변수에 저장해두었습니다.

20길이의 프로그레스바는 5% 마다 = 한개를 출력합니다.
10%라고 하면 == 를 출력하겠죠?

17~31행의 while 문은 0~전체 진행량까지 반복하는 반복문입니다.
19행에서 매번 반복마다 진행률(%)을 계산하여
20행에서 프로그레스바 = 문자를 몇개 출력할지 계산한 후
그 아래 21행 for문에서 출력을 합니다.

for문은 0~LEN 까지 반복을 하는데 LEN은 프로그레스바의 길이가 저장되있는 변수이름입니다.
20이라고 가정하면 0~20까지 반복하계되죠
bar_count는 20행에서 현재 %는 몇개의 =문자를 출력할지 저장되어있습니다.

만약 프로그레스바 길이가 20이고 진행률이 7%라고 가정합시다.

1 - 13행처럼 먼저 몇 %마다 =문자를 출력할지 계산
100/20 = 5(%)
tick = 5

2 - bar_count에 현재 퍼센트는 몇개의 =를 출력할지 계산
percent/tick = 갯수
7/5 = 1(나머지 버림)
bar_count = 1

3 - for문에서 출력
LEN이 20이므로 i = 0~20
if(bar_count > i) 
0~20까지 i가 증가하면서 bar_count와 비교하여 출력
bar_count는 현재 1이 저장되어있으므로 i가 0일때만 참
20번 반복하면서 =하나를 출력하게 되고
거짓인 경우에는 else 로 가서 공백을 출력합니다.

4 - for문으로 출력 후 퍼센트 출력 및 count 증가
count(현재 진행량)를 1 증가

위의 과정을 현재 진행량~전체 진행량까지 반복하여 100%가 되면 종료합니다.
출력하는데 왜 이어서 출력이 되지않고 원래 위치 그대로에서 출력될까요?
그 문제는 18행에 있습니다.
printf("\r")

\r 이스케이프 시퀀스는 해당 라인의 첫 번째 위치로 이동합니다.
첫 번째로 이동한 후 다시 출력을 하게되어 위치가 바뀌지않고 진행되는이유입니다.

 

[출처] https://geundung.dev/43

 

 

반응형

댓글()

정규표현식 연속된 문자 검색

프로그래밍/BASH SHELL|2023. 6. 2. 08:16
반응형

시스템 운영을하며 많이 접하는 명령이 grep 이기도 한데 구체적인 문자열 검색을 위해 정규표현식이 사용됩니다.

어떤 행에서 '몇개의 숫자, 점, 몇개의 숫자' 로 이루어진 문자열은 아래와 같이 출력합니다.

 

grep '[0-9]*[.][0-9]*'

 

 

반응형

댓글()

[ShellScript] 로그 파일 실시간 감시 및 마지막행 처리 방법

프로그래밍/BASH SHELL|2023. 3. 29. 10:57
반응형

쉘스크립트 파일을 아래 내용으로 작성하고 실행하면 됩니다.

 

# vi monitor_and_run.sh

#!/bin/bash

FILE="/home/sysdocu/app.log" # 감시할 파일명

# 파일 감시 및 처리
tail -F -n 0 "$FILE" |\
while read line
do
    # 로그에서 특정 문자열 확인 및 처리
    case "$line" in
        *"test"*) # 여기에 일치되는 문자열
            echo 마지막으로 추가된 행 내용 : $line
        ;;
   esac
done

 

* 결과

마지막으로 추가된 행 내용 : 이건 test 입니다.

마지막으로 추가된 행 내용 : lasttest

마지막으로 추가된 행 내용 : test

마지막으로 추가된 행 내용 : test 끝

 

반응형

댓글()

[쉘스크립트] 오래된 백업 파일 삭제하기

프로그래밍/BASH SHELL|2023. 1. 13. 09:31
반응형

백업스크립트로 데이터를 백업할때 디스크용량이 꽉 차는것을 방지하기 위해 보통은 스크립트 상단에 오래된 백업 파일 또는 디렉토리를 삭제하도록 합니다.

하지만 오래된 디렉토리를 삭제할 경우 그 안의 내용은 지워지지만 디렉토리 자체는 날짜가 갱신되어 (Access, Modify, Change) 삭제가 되지 않습니다.

이 경우 아래와 같이 조치가 가능합니다.

 

예) 20 으로 시작되는 날짜 디렉토리 중 30일이 초과된 디렉토리 삭제

 

1) 기존 방법

명령 : find /backup/20* -ctime +30 -exec rm -rf {} \;

결과 : 날짜가 오래된 디렉토리 내 파일은 삭제되지만 디렉토리는 남게됩니다.

 

2) 새 방법

디렉토리의 변경되지 않는 Birth 날짜와 현재 날짜를 비교하여 삭제

cd /backup
LIST=`stat -c %n" "%w 20* |sed -e 's/-//g' |awk {'print $1" "$2'}`
while read i j; do # i : Directory name, j : Birth date
    if [ $j -le `date +%Y%m%d --date '30 days ago'` ]; then
        rm -rf $i
    fi;
done <<< "$LIST"

 

* 참고

파일 또는 디렉토리의 날짜 확인

# stat 20230102
  File: 20230102
  Size: 30         Blocks: 0          IO Block: 4096   디렉토리
Device: 821h/2081d Inode: 1799764911  Links: 4
Access: (0755/drwxr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2023-01-11 12:00:01.478938107 +0900
Modify: 2023-01-02 12:00:02.236334564 +0900
Change: 2023-01-02 12:00:02.236334564 +0900
 Birth: 2023-01-02 00:00:01.354856557 +0900

 

반응형

댓글()

쉘스크립트 rsync 실행시 끝에 \#015 문자가 붙는 경우 조치방법

프로그래밍/BASH SHELL|2023. 1. 11. 14:44
반응형

파일 리스트 등의 결과를 변수에 넣고 rsync 로 한줄씩 사용하고자 할때 아래와 같은 현상이 발생되었습니다.

이밖에 다른 경우에도 같은 현상이 나타날 수 있는데, 이때 해결방법은 아래와 같습니다.

 

방법1)

리스트가 list.txt 에 있는 경우 개행문자를 제거하고

list_new.txt 라는 새로운 파일에 입력합니다.

 

# tr -d '\r' < list.txt > list_new.txt

 

방법2)

sed -i 's/^M//' list.txt

 

여기에서 ^M 은 ctrl 키를 누른 상태에서 v, m 을 순서대로 누르는 것입니다.

(ctrl + v + m)

 

반응형

댓글()

FCM 을 활용한 PUSH 메세지 보내기 (2024-11-23)

프로그래밍/Android (Java)|2022. 10. 19. 16:16
반응형

New Project > Empty Views Activity 선택

Language : Java

Minimum SDK : API 24

이 상태로 진행하였습니다.

 

 

build.gradle 수정 (프로젝트 수준)

 

아래 내용을 파일에 추가 합니다.

...

buildscript {

    dependencies {
        classpath("com.google.gms:google-services:4.4.2")
    }
}

 

 

build.gradle 수정 (앱 수준)

 

아래 내용을 추가 합니다.

중간에 주석 부분도 추가해줍니다. 없을 경우 바로 아랫줄에 구문 에러 메세지가 출력됩니다.

plugins {
    alias(libs.plugins.android.application)
    id("com.google.gms.google-services")
}

...

dependencies {

    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:2.0.4'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'

    implementation(platform("com.google.firebase:firebase-bom:33.6.0"))
    implementation("com.google.firebase:firebase-analytics")
    implementation("com.google.firebase:firebase-messaging:24.1.0")
    implementation("com.google.firebase:firebase-core:21.1.1")
}

 

 

google-services.json 생성

 

Firebase console 페이지에 접근해서 프로젝트에 APP 을 추가합니다.

추가하는 과정에서 google-services.json 파일을 다운로드 받을 수 있으며,

해당 파일은 app 디렉토리 안에 넣어 놓습니다.

 

 

build.gradle 페이지에서 [Sync Now] 버튼을 눌러줍니다.

 

 

MyFirebaseMessagingService.java 생성

package kr.sysdocu.fcm;

import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;

import androidx.annotation.NonNull;
import androidx.core.app.NotificationCompat;

import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

public class MyFirebaseMessagingService extends FirebaseMessagingService {

    @Override
    public void onNewToken(@NonNull String token) {
        super.onNewToken(token);
        // token을 서버로 전송해서 저장하고 싶은 경우 이곳에 코드 입력
    }

    @Override
    public void onMessageReceived(@NonNull RemoteMessage remoteMessage) {
        super.onMessageReceived(remoteMessage);

        String messageContent;

        // 푸시 메시지 데이터 처리
        if (!remoteMessage.getData().isEmpty()) {
            // Data Payload 처리
            messageContent = remoteMessage.getData().get("message");
            String title = remoteMessage.getData().get("title");
            showNotification(title, messageContent); // 아래에서 정의한 알람을 실행하는 부분
        } else if (remoteMessage.getNotification() != null) {
            // Notification Payload 처리
            messageContent = remoteMessage.getNotification().getBody();
        } else {
            messageContent = "No Content";
        }

    }

    // 알람을 띄운다
    private void showNotification(String title, String message) {
        NotificationManager notificationManager =
                (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

        String channelId = "default_channel_id";

        // Android 8.0 (API 26) 이상에서는 알림 채널을 생성해야 함
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(
                    channelId,
                    "Default Channel",
                    NotificationManager.IMPORTANCE_DEFAULT
            );
            notificationManager.createNotificationChannel(channel);
        }

        // 알림 클릭 시 실행할 Intent 정의
        Intent intent = new Intent(this, MainActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); // 기존 Activity 스택 제거 후 실행

        // PendingIntent 생성
        PendingIntent pendingIntent = PendingIntent.getActivity(
                this,
                0,
                intent,
                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE // 최신 상태 유지
        );

        // 알림 빌더 구성
        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, channelId)
                .setContentTitle(title)                     // 알림 제목
                .setContentText(message)                    // 알림 메시지
                .setSmallIcon(R.drawable.ic_icon)           // 알림 아이콘
                .setAutoCancel(true)                        // 알림 터치 후 제거
                .setContentIntent(pendingIntent);           // 알림 클릭 시 실행될 PendingIntent 설정

        // 알림 표시
        notificationManager.notify(0, notificationBuilder.build());
    }

}

 

 

MainActivity.java 수정

여기에서 코드를 작성할때 파란색 부분은 okhttp 라이브러리를 이용해 토큰을 웹서버로 전송하기 위한 부분입니다.

필요시 같이 작성하면 됩니다.

 

앱수준 build.gradle 파일에 dependencies 추가후 sync 맞추기

implementation("com.squareup.okhttp3:okhttp:4.11.0")

 

package kr.sysdocu.fcm;

import android.os.Bundle;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.firebase.messaging.FirebaseMessaging;

public class MainActivity extends AppCompatActivity {

    private final OkHttpClient client = new OkHttpClient();

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 이 안의 부분은 처음 토큰을 받아올때 한 번만 실행 됩니다.
        FirebaseMessaging.getInstance().getToken().addOnSuccessListener(new OnSuccessListener<String>() {
            @Override
            public void onSuccess(String token) {

                // 토큰을 토스트로 간단히 확인
                Toast toast = Toast.makeText(getApplicationContext(), "TOKEN : " + token, Toast.LENGTH_LONG);
                toast.show();

                // 서버로 토큰 전송 (ssl 사용)
                new Thread(() -> {
                    try {
                        String url = "https://fcm.sysdocu.kr/register.html?token=" + token;
                        String result = run(url);
                        // 결과 처리 (예: 로그 출력)
                        System.out.println(result);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }).start();

            }
        });
    }

    // okhttp 라이브러리 사용
    // run 메서드는 클래스 수준에 정의해야 합니다.
    public String run(String url) throws IOException {
        Request request = new Request.Builder()
                .url(url)
                .build();
        try (Response response = client.newCall(request).execute()) {
            return response.body().string();
        }
    }

}

 

 

activity_main.xml 수정

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:background="#DFE5E5"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:textColor="@color/black"
        android:text="토큰은 웹서버에서 확인하세요." />

</LinearLayout>

 

 

AndroidManifest.xml

 

아래 내용을 추가합니다.

(생략)

<uses-permission android:name="android.permission.INTERNET"/>


...

// <application> 안에 아래 내용 추가

<service
    android:name=".MyFirebaseMessagingService"
    android:exported="false">
    <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT"/>
    </intent-filter>
</service>

(생략)

 

프로젝트를 빌드하여 apk 파일을 생성하고, 핸드폰에 설치합니다.

 

잠깐, 앱을 실행하기 전에 아래 내용 확인해주세요.

위 소스에서 사용되었던 서버 URL (https://fcm.sysdocu.kr/register.html) 에 해당되는 파일을 미리 생성해 놓도록 합니다.

access log 에 접근 로그가 남아 token 확인을 쉽게하기 위함이니 빈 파일도 괜찮습니다.

 

 

PUSH 발송 테스트

 

방법1)

Firebase Console 에서 프로젝트 선택 > '실행' 메뉴 > 'Messaging' 메뉴 > (첫 번째 캠페인 만들기) 버튼 클릭 > Firebase 알림 메세지 체크 후 [만들기]

출력된 화면에서 적절한 내용으로 모두 입력 후 [검토] 버튼을 누르면, 앱을 설치한 모든 Device 로 FCM 메세지가 발송 됩니다.

 

방법2)

PHP 코드를 아래와 같이 작성 합니다.

현재 (2024년 11월) 는 이전에 사용하던 방식에서 FCM HTTP API의 v1 버전으로 전송 방식이 바뀌었습니다.

 

1) 사용자 계정 키 (json) 다운로드

서비스 계정 키 다운로드를 위해 Firebase Console (https://console.firebase.google.com/ ) 로 이동합니다.

프로젝트 설정 > 서비스 계정 > 새 비공개 키 생성 클릭. 

JSON 파일을 다운로드합니다.

다운로드 한 파일은 PHP 생성할 위치로 복사합니다.

 

2) php 파일 작성

v1 API 는 OAuth2 인증을 사용해야 하므로 google/apiclient 패키지를 설치합니다.

# composer require google/apiclient

 

아래 파란색 부분을 적절한 값으로 수정하여 작성합니다.

# vi fcm_send.php

<?php
require 'vendor/autoload.php';

use Google\Auth\Credentials\ServiceAccountCredentials;
use GuzzleHttp\Client;

function sendFCMMessage($serviceAccountPath, $projectId, $token, $title, $body) {
    // FCM 엔드포인트 URL
    $url = "https://fcm.googleapis.com/v1/projects/$projectId/messages:send";

    // 메시지 페이로드
    $message = [
        'message' => [
            'token' => $token,
            'data' => [
                'title' => $title,
                'message' => $body
            ]
        ]
    ];

    // Google OAuth2 인증
    $credentials = new ServiceAccountCredentials(
        'https://www.googleapis.com/auth/firebase.messaging',
        json_decode(file_get_contents($serviceAccountPath), true)
    );

    $accessToken = $credentials->fetchAuthToken()['access_token'];

    // HTTP 요청 헤더
    $headers = [
        'Authorization' => 'Bearer ' . $accessToken,
        'Content-Type' => 'application/json',
    ];

    // HTTP 클라이언트
    $client = new Client();

    try {
        // FCM 메시지 요청
        $response = $client->post($url, [
            'headers' => $headers,
            'body' => json_encode($message)
        ]);

        echo "Response: " . $response->getBody();
    } catch (Exception $e) {
        echo "Error: " . $e->getMessage();
    }
}

// 서비스 계정 JSON 파일 경로
$serviceAccountPath = './your-service-account.json';

// Firebase 프로젝트 ID
$projectId = 'your-project-id';

// FCM 토큰, 제목, 내용
$token = 'your-device-token';
$title = 'Test Title';
$body = 'Test Body';

// 메시지 전송 함수 호출
sendFCMMessage($serviceAccountPath, $projectId, $token, $title, $body);
?>

 

이제 작성한 파일을 실행만 하면 입력해놓은 특정 디바이스로 (token) FCM 메세지가 전송됩니다.

# php fcm_send.php

 

반응형

댓글()

[Shell Script] MySQL 테이블 별로 백업 하기

프로그래밍/BASH SHELL|2022. 9. 8. 08:17
반응형

쉘스크립트를 이용하여 모든 데이터베이스를 테이블 별로 백업하는 방법 입니다.

파일명은 {DB명}.{테이블명}.sql 형식으로 남게 됩니다.

 

# vi mysql_backup_by_table.sh

 

#!/bin/bash

today=`date +%Y%m%d`

# 백업 디렉토리
mkdir -p /backup/${today}
cd /backup/${today}

# MySQL root 패스워드
root_pw='12345678'

# 1) 모든 DB 백업
list=`echo "show databases;" |mysql -uroot -p"$root_pw"`

# 2) 선택한 DB 만 백업 (개행 \n 으로 구분)
#list=`echo -e "mysql\nmonitor\nsysdocu"`

for db in $list;
do
    table_list=`echo "show tables" |mysql -uroot -p"$root_pw" $db`
    for table in $table_list;
    do
        mysqldump -uroot -p"$root_pw" $db $table > ${db}.${table}.sql
        #tar cvzf ${db}.${table}.tar.gz ${db}.${table}.sql --remove-files # 압축 보관 및 원본 삭제
    done
done

 

# chmod 700 mysql_backup_by_table.sh

 

실행

# sh mysql_backup_by_table.sh

 

 

반응형

댓글()

PHP 에서 SQL Injection 방지 쿼리 사용법 두가지 (bind_param, PDO)

프로그래밍/PHP|2022. 8. 30. 11:25
반응형

PHP 에서는 MySQL DB 데이터 입력 또는 조회시 SQL Injection 공격을 막기 위한 방법으로

bind_param 또는 PDO 방식을 사용 할 수 있습니다.

 

 

1. bind_param 사용하기

 

1) INSERT, UPDATE, DELETE

값을 출력하지 않아도 되는 경우 아래와 같은 코드를 사용 합니다.

<?php
$DB_HOST = "localhost";
$DB_USER = "sysdocu";
$DB_PASSWORD = "12345678";
$DB_NAME = "test";

// DB 연결
$conn = mysqli_connect($DB_HOST, $DB_USER, $DB_PASSWORD, $DB_NAME);

// 쿼리 준비
$stmt = $conn->prepare("INSERT INTO item (id, itemA, itemB) VALUES (?, ?, ?)");

// 데이터 준비
$id = "hero";
$itemA = "sword";
$itemB = "shield";

// 데이터 바인딩
$stmt->bind_param('sss', $id, $itemA, $itemB); // 대체될 데이터 세개 (하단 '참고' 확인)

// 쿼리 실행
$stmt->execute();

// 연결 종료
$stmt->close();
$conn->close();
?>

* 참고 : 바인딩 할때 변수 데이터 형식을 정의 하게 되는데, 아래 네가지 종료가 있습니다.

i : 정수

s : 문자열

d : double

b : BLOB

 

2) SELECT

값을 출력하는 경우 아래와 같은 코드를 사용 합니다.

<?php
$DB_HOST = "localhost";
$DB_USER = "sysdocu";
$DB_PASSWORD = "12345678";
$DB_NAME = "test";

// DB 연결
$conn = mysqli_connect($DB_HOST, $DB_USER, $DB_PASSWORD, $DB_NAME);

// 쿼리 준비
$stmt = $conn->prepare("SELECT * FROM item WHERE id=? or id=?");

// 데이터 준비
$var1 = "hero";
$var2 = "hero2";

// 데이터 바인딩
$stmt->bind_param('ss', $var1, $var2); // 대체될 데이터 두개

// 쿼리 실행
$stmt->execute();

// 모든 행의 결과를 출력
$result = $stmt->get_result();
while ($data = $result->fetch_assoc()) {
    echo $data['id'] . " / " . $data['itemA'] . " / " . $data['itemB'] . "<br>";
}

// 연결 종료
$stmt->close();
$conn->close();
?>

 

 

2. PDO 사용하기

 

1) INSERT, UPDATE, DELETE

값을 출력하지 않아도 되는 경우 아래와 같은 코드를 사용 합니다.

<?php
$DB_HOST = "localhost";
$DB_USER = "sysdocu";
$DB_PASSWORD = "12345678";
$DB_NAME = "test";

// DB 연결
$pdo = new PDO("mysql:host=$DB_HOST;dbname=$DB_NAME", $DB_USER, $DB_PASSWORD);

//쿼리 준비
$stmt = $pdo->prepare("INSERT INTO item (id, itemA, itemB) VALUES (:id, :itemA, :itemB)");

// 데이터 바인딩
$stmt->bindValue(":id", "hero");
$stmt->bindValue(":itemA", "sword");
$stmt->bindValue(":itemB", "shield");

// 쿼리 실행
$stmt->execute();

// 연결 종료
$stmt->close();
$pdo->close();
?>

 

2) SELECT

값을 출력하는 경우 아래와 같은 코드를 사용 합니다.

<?php
$DB_HOST = "localhost";
$DB_USER = "sysdocu";
$DB_PASSWORD = "12345678";
$DB_NAME = "test";

// DB 연결
$pdo = new PDO("mysql:host=$DB_HOST;dbname=$DB_NAME", $DB_USER, $DB_PASSWORD);

//쿼리 준비
$stmt = $pdo->prepare("SELECT * FROM item WHERE id=:id");

// 데이터 바인딩
$stmt->bindValue(":id", "hero");

// 쿼리 실행
$stmt->execute();

// 모든 행의 결과를 출력 (한개의 행 또는 한개의 컬럼 등 가져오는 방식은 추가 검색 권고)
$stmt->setFetchMode(PDO::FETCH_ASSOC);  // 추가 설명 아래 '참고' 확인
while ($row = $stmt->fetch()) {
    echo $data['id'] . " / " . $data['itemA'] . " / " . $data['itemB'] . "<br>";
}

// 연결 종료
$stmt->close();
$pdo->close();
?>

* 참고 : 데이터 출력 방식

fetch(PDO::FETCH_BOTH) - 숫자 인덱스와 명명된 인덱스가 있는 배열 입니다.
fetch(PDO::FETCH_ASSOC) - 행은 명명된 인덱스가 있는 배열입니다. ex) $row['itemA']
fetch(PDO::FETCH_NUM) - 행은 숫자 인덱스가 있는 배열입니다. ex) $row[0]

 

 

반응형

댓글()

안드로이드 알람 생성 2가지 방법 (Android Notifications Tutorial with Examples)

프로그래밍/Android (Java)|2022. 7. 18. 12:13
반응형

아래 예제는 Notification 두 가지 방법을 다루고 있습니다.

1. 진동 및 알림 메세지가 전면 상단에 출력되며, 상태바에서도 아이콘으로 나타나는 방법

2. 진동 및 알림 메세지 없이 맨 위 상태바에서만 조용히 아이콘으로 나타나는 방법

 

 

아래는 원문 출처를 따라하며 요약한 내용입니다.

[출처] https://o7planning.org/10427/android-notification

==================================================

 

 

1. Empty Activity 로 프로젝트를 생성합니다.

 

 

2. 알림 메세지에 사용할 이미지 파일 두개를 생성합니다.

drawable/icon_notify1.png

drawable/icon_notify1.png

 

 

3. activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <EditText
        android:id="@+id/editText_title"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="16dp"
        android:ems="10"
        android:hint="Title"
        android:inputType="textPersonName"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <EditText
        android:id="@+id/editText_message"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="16dp"
        android:ems="10"
        android:hint="Message"
        android:inputType="textPersonName"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/editText_title" />

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="33dp"
        android:text="Send on Channel 1"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/editText_message" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="23dp"
        android:text="Send On Channel 2"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/button1" />
</androidx.constraintlayout.widget.ConstraintLayout>

 

4. NotificationApp.java

package org.o7planning.notificationbasicexample;

import android.app.Application;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.os.Build;

public class NotificationApp extends Application  {

    public static final  String CHANNEL_1_ID = "channel1";
    public static final  String CHANNEL_2_ID = "channel2";

    @Override
    public void onCreate() {
        super.onCreate();

        this.createNotificationChannels();
    }

    private void createNotificationChannels()  {
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel1 = new NotificationChannel(
                    CHANNEL_1_ID,
                    "Channel 1",
                    NotificationManager.IMPORTANCE_HIGH
            );
            channel1.setDescription("This is channel 1");

            NotificationChannel channel2 = new NotificationChannel(
                    CHANNEL_2_ID,
                    "Channel 2",
                    NotificationManager.IMPORTANCE_LOW
            );
            channel1.setDescription("This is channel 2");


            NotificationManager manager = this.getSystemService(NotificationManager.class);
            manager.createNotificationChannel(channel1);
            manager.createNotificationChannel(channel2);
        }
    }
}

 

5. AndroidManifest.xml

<application
        android:name=".NotificationApp"
        ....
>
...
</application>

 

6. MainActivity.java

package org.o7planning.notificationbasicexample;

import android.app.Notification;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;


public class MainActivity extends AppCompatActivity {

    private NotificationManagerCompat notificationManagerCompat;

    private EditText editTextTitle;
    private EditText editTextMessage;

    private Button button1;
    private Button button2;


    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        this.editTextTitle = (EditText) this.findViewById(R.id.editText_title);
        this.editTextMessage = (EditText) this.findViewById(R.id.editText_message);

        this.button1 = (Button) this.findViewById(R.id.button1);
        this.button2 = (Button) this.findViewById(R.id.button2);

        this.button1.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View view) {
                sendOnChannel1(  );
            }
        });

        this.button2.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View view) {
                sendOnChannel2(  );
            }
        });

        //
        this.notificationManagerCompat = NotificationManagerCompat.from(this);
    }


    private void sendOnChannel1()  {
        String title = this.editTextTitle.getText().toString();
        String message = this.editTextMessage.getText().toString();

        Notification notification = new NotificationCompat.Builder(this, NotificationApp.CHANNEL_1_ID)
                .setSmallIcon(R.drawable.icon_notify1)
                .setContentTitle(title)
                .setContentText(message)
                .setPriority(NotificationCompat.PRIORITY_HIGH)
                .setCategory(NotificationCompat.CATEGORY_MESSAGE)
                .build();

        int notificationId = 1;
        this.notificationManagerCompat.notify(notificationId, notification);
    }

    private void sendOnChannel2()  {
        String title = this.editTextTitle.getText().toString();
        String message = this.editTextMessage.getText().toString();

        Notification notification = new NotificationCompat.Builder(this, NotificationApp.CHANNEL_2_ID)
                .setSmallIcon(R.drawable.icon_notify2)
                .setContentTitle(title)
                .setContentText(message)
                .setPriority(NotificationCompat.PRIORITY_LOW)
                .setCategory(NotificationCompat.CATEGORY_PROMO) // Promotion.
                .build();

        int notificationId = 2;
        this.notificationManagerCompat.notify(notificationId, notification);
    }
}

 

빌딩 이후 apk 파일을 설치, 어플을 실행하면 두 가지 예제의 알림 방식을 구분할 수 있습니다.

 

[출처] https://o7planning.org/10427/android-notification

반응형

댓글()

[C/C++] int 를 char 또는 const char* 로 변환하기

프로그래밍/C, C++|2022. 6. 30. 13:13
반응형

숫자 뒤에 0 을 붙이면 문자로 인식합니다.

 

const int pos = 100;

const char* charpos = pos+"0";

 

반응형

댓글()

PHP 날짜 비교하기

프로그래밍/PHP|2022. 4. 13. 10:20
반응형

일반 변수에 들어있는 날짜 텍스트로는 비교가 안됩니다. 아래와 같이 strtotime 함수를 이용해 비교 가능하도록 해주세요.

아래는 사용 예제입니다.

 

<?PHP

// 하루 전 날짜

$yesterday_time = strtotime(date("Y-m-d H:i:s", strtotime("-1 day")));

 

// 임의의 날짜

$tmp_time = strtotime("2022-04-13 10:00:00");

 

// 비교하기

if ($yesterday_time <= $tmp_time) {

    echo "임의의 날짜가 더 나중입니다.";

} else {

    echo "하루 전 날짜가 더 나중입니다.";
}

?>

반응형

댓글()