• 《Redis实战》-Josiah L.Carlson 的python的源代码翻译成C# 第一章


      static void Main(string[] args)
            {
                //根据连接信息构造客户端对象
                var redis = new CSRedis.CSRedisClient("127.0.0.1:6379,defaultDatabase=0,poolsize=500,ssl=false,writeBuffer=10240,prefix=test_");//prefix所有的key会加上test_
                bool bool_echo;
                long long_echo;
    
                /********************************************************************************************************
                # <start id="simple-string-calls"/>
                $ redis - cli                                 #A
                redis 127.0.0.1:6379 > set hello world       #D
                OK                                          #E
                redis 127.0.0.1:6379 > get hello             #F
                "world"                                     #G
                redis 127.0.0.1:6379 > del hello             #H
                (integer) 1                                 #I
                redis 127.0.0.1:6379 > get hello             #J
                (nil)
                redis 127.0.0.1:6379 >
                # <end id="simple-string-calls"/>
                # A Start the redis-cli client up
                # D Set the key 'hello' to the value 'world'
                # E If a SET command succeeds, it returns 'OK', which turns into True on the Python side
                # F Now get the value stored at the key 'hello'
                # G It is still 'world', like we just set it
                # H Let's delete the key/value pair
                # I If there was a value to delete, DEL returns the number of items that were deleted
                # J There is no more value, so trying to fetch the value returns nil, which turns into None on the Python side
                # END
                *********************************************************************************************************/
    
                #region simple-string-calls
                Console.WriteLine("simple-string-calls");
                bool_echo = redis.Set("hello", "world");
                Console.WriteLine($"redis.Set('hello', 'world'); {Bool2String_echo(bool_echo)}");
                var hello = redis.Get("hello");
                Console.WriteLine($"redis.Get('hello'); {hello}");
                long_echo = redis.Del("hello");
                Console.WriteLine($"redis.Del('hello'); {long_echo}");
                hello = redis.Get("hello");
                Console.WriteLine($"redis.Get('hello'); {hello ?? "null"}");
                #endregion
    
                /*********************************************************************************************************
                # <start id="simple-list-calls"/>
                redis 127.0.0.1:6379> rpush list-key item   #A
                (integer) 1                                 #A
                redis 127.0.0.1:6379> rpush list-key item2  #A
                (integer) 2                                 #A
                redis 127.0.0.1:6379> rpush list-key item   #A
                (integer) 3                                 #A
                redis 127.0.0.1:6379> lrange list-key 0 -1  #B
                1) "item"                                   #B
                2) "item2"                                  #B
                3) "item"                                   #B
                redis 127.0.0.1:6379> lindex list-key 1     #C
                "item2"                                     #C
                redis 127.0.0.1:6379> lpop list-key         #D
                "item"                                      #D
                redis 127.0.0.1:6379> lrange list-key 0 -1  #D
                1) "item2"                                  #D
                2) "item"                                   #D
                redis 127.0.0.1:6379> 
                # <end id="simple-list-calls"/>
                #A When we push items onto a LIST, the command returns the current length of the list
                #B We can fetch the entire list by passing a range of 0 for the start index, and -1 for the last index
                #C We can fetch individual items from the list with LINDEX
                #D By popping an item from the list, it is no longer available
                #END
                *********************************************************************************************************/
    
                #region simple-list-calls
                Console.WriteLine("simple-list-calls");
                redis.Del("list-key");
                long_echo = redis.RPush("list-key", "item");
                Console.WriteLine($"redis.RPush('list-key', 'item'); {long_echo}");
                long_echo = redis.RPush("list-key", "item2");
                Console.WriteLine($"redis.RPush('list-key', 'item2');{long_echo}");
                long_echo = redis.RPush("list-key", "item");
                Console.WriteLine($"redis.RPush('list-key', 'item'); {long_echo}");
                var list_key = redis.LRange("list-key", 0, -1);
                Console.WriteLine($"redis.LRange('list-key', 0, -1); {StringArray2String(list_key)}");
                var list_key1 = redis.LIndex("list-key", 1);
                Console.WriteLine($"redis.LIndex('list-key', 1); {list_key1}");
                var list_keyleft = redis.LPop("list-key");
                Console.WriteLine($"redis.LPop('list-key'); {list_keyleft}");
                list_key = redis.LRange("list-key", 0, -1);
                Console.WriteLine($"redis.LRange('list-key', 0, -1); {StringArray2String(list_key)}");
                #endregion
    
                /********************************************************************************************************
                # <start id="simple-set-calls"/>
                redis 127.0.0.1:6379 > sadd set - key item     #A
                (integer) 1                                 #A
                redis 127.0.0.1:6379 > sadd set - key item2    #A
                (integer) 1                                 #A
                redis 127.0.0.1:6379 > sadd set - key item3    #A
                (integer) 1                                 #A
                redis 127.0.0.1:6379 > sadd set - key item     #A
                (integer) 0                                 #A
                redis 127.0.0.1:6379 > smembers set - key      #B
                1) "item"                                   #B
                2) "item2"                                  #B
                3) "item3"                                  #B
                redis 127.0.0.1:6379 > sismember set - key item4   #C
                (integer) 0                                     #C
                redis 127.0.0.1:6379 > sismember set - key item    #C
                (integer) 1                                     #C
                redis 127.0.0.1:6379 > srem set - key item2    #D
                (integer) 1                                 #D
                redis 127.0.0.1:6379 > srem set - key item2    #D
                (integer) 0                                 #D
                redis 127.0.0.1:6379 > smembers set - key
                1) "item"
                2) "item3"
                redis 127.0.0.1:6379 >
                # <end id="simple-set-calls"/>
                # A When adding an item to a SET, Redis will return a 1 if the item is new to the set and 0 if it was already in the SET
                # B We can fetch all of the items in the SET, which returns them as a sequence of items, which is turned into a Python set from Python
                # C We can also ask Redis whether an item is in the SET, which turns into a boolean in Python
                # D When we attempt to remove items, our commands return the number of items that were removed
                # END
                *********************************************************************************************************/
    
                #region simple-set-calls
                redis.Del("set-key");
                Console.WriteLine("simple-set-calls");
                long_echo = redis.SAdd("set-key", "item");
                Console.WriteLine($"redis.SAdd('set-key', 'item'); {long_echo}");
                long_echo = redis.SAdd("set-key", "item2");
                Console.WriteLine($"redis.SAdd('set-key', 'item2'); {long_echo}");
                long_echo = redis.SAdd("set-key", "item3");
                Console.WriteLine($"redis.SAdd('set-key', 'item3'); {long_echo}");
                long_echo = redis.SAdd("set-key", "item");
                Console.WriteLine($"redis.SAdd('set-key', 'item'); {long_echo}");
                var set_key = redis.SMembers("set-key");
                Console.WriteLine($"redis.SMembers('set-key'); {StringArray2String(set_key)}");
                bool_echo = redis.SIsMember("set-key", "item4");
                Console.WriteLine($"redis.SIsMember('set-key', 'item4'); {Bool2String_echo(bool_echo)}");
                bool_echo = redis.SIsMember("set-key", "item");
                Console.WriteLine($"redis.SIsMember('set-key', 'item'); {Bool2String_echo(bool_echo)}");
                long_echo = redis.SRem("set-key", "item2");
                Console.WriteLine($"redis.SRem('set-key', 'item2'); {long_echo}");
                long_echo = redis.SRem("set-key", "item2");
                Console.WriteLine($"redis.SRem('set-key', 'item2'); {long_echo}");
                set_key = redis.SMembers("set-key");
                Console.WriteLine($"redis.SMembers('set-key'); {StringArray2String(set_key)}");
                #endregion
    
                /********************************************************************************************************
                # <start id="simple-hash-calls"/>
                redis 127.0.0.1:6379> hset hash-key sub-key1 value1 #A
                (integer) 1                                         #A
                redis 127.0.0.1:6379> hset hash-key sub-key2 value2 #A
                (integer) 1                                         #A
                redis 127.0.0.1:6379> hset hash-key sub-key1 value1 #A
                (integer) 0                                         #A
                redis 127.0.0.1:6379> hgetall hash-key              #B
                1) "sub-key1"                                       #B
                2) "value1"                                         #B
                3) "sub-key2"                                       #B
                4) "value2"                                         #B
                redis 127.0.0.1:6379> hdel hash-key sub-key2        #C
                (integer) 1                                         #C
                redis 127.0.0.1:6379> hdel hash-key sub-key2        #C
                (integer) 0                                         #C
                redis 127.0.0.1:6379> hget hash-key sub-key1        #D
                "value1"                                            #D
                redis 127.0.0.1:6379> hgetall hash-key
                1) "sub-key1"
                2) "value1"
                # <end id="simple-hash-calls"/>
                #A When we add items to a hash, again we get a return value that tells us whether the item is new in the hash
                #B We can fetch all of the items in the HASH, which gets translated into a dictionary on the Python side of things
                #C When we delete items from the hash, the command returns whether the item was there before we tried to remove it
                #D We can also fetch individual fields from hashes
                #END
                *********************************************************************************************************/
    
                #region simple-hash-calls
                redis.Del("hash-key");
                bool_echo = redis.HSet("hash-key", "sub-key1", "value1");
                Console.WriteLine($"redis.HSet('hash-key', 'sub-key1', 'value1'); {Bool2String_echo(bool_echo)}");
                bool_echo = redis.HSet("hash-key", "sub-key2", "value2");
                Console.WriteLine($"redis.HSet('hash-key', 'sub-key2', 'value2'); {Bool2String_echo(bool_echo)}");
                bool_echo = redis.HSet("hash-key", "sub-key1", "value1");
                Console.WriteLine($"redis.HSet('hash-key', 'sub-key1', 'value1'); {Bool2String_echo(bool_echo)}");
                var hash_key = redis.HGetAll("hash-key");
                Console.WriteLine($"redis.HGetAll('hash-key'); {Dictionary2String(hash_key)}");
                long_echo = redis.HDel("hash-key", "sub-key2");
                Console.WriteLine($"redis.HDel('hash-key', 'sub-key2'); {long_echo}");
                long_echo = redis.HDel("hash-key", "sub-key2");
                Console.WriteLine($"redis.HDel('hash-key', 'sub-key2'); {long_echo}");
                var sub_key1 = redis.HGet("hash-key", "sub-key1");
                Console.WriteLine($"redis.HGet('hash-key', 'sub-key1'); {sub_key1}");
                hash_key = redis.HGetAll("hash-key");
                Console.WriteLine($"redis.HGetAll('hash-key'); {Dictionary2String(hash_key)}");
                #endregion
    
                /********************************************************************************************************
                # <start id="simple-zset-calls"/>
                redis 127.0.0.1:6379> zadd zset-key 728 member1     #A
                (integer) 1                                         #A
                redis 127.0.0.1:6379> zadd zset-key 982 member0     #A
                (integer) 1                                         #A
                redis 127.0.0.1:6379> zadd zset-key 982 member0     #A
                (integer) 0                                         #A
                redis 127.0.0.1:6379> zrange zset-key 0 -1 withscores   #B
                1) "member1"                                            #B
                2) "728"                                                #B
                3) "member0"                                            #B
                4) "982"                                                #B
                redis 127.0.0.1:6379> zrangebyscore zset-key 0 800 withscores   #C
                1) "member1"                                                    #C
                2) "728"                                                        #C
                redis 127.0.0.1:6379> zrem zset-key member1     #D
                (integer) 1                                     #D
                redis 127.0.0.1:6379> zrem zset-key member1     #D
                (integer) 0                                     #D
                redis 127.0.0.1:6379> zrange zset-key 0 -1 withscores
                1) "member0"
                2) "982"
                # <end id="simple-zset-calls"/>
                #A When we add items to a ZSET, the the command returns the number of new items
                #B We can fetch all of the items in the ZSET, which are ordered by the scores, and scores are turned into floats in Python
                #C We can also fetch a subsequence of items based on their scores
                #D When we remove items, we again find the number of items that were removed
                #END
                *********************************************************************************************************/
    
                #region simple-zset-calls
                redis.Del("zset-key");
                long_echo = redis.ZAdd("zset-key", (728, "member1"));
                Console.WriteLine($"redis.ZAdd('zset-key', (728, 'member1')); {long_echo}");
                long_echo = redis.ZAdd("zset-key", (982, "member0"));
                Console.WriteLine($"redis.ZAdd('zset-key', (982, 'member0')); {long_echo}");
                var zset_key = redis.ZRangeWithScores("zset-key", 0, -1);
                Console.WriteLine($"redis.ZRangeWithScores('zset-key', 0, -1); {ValueTuple2String(zset_key)}");
                zset_key = redis.ZRangeByScoreWithScores("zset-key", 0, 800);
                Console.WriteLine($"redis.ZRangeByScoreWithScores('zset-key', 0, 800); {ValueTuple2String(zset_key)}");
                long_echo = redis.ZRem("zset-key", "member1");
                Console.WriteLine($"redis.ZRem('zset-key', 'member1'); {long_echo}");
                long_echo = redis.ZRem("zset-key", "member1");
                Console.WriteLine($"redis.ZRem('zset-key', 'member1'); {long_echo}");
                zset_key = redis.ZRangeWithScores("zset-key", 0, -1);
                Console.WriteLine($"redis.ZRangeWithScores('zset-key', 0, -1); {ValueTuple2String(zset_key)}");
                #endregion
    
                new TestCh01().test_article_functionality();
            }
    
            static string Bool2String_echo(bool bool_echo)
            {
                return bool_echo ? "OK" : "Fail";
            }
    
            static string StringArray2String(string[] strs)
            {
                string str = string.Empty;
                for (int i = 0; i < strs.Length; i++)
                {
                    str += $"{i}){strs[i]} ";
                }
                return str;
            }
    
            static string ValueTuple2String((string, decimal)[] tuples)
            {
                string str = string.Empty;
                for (int i = 0; i < tuples.Length; i++)
                {
                    str += $"{i}){tuples[i].Item1} {tuples[i].Item2} ";
                }
                return str;
            }
    
            static string Dictionary2String(Dictionary<string, string> dics)
            {
    
                string str = string.Empty;
                for (int i = 0; i < dics.Count; i++)
                {
                    str += $"{i}){dics.Keys.ToList()[i]} {dics.Values.ToList()[i]} ";
                }
                return str;
            }
    View Code
    using CSRedis;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace AIStudio.ConSole.Redis.Ch01
    {
    /************************************************************************************************************************
    # <start id="upvote-code"/>
    ONE_WEEK_IN_SECONDS = 7 * 86400                     #A
    VOTE_SCORE = 432                                    #A
    
    def article_vote(conn, user, article):
        cutoff = time.time() - ONE_WEEK_IN_SECONDS      #B
        if conn.zscore('time:', article) < cutoff:      #C
            return
    
        article_id = article.partition(':')[-1]         #D
        if conn.sadd('voted:' + article_id, user):      #E
            conn.zincrby('score:', VOTE_SCORE, article) #E
            conn.hincrby(article, 'votes', 1)           #E
    # <end id="upvote-code"/>
    #A Prepare our constants
    #B Calculate the cutoff time for voting
    #C Check to see if the article can still be voted on (we could use the article HASH here, but scores are returned as floats so we don't have to cast it)
    #D Get the id portion from the article:id identifier
    #E If the user hasn't voted for this article before, increment the article score and vote count (note that our HINCRBY and ZINCRBY calls should be in a Redis transaction, but we don't introduce them until chapter 3 and 4, so ignore that for now)
    #END
    
    # <start id="post-article-code"/>
    def post_article(conn, user, title, link):
        article_id = str(conn.incr('article:'))     #A
    
        voted = 'voted:' + article_id
        conn.sadd(voted, user)                      #B
        conn.expire(voted, ONE_WEEK_IN_SECONDS)     #B
    
        now = time.time()
        article = 'article:' + article_id
        conn.hmset(article, {                       #C
            'title': title,                         #C
            'link': link,                           #C
            'poster': user,                         #C
            'time': now,                            #C
            'votes': 1,                             #C
        })                                          #C
    
        conn.zadd('score:', {article: now + VOTE_SCORE})#D
        conn.zadd('time:', {article: now})              #D
    
        return article_id
    # <end id="post-article-code"/>
    #A Generate a new article id
    #B Start with the posting user having voted for the article, and set the article voting information to automatically expire in a week (we discuss expiration in chapter 3)
    #C Create the article hash
    #D Add the article to the time and score ordered zsets
    #END
    
    # <start id="fetch-articles-code"/>
    ARTICLES_PER_PAGE = 25
    
    def get_articles(conn, page, order='score:'):
        start = (page-1) * ARTICLES_PER_PAGE            #A
        end = start + ARTICLES_PER_PAGE - 1             #A
    
        ids = conn.zrevrange(order, start, end)         #B
        articles = []
        for id in ids:                                  #C
            article_data = conn.hgetall(id)             #C
            article_data['id'] = id                     #C
            articles.append(article_data)               #C
    
        return articles
    # <end id="fetch-articles-code"/>
    #A Set up the start and end indexes for fetching the articles
    #B Fetch the article ids
    #C Get the article information from the list of article ids
    #END
    
    # <start id="add-remove-groups"/>
    def add_remove_groups(conn, article_id, to_add=[], to_remove=[]):
        article = 'article:' + article_id           #A
        for group in to_add:
            conn.sadd('group:' + group, article)    #B
        for group in to_remove:
            conn.srem('group:' + group, article)    #C
    # <end id="add-remove-groups"/>
    #A Construct the article information like we did in post_article
    #B Add the article to groups that it should be a part of
    #C Remove the article from groups that it should be removed from
    #END
    
    # <start id="fetch-articles-group"/>
    def get_group_articles(conn, group, page, order='score:'):
        key = order + group                                     #A
        if not conn.exists(key):                                #B
            conn.zinterstore(key,                               #C
                ['group:' + group, order],                      #C
                aggregate='max',                                #C
            )
            conn.expire(key, 60)                                #D
        return get_articles(conn, page, key)                    #E
    # <end id="fetch-articles-group"/>
    #A Create a key for each group and each sort order
    #B If we haven't sorted these articles recently, we should sort them
    #C Actually sort the articles in the group based on score or recency
    #D Tell Redis to automatically expire the ZSET in 60 seconds
    #E Call our earlier get_articles() function to handle pagination and article data fetching
    #END
    
    #--------------- Below this line are helpers to test the code ----------------
    
    class TestCh01(unittest.TestCase):
        def setUp(self):
            import redis
            self.conn = redis.Redis(db=15)
    
        def tearDown(self):
            del self.conn
            print()
            print()
    
        def test_article_functionality(self):
            conn = self.conn
            import pprint
    
            article_id = str(post_article(conn, 'username', 'A title', 'http://www.google.com'))
            print("We posted a new article with id:", article_id)
            print()
            self.assertTrue(article_id)
    
            print("Its HASH looks like:")
            r = conn.hgetall('article:' + article_id)
            print(r)
            print()
            self.assertTrue(r)
    
            article_vote(conn, 'other_user', 'article:' + article_id)
            print("We voted for the article, it now has votes:", end=' ')
            v = int(conn.hget('article:' + article_id, 'votes'))
            print(v)
            print()
            self.assertTrue(v > 1)
    
            print("The currently highest-scoring articles are:")
            articles = get_articles(conn, 1)
            pprint.pprint(articles)
            print()
    
            self.assertTrue(len(articles) >= 1)
    
            add_remove_groups(conn, article_id, ['new-group'])
            print("We added the article to a new group, other articles include:")
            articles = get_group_articles(conn, 'new-group', 1)
            pprint.pprint(articles)
            print()
            self.assertTrue(len(articles) >= 1)
    
            to_del = (
                conn.keys('time:*') + conn.keys('voted:*') + conn.keys('score:*') + 
                conn.keys('article:*') + conn.keys('group:*')
            )
            if to_del:
                conn.delete(*to_del)
    
    if __name__ == '__main__':
        unittest.main()
    
        **************************************************************************************************************/
        class TestCh01
        {
            public const int ONE_WEEK_IN_SECONDS = 7 * 86400;
            public const int VOTE_SCORE = 432;
            public const int ARTICLES_PER_PAGE = 25;
    
            CSRedisClient _conn;
            public TestCh01()
            {
                _conn = new CSRedis.CSRedisClient("127.0.0.1:6379,defaultDatabase=14,poolsize=500,ssl=false,writeBuffer=10240");
            }
    
            public void article_vote(CSRedisClient conn, string user, string article)
            {
                var cutoff = (DateTime.Now.AddSeconds(0 - ONE_WEEK_IN_SECONDS) - new DateTime(1970, 1, 1)).TotalSeconds;
                if ((conn.ZScore("time:", article) ?? 0) < (decimal)cutoff)
                {
                    return;
                }
    
                var article_id = article.Substring(article.IndexOf(":") + 1);
                if (conn.SAdd("voted:" + article_id, user) > 0)
                {
                    conn.ZIncrBy("score:", article, VOTE_SCORE);
                    conn.HIncrBy(article, "votes", 1);
                }
            }
    
            public string post_article(CSRedisClient conn, string user, string title, string link)
            {
                var article_id = conn.IncrBy("article:").ToString();
                var voted = "voted:" + article_id;
                conn.SAdd(voted, user);
                conn.Expire(voted, ONE_WEEK_IN_SECONDS);
                var now = (DateTime.Now - new DateTime(1970, 1, 1)).TotalSeconds;
                var article = "article:" + article_id;
                conn.HMSet(article, new[]
                {
                    "title", title,
                    "link",link,
                    "poster", user ,
                    "time", now.ToString(),
                    "votes", 1.ToString(),
                });
    
                conn.ZAdd("score:", ((decimal)(now + VOTE_SCORE), article));
                conn.ZAdd("time:", ((decimal)now, article));
    
                return article_id;
            }
    
            public List<Article> get_articles(CSRedisClient conn, int page, string order = "score:")
            {
                var start = (page - 1) * ARTICLES_PER_PAGE;
                var end = start + ARTICLES_PER_PAGE - 1;
    
                var ids = conn.ZRevRange(order, start, end);
                List<Article> articles = new List<Article>();
                foreach (var id in ids)
                {
                    var article_data = conn.HGetAll(id);
                    articles.Add(new Article() { id = id, link = article_data["link"], poster = article_data["poster"], time = article_data["time"], title = article_data["title"], votes = article_data["votes"], });
                }
    
                return articles;
            }
    
            public void add_remove_groups(CSRedisClient conn, string article_id, string[] to_add, string[] to_remove)
            {
                var article = "article:" + article_id;
                foreach (var group in to_add)
                {
                    conn.SAdd("group:" + group, article);
                }
                foreach (var group in to_remove)
                {
                    conn.SRem("group:" + group, article);
                }
            }
    
            public List<Article> get_group_articles(CSRedisClient conn, string group, int page, string order = "score:")
            {
                var key = order + group;
                if (!conn.Exists(key))
                {
                    conn.ZInterStore(key, null, RedisAggregate.Max, "group:" + group, order);
                    conn.Expire(key, 60);
                }
    
                return get_articles(conn, page, key);
            }
    
            public void test_article_functionality()
            {
                var article_id = post_article(_conn, "username", "A title", "http://www.google.com");
                Console.WriteLine("We posted a new article with id:" + article_id);
                Console.WriteLine();
    
                Console.WriteLine("Its HASH looks like:");
                var r = _conn.HGetAll("article:" + article_id);
                Console.WriteLine(r);
                Console.WriteLine();
    
                article_vote(_conn, "other_user", "article:" + article_id);
                Console.WriteLine("We voted for the article, it now has votes:");
                var v = _conn.HGet("article:" + article_id, "votes");
                Console.WriteLine(v);
                Console.WriteLine();
    
                Console.WriteLine("The currently highest-scoring articles are:");
                var articles = get_articles(_conn, 1);
                Console.WriteLine(articles.ToString());
                Console.WriteLine();
    
                add_remove_groups(_conn, article_id, new string[] { "new-group" }, new string[] { });
                Console.WriteLine("We added the article to a new group, other articles include:");
                articles = get_group_articles(_conn, "new-group", 1);
                Console.WriteLine(articles.ToString());
                Console.WriteLine();
    
                var keys = _conn.Keys("time:*").Union(_conn.Keys("voted:*")).Union(_conn.Keys("score:*")).Union(_conn.Keys("article:*")).Union(_conn.Keys("group:*")).ToArray();
                _conn.Del(keys);
    
    
            }
        }
    
        public class Article
        {
            public string id { get; set; }
            public string title { get; set; }
            public string link { get; set; }
            public string poster { get; set; }
            public string time { get; set; }
            public string votes { get; set; }
    
    
        }
    }
    View Code

    源码码下载地址 AIStudio.ConSole.Redis (gitee.com)

    作者:竹天笑
    互相学习,提高自己。
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
  • 相关阅读:
    LinuxMCE
    qBittorrent 0.9.0
    Exaile 0.2.9
    GAdminHttpd:图形化的 Apache 打点对象
    FBReader-电子书阅读对象
    CSSED:Linux 下 Web 拓荒者的 CSS 编纂利器
    Canorus:乐谱编辑软件
    AutoScan-收集监视及办理器械
    Lunar Applet:在桌面表现阴历
    Totem 2.18.1
  • 原文地址:https://www.cnblogs.com/akwkevin/p/14055435.html
Copyright © 2020-2023  润新知