2018年12月26日 星期三

[C#.NET] 字串 codepage 轉碼處理

這是我在某天所遇到的一個問題,我對某台設備進行操作時它回傳了一串我看不懂的字串
⊃;nÅé¼Ò⊃;Õ¤¤ªº¤@¯ë©Ê¿ù»~¡G¼Ð·Ç GUI (⊃;ϧΤƥΤᤶ­±)¡C";
當時直覺就是使用編碼來處理,不過這樣做並不會得到正確的結果,原因是我並未取得正確的編碼byte,也就是codepage
byte[] big5 =Encoding.GetEncoding("Big5").GetBytes("⊃;nÅé¼Ò⊃;Õ¤¤ªº¤@¯ë©Ê¿ù»~¡G¼Ð·Ç GUI (⊃;ϧΤƥΤᤶ­±)¡C");
Console.WriteLine(Encoding.GetEncoding("Big5").GetString(big5));
 
插撥一下,稍微講解一下Encoding.GetEncoding的用法,Encoding.GetEncoding的第一個參數叫codepage,什麼叫codepage(字碼頁)
image
字碼頁。
微軟賦予每個語言一個數字代碼,叫做字碼頁(codepage)。字碼頁可能會決定使用哪些文字,以及單位符號等等。
繁體中文的字碼頁是 950。
我們可以由Encoding.GetEncodings方法 加上  Encoding.CodePage 屬性 取得所有的codepage
static void codepage()
{
    foreach (EncodingInfo ei in Encoding.GetEncodings())
    {
        Encoding e = ei.GetEncoding();
        Console.WriteLine("Name:{0},CodePage:{1}",ei.Name, e.CodePage);
    }
    Console.ReadLine();
}
image
也可以查一下google


接下來回到正題,該怎麼樣處理那該死的亂碼?只要用以下的原始碼就能夠處理。
static void encoding()
{
    string source = "⊃;nÅé¼Ò⊃;Õ¤¤ªº¤@¯ë©Ê¿ù»~¡G¼Ð·Ç GUI (⊃;ϧΤƥΤᤶ­±)¡C";
    byte[] unknow = Encoding.GetEncoding(28591).GetBytes(source);
    string Big5 = Encoding.GetEncoding(950).GetString(unknow);
    Console.WriteLine(Big5);
    Console.ReadLine();
}
先把字串轉成正確的byte
byte[] unknow = Encoding.GetEncoding(28591).GetBytes(source);
再把轉到你想要顯示的編碼
string Big5 = Encoding.GetEncoding(950).GetString(unknow);

果真翻譯成我們要看的編碼了~
image


好了問題來了,怎麼知道編碼是28591,我們可以用下列片斷程式碼來處理,會得到所有codepage交叉處理的結果,觀察文字檔有哪些是看的懂的文字,會得到不少的正確。

static void savecode()
{
    StringBuilder sb = new StringBuilder();
    string source = "⊃;nÅé¼Ò⊃;Õ¤¤ªº¤@¯ë©Ê¿ù»~¡G¼Ð·Ç GUI (⊃;ϧΤƥΤᤶ­±)¡C";

    foreach (var e1 in  Encoding.GetEncodings())
    {
        foreach (var e2 in Encoding.GetEncodings())
        {
            byte[] unknow = Encoding.GetEncoding(e1.CodePage).GetBytes(source);
            string result = Encoding.GetEncoding(e2.CodePage).GetString(unknow);
            sb.AppendLine(string.Format("{0} => {1} : {2}",e1.CodePage,e2.CodePage,result));
        }
    }
    File.WriteAllText("test.txt", sb.ToString());
}
image

內容有1.8Mbyte實在很多,這次只要用950跟所有的codepage交叉比對,資料量立即下降許多
static void savebig5()
{
    StringBuilder sb = new StringBuilder();
    string source = "⊃;nÅé¼Ò⊃;Õ¤¤ªº¤@¯ë©Ê¿ù»~¡G¼Ð·Ç GUI (⊃;ϧΤƥΤᤶ­±)¡C";

    foreach (var e in Encoding.GetEncodings())
    {
        byte[] unknow = Encoding.GetEncoding(e.CodePage).GetBytes(source);
        string result = Encoding.GetEncoding(950).GetString(unknow);
        sb.AppendLine(string.Format("{0} => {1} : {2}", e.CodePage, 950, result));
    }
    File.WriteAllText("big5.txt", sb.ToString());
}
image


後記:
也感謝所有參與回覆的伙伴。
若有謬誤,煩請告知,新手發帖請多包涵

from : https://dotblogs.com.tw/yc421206/archive/2011/06/09/27596.aspx

Encoding of C# (CLR)

A string in C# is always UTF-16

If request from website is using UTF-8, you will always need to do the conversion from UTF-16 to UTF-8 as below:
public static string Utf16ToUtf8(string utf16String)
{
    // Get UTF16 bytes and convert UTF16 bytes to UTF8 bytes
    byte[] utf16Bytes = Encoding.Unicode.GetBytes(utf16String);
    byte[] utf8Bytes = Encoding.Convert(Encoding.Unicode, Encoding.UTF8, utf16Bytes);

    // Return UTF8 bytes as ANSI string
    return Encoding.Default.GetString(utf8Bytes);
}

2018年12月18日 星期二

C# 存取修飾詞 - internal

遇過不少 .NET 工程師,都知道 C# 存取修飾詞有四種,publicprotectedprivateinternal
前三種大部都可以很輕易的回答出來,但知道internal的工程師比例卻不高。
本篇來幫大家複習一下 C# 的存取修飾詞 internal

存取範圍

internal的存取範圍是相同組件(Assembly)都可以使用,白話一點講就是internal可以在同一個dll內存取。
  • 類別預設的存取修飾詞是 internal
  • 類別成員預設的存取修飾詞是 private

protected internal

internal可以跟protected何在一起使用,存取範圍就變成:
相同組件  子類別都可以存取。
如下範例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
namespace AssemblyA
{
    class Parent
    {
        protected internal int Number;
    }

    class Sample
    {
        public void Method()
        {
            var parent = new Parent();
            // 因為 Number 帶有 internal,所以沒繼承也能存取
            parent.Number = 10; 
        }
    }
}

namespace AssemblyB
{
    class Child : AssemblyA.Parent
    {
        public void Method()
        {
          // 因為 Number 帶有 protected,所以在不同組建也能存取
            Number = 20;
        }
    }
}

Friend Assembly

為了符合封裝原則,基本上我們不太希望把不該開放的類別或成員給外部的組件存取,但為了測試專案,不開放存取權限又顯得很難做事。
這時候我們可以透過 InternalsVisibleTo 開放特定的組件也能存取internal修飾詞的類別或成員。
例如我們有 AssemblyA 及 AssemblyA.Tests 專案,我們希望 AssemblyA.Tests 專案可以存取 AssemblyA 專案的internal類別或成員。
打開 AssemblyA 專案底下的 Properties\AssemblyInfo.cs 檔案編輯:
C# 存取修飾詞 - internal Friend Assembly
1
2
3
4
5
using System.Runtime.CompilerServices;

//...

[assembly: InternalsVisibleTo("AssemblyA.Tests")]
以上設定並不是把 AssemblyA 及 AssemblyA.Tests 變成同組件,而是:
AssemblyA.Tests 可以存取 AssemblyA 的internal類別及成員。
AssemblyA 依然不可以存取 AssemblyA.Tests 的internal類別及成員。

參考



from : https://blog.johnwu.cc/article/c-sharp-access-modifiers-internal.html

2018年8月17日 星期五

Encapsulating common Try-Catch code

In an effort to reduce code duplication, I often use and have used this style to capture handling of exceptions on a boundary of an application: Given the following extension methods:
public static class FuncExtenstion
{
    [DebuggerStepThrough]
    public static Action<A> AsAction<A, T>(this Func<A, T> f)
    {
        return (a) => f(a);
    }

    [DebuggerStepThrough]
    public static Func<bool> AsFunc(this Action a)
    {
        return () => { a(); return true; };
    }
}

public static class IlogExtensions
{
    public static TResult TryCatchLogThrow<TResult>(this ILog logger, Func<TResult> f)
    {
        try
        {
            return f();
        }
        catch (Exception ex)
        {
            logger.Error(ex.Message, ex);
            throw;
        }
    }

    public static void TryCatchLogThrow(this ILog logger, Action f)
    {
        logger.TryCatchLogThrow(f.AsFunc());
    }

    public static TResult TryCatchLog<TResult>(this ILog logger, Func<TResult> f)
    {
        try
        {
            return f();
        }
        catch (Exception ex)
        {
            logger.Error(ex.Message, ex);
            return default(TResult);
        }
    }

    public static void TryCatchLog(this ILog logger, Action f)
    {
        logger.TryCatchLog(f.AsFunc());
    }
}
In the code I will use these (e.g. in a Windows/WCF/Web Service) to either silently catch the error and let the code continue to run (used in a polling service) or to at least log the error locally on the server and then rethrow the full exception again. I have used several other variations where the exception gets wrapped in more 'user friendly' exceptions or in WCF FaultExceptions
So typically this code is used as follows:
    Response ISomeService.SomeOperation(Request request)
    {
        return _logger.TryCatchLogThrow(() => _domainImplementation.SomeOperation(request));
    }

    OtherResponse ISomeService.OtherOperation(OtherRequest request)
    {
        return _logger.TryCatchLogThrow(() => _domainImplementation.OtherOperation(request));
    }
I have not seen this style of code anywhere else. So I was wondering if there is an other pattern I should be using, or if this is ok to use.

from : https://codereview.stackexchange.com/questions/11999/encapsulating-common-try-catch-code-is-this-a-known-pattern-is-it-good-or-bad